diff --git a/Package.swift b/Package.swift index 14edca0c..f0f11dda 100644 --- a/Package.swift +++ b/Package.swift @@ -56,6 +56,10 @@ let package = Package( .product(name: "Vapor", package: "vapor"), ] ), + .target( + name: "Minifier", + path: "Sources/Utilities" + ), .testTarget( name: "HTMLKitTests", dependencies: [ @@ -92,6 +96,12 @@ let package = Package( .copy("Localization") ] ), + .testTarget( + name: "UtilitiesTests", + dependencies: [ + .target(name: "Minifier"), + ] + ), .executableTarget( name: "ConvertCommand", dependencies: [ @@ -102,7 +112,8 @@ let package = Package( .executableTarget( name: "DeployCommand", dependencies: [ - .target(name: "HTMLKitComponents") + .target(name: "HTMLKitComponents"), + .target(name: "Minifier") ], path: "Sources/Commands/Components" ), diff --git a/Sources/Commands/Components/DeployCommand.swift b/Sources/Commands/Components/DeployCommand.swift index f6321d81..a63983e8 100644 --- a/Sources/Commands/Components/DeployCommand.swift +++ b/Sources/Commands/Components/DeployCommand.swift @@ -1,8 +1,15 @@ import Foundation +import Minifier @main internal struct DeployCommand { + internal enum BundleOption { + + case minified + case detailed + } + private static var sourcePath: String { return CommandLine.arguments[1] } @@ -22,23 +29,30 @@ internal struct DeployCommand { } else { - try compose(source: sourcePath + "/Resources/css/", to: targetPath + "/htmlkit/", with: "all.css") + try bundle(css: sourcePath + "/Resources/css/", to: targetPath + "/htmlkit/", with: "all.css") - try compose(source: sourcePath + "/Resources/js/", to: targetPath + "/htmlkit/", with: "all.js") + try bundle(js: sourcePath + "/Resources/js/components/", to: targetPath + "/htmlkit/", with: "all.js") + + try bundle(js: sourcePath + "/Resources/js/interactions/", to: targetPath + "/htmlkit/", with: "query.js") exit(0) } } /// Iterates through the directory and collects data to create a single file - private static func compose(source: String, to target: String, with filename: String) throws { + private static func bundle(css source: String, to target: String, with filename: String, option: BundleOption = .minified) throws { + + let template = """ + @charset "utf-8"; + /* Copyright (c) 2019 - 2023 Vapor Community - Licensed under MIT (https://github.com/vapor-community/HTMLKit/blob/main/LICENSE) */ + """ if !manager.fileExists(atPath: target) { try manager.createDirectory(atPath: target, withIntermediateDirectories: true) } if !manager.fileExists(atPath: target + filename) { - manager.createFile(atPath: target + filename, contents: nil) + manager.createFile(atPath: target + filename, contents: template.data(using: .utf8)) } if let enumerator = manager.enumerator(at: URL(fileURLWithPath: source), includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) { @@ -56,7 +70,67 @@ internal struct DeployCommand { handle.seekToEndOfFile() - handle.write(try Data(contentsOf: path)) + if case .minified = option { + + let minifier = Minifier(compression: [.stripComments, .removeWhitespaces]) + + if let data = minifier.minify(css: try String(contentsOf: path)).data(using: .utf8) { + handle.write(data) + } + + } else { + handle.write(try Data(contentsOf: path)) + } + + handle.closeFile() + } + } + } + } + } + } + + /// Iterates through the directory and collects data to create a single file + private static func bundle(js source: String, to target: String, with filename: String, option: BundleOption = .minified) throws { + + let template = """ + /* Copyright (c) 2019 - 2023 Vapor Community - Licensed under MIT (https://github.com/vapor-community/HTMLKit/blob/main/LICENSE) */ + """ + + if !manager.fileExists(atPath: target) { + try manager.createDirectory(atPath: target, withIntermediateDirectories: true) + } + + if !manager.fileExists(atPath: target + filename) { + manager.createFile(atPath: target + filename, contents: template.data(using: .utf8)) + } + + if let enumerator = manager.enumerator(at: URL(fileURLWithPath: source), includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) { + + for case let path as URL in enumerator { + + if !path.hasDirectoryPath { + + if !path.isFileURL { + enumerator.skipDescendants() + + } else { + + if let handle = FileHandle(forWritingAtPath: target + filename) { + + handle.seekToEndOfFile() + + if case .minified = option { + + let minifier = Minifier(compression: [.stripComments, .removeWhitespaces]) + + if let data = minifier.minify(js: try String(contentsOf: path)).data(using: .utf8) { + handle.write(data) + } + + } else { + handle.write(try Data(contentsOf: path)) + } handle.closeFile() } diff --git a/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift b/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift index e510aa45..0d2cfbb7 100644 --- a/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift +++ b/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift @@ -2622,3 +2622,21 @@ extension SelectedAttribute where Self: EmptyNode { return self.mutate(key: "selected", value: value) } } + +/// The protocol provides the element with the shadowrootmode handler. +public protocol ShadowRootModeAttribute: Attribute { + + /// The function represents the html-attribute 'shadowrootmode'. + /// + /// ```html + /// + /// ``` + func shadowRootMode(_ value: Values.Shadow.Mode) -> Self +} + +extension ShadowRootModeAttribute where Self: ContentNode { + + internal func mutate(shadowrootmode value: String) -> Self { + return self.mutate(key: "shadowrootmode", value: value) + } +} diff --git a/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift b/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift index 0501b911..15ddad7e 100644 --- a/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift +++ b/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift @@ -182,24 +182,24 @@ public protocol PositionPointAttribute: Attribute { /// ```html /// /// ``` - func positionPoint(_ point: Geometrics.Point) -> Self + func positionPoint(_ point: (Int, Int)) -> Self } extension PositionPointAttribute where Self: ContentNode { - internal func mutate(radius: Geometrics.Point) -> Self { + internal func mutate(positionpoint: (Int, Int)) -> Self { guard var attributes = self.attributes else { var attributes = OrderedDictionary() - attributes["x"] = radius.x - attributes["y"] = radius.y + attributes["x"] = positionpoint.0 + attributes["y"] = positionpoint.1 return .init(attributes: attributes, content: content) } - attributes["x"] = radius.x - attributes["y"] = radius.y + attributes["x"] = positionpoint.0 + attributes["y"] = positionpoint.1 return .init(attributes: attributes, content: content) } @@ -213,24 +213,24 @@ public protocol RadiusPointAttribute: Attribute { /// ```html /// /// ``` - func radiusPoint(_ point: Geometrics.Point) -> Self + func radiusPoint(_ point: (Int, Int)) -> Self } extension RadiusPointAttribute where Self: ContentNode { - internal func mutate(radius: Geometrics.Point) -> Self { + internal func mutate(radiuspoint: (Int, Int)) -> Self { guard var attributes = self.attributes else { var attributes = OrderedDictionary() - attributes["rx"] = radius.x - attributes["ry"] = radius.y + attributes["rx"] = radiuspoint.0 + attributes["ry"] = radiuspoint.1 return .init(attributes: attributes, content: content) } - attributes["rx"] = radius.x - attributes["ry"] = radius.y + attributes["rx"] = radiuspoint.0 + attributes["ry"] = radiuspoint.1 return .init(attributes: attributes, content: content) } @@ -244,24 +244,24 @@ public protocol CenterPointAttribute: Attribute { /// ```html /// /// ``` - func centerPoint(_ point: Geometrics.Point) -> Self + func centerPoint(_ point: (Int, Int)) -> Self } extension CenterPointAttribute where Self: ContentNode { - internal func mutate(centerpoint: Geometrics.Point) -> Self { + internal func mutate(centerpoint: (Int, Int)) -> Self { guard var attributes = self.attributes else { var attributes = OrderedDictionary() - attributes["cx"] = centerpoint.x - attributes["cy"] = centerpoint.y + attributes["cx"] = centerpoint.0 + attributes["cy"] = centerpoint.1 return .init(attributes: attributes, content: content) } - attributes["cx"] = centerpoint.x - attributes["cy"] = centerpoint.y + attributes["cx"] = centerpoint.0 + attributes["cy"] = centerpoint.1 return .init(attributes: attributes, content: content) } @@ -284,3 +284,39 @@ extension ViewBoxAttribute where Self: ContentNode { return self.mutate(key: "viewbox", value: value) } } + +/// The protocol provides the element with the viewbox handler. +public protocol NamespaceAttribute: Attribute { + + /// The function represents the html-attribute 'viewbox'. + /// + /// ```html + /// + /// ``` + func namespace(_ value: String) -> Self +} + +extension NamespaceAttribute where Self: ContentNode { + + internal func mutate(namespace value: String) -> Self { + return self.mutate(key: "xmlns", value: value) + } +} + +/// The protocol provides the element with the viewbox handler. +public protocol PointsAttribute: Attribute { + + /// The function represents the html-attribute 'viewbox'. + /// + /// ```html + /// + /// ``` + func points(_ value: String) -> Self +} + +extension PointsAttribute where Self: ContentNode { + + internal func mutate(points value: String) -> Self { + return self.mutate(key: "points", value: value) + } +} diff --git a/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift b/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift index f4e1f9f7..98b6ace0 100644 --- a/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift +++ b/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift @@ -17469,8 +17469,8 @@ public struct Template: ContentNode, BodyElement, FormElement, FigureElement, Ob } } -extension Template: GlobalAttributes, GlobalEventAttributes { - +extension Template: GlobalAttributes, GlobalEventAttributes, ShadowRootModeAttribute { + public func accessKey(_ value: Character) -> Template { return mutate(accesskey: value) } @@ -17594,6 +17594,10 @@ extension Template: GlobalAttributes, GlobalEventAttributes { public func on(event: Events.Wheel, _ value: String) -> Template { return mutate(key: event.rawValue, value: value) } + + public func shadowRootMode(_ value: Values.Shadow.Mode) -> Template { + return mutate(shadowrootmode: value.rawValue) + } } /// The element represents a comment output. @@ -18141,8 +18145,8 @@ public struct Vector: ContentNode, HtmlElement, BodyElement, FormElement, Figure } } -extension Vector: GlobalVectorAttributes, WidthAttribute, HeightAttribute, ViewBoxAttribute { - +extension Vector: GlobalVectorAttributes, WidthAttribute, HeightAttribute, ViewBoxAttribute, NamespaceAttribute { + public func id(_ value: String) -> Vector { return self.mutate(id: value) } @@ -18199,7 +18203,163 @@ extension Vector: GlobalVectorAttributes, WidthAttribute, HeightAttribute, ViewB return self.mutate(strokelinejoin: value.rawValue) } + public func namespace(_ value: String) -> Vector { + return self.mutate(namespace: value) + } + public func custom(key: String, value: Any) -> Vector { return self.mutate(key: key, value: value) } } + +/// The element is typically used in a shadow tree. Its tend to be a placeholder that you can fill with another markup. +/// +/// ```html +/// +/// ``` +public struct Slot: ContentNode, BodyElement, FormElement, FigureElement, ObjectElement { + + internal var name: String { "slot" } + + internal var attributes: OrderedDictionary? + + internal var content: [Content] + + public init(@ContentBuilder content: () -> [Content]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [Content]) { + self.attributes = attributes + self.content = content + } + + public func modify(if condition: Bool, element: (Slot) -> Slot) -> Slot { + + if condition { + return self.modify(element(self)) + } + + return self + } + + public func modify(unwrap value: T?, element: (Slot, T) -> Slot) -> Slot { + + guard let value = value else { + return self + } + + return self.modify(element(self, value as T)) + } +} + +extension Slot: GlobalAttributes, NameAttribute { + + public func accessKey(_ value: Character) -> Slot { + return self.mutate(accesskey: value) + } + + public func autocapitalize(_ value: Values.Capitalization) -> Slot { + return mutate(autocapitalize: value.rawValue) + } + + public func autofocus() -> Slot { + return mutate(autofocus: "autofocus") + } + + public func `class`(_ value: String) -> Slot { + return mutate(class: value) + } + + public func isEditable(_ value: Bool) -> Slot { + return mutate(contenteditable: value) + } + + public func direction(_ value: Values.Direction) -> Slot { + return mutate(dir: value.rawValue) + } + + public func isDraggable(_ value: Bool) -> Slot { + return mutate(draggable: value) + } + + public func enterKeyHint(_ value: Values.Hint) -> Slot { + return mutate(enterkeyhint: value.rawValue) + } + + public func hidden() -> Slot { + return mutate(hidden: "hidden") + } + + public func inputMode(_ value: String) -> Slot { + return mutate(inputmode: value) + } + + public func `is`(_ value: String) -> Slot { + return mutate(is: value) + } + + public func itemId(_ value: String) -> Slot { + return mutate(itemid: value) + } + + public func itemProperty(_ value: String) -> Slot { + return mutate(itemprop: value) + } + + public func itemReference(_ value: String) -> Slot { + return mutate(itemref: value) + } + + public func itemScope(_ value: String) -> Slot { + return mutate(itemscope: value) + } + + public func itemType(_ value: String) -> Slot { + return mutate(itemtype: value) + } + + public func id(_ value: String) -> Slot { + return mutate(id: value) + } + + public func language(_ value: Values.Language) -> Slot { + return mutate(lang: value.rawValue) + } + + public func nonce(_ value: String) -> Slot { + return mutate(nonce: value) + } + + public func role(_ value: Values.Role) -> Slot { + return mutate(role: value.rawValue) + } + + public func hasSpellCheck(_ value: Bool) -> Slot { + return mutate(spellcheck: value) + } + + public func style(_ value: String) -> Slot { + return mutate(style: value) + } + + public func tabIndex(_ value: Int) -> Slot { + return mutate(tabindex: value) + } + + public func title(_ value: String) -> Slot { + return mutate(title: value) + } + + public func translate(_ value: Values.Decision) -> Slot { + return mutate(translate: value.rawValue) + } + + public func name(_ value: String) -> Slot { + return mutate(name: value) + } + + public func custom(key: String, value: Any) -> Slot { + return mutate(key: key, value: value) + } +} diff --git a/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift b/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift index a8e16543..67ecfce8 100644 --- a/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift +++ b/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift @@ -59,7 +59,7 @@ public struct Circle: ContentNode, VectorElement { } extension Circle: GlobalVectorAttributes, CenterPointAttribute, RadiusAttribute { - + public func id(_ value: String) -> Circle { return self.mutate(id: value) } @@ -88,7 +88,7 @@ extension Circle: GlobalVectorAttributes, CenterPointAttribute, RadiusAttribute return self.mutate(strokewidth: size) } - public func centerPoint(_ point: Geometrics.Point) -> Circle { + public func centerPoint(_ point: (Int, Int)) -> Circle { return self.mutate(centerpoint: point) } @@ -188,8 +188,8 @@ extension Rectangle: GlobalVectorAttributes, WidthAttribute, HeightAttribute, Ra return self.mutate(strokewidth: size) } - public func radiusPoint(_ point: Geometrics.Point) -> Rectangle { - return self.mutate(radius: point) + public func radiusPoint(_ point: (Int, Int)) -> Rectangle { + return self.mutate(radiuspoint: point) } public func width(_ size: Int) -> Rectangle { @@ -292,12 +292,12 @@ extension Ellipse: GlobalVectorAttributes, CenterPointAttribute, RadiusPointAttr return self.mutate(strokewidth: size) } - public func centerPoint(_ point: Geometrics.Point) -> Ellipse { + public func centerPoint(_ point: (Int, Int)) -> Ellipse { return self.mutate(centerpoint: point) } - public func radiusPoint(_ point: Geometrics.Point) -> Ellipse { - return self.mutate(radius: point) + public func radiusPoint(_ point: (Int, Int)) -> Ellipse { + return self.mutate(radiuspoint: point) } public func fillOpacity(_ value: Double) -> Ellipse { @@ -454,7 +454,7 @@ public struct Polygon: ContentNode, VectorElement { } } -extension Polygon: GlobalVectorAttributes { +extension Polygon: GlobalVectorAttributes, PointsAttribute { public func id(_ value: String) -> Polygon { return self.mutate(id: value) @@ -500,6 +500,10 @@ extension Polygon: GlobalVectorAttributes { return self.mutate(strokelinejoin: value.rawValue) } + public func points(_ value: String) -> Polygon { + return self.mutate(points: value) + } + public func custom(key: String, value: Any) -> Polygon { return self.mutate(key: key, value: value) } @@ -546,7 +550,7 @@ public struct Polyline: ContentNode, VectorElement { } } -extension Polyline: GlobalVectorAttributes { +extension Polyline: GlobalVectorAttributes, PointsAttribute { public func id(_ value: String) -> Polyline { return self.mutate(id: value) @@ -592,6 +596,10 @@ extension Polyline: GlobalVectorAttributes { return self.mutate(strokelinejoin: value.rawValue) } + public func points(_ value: String) -> Polyline { + return self.mutate(points: value) + } + public func custom(key: String, value: Any) -> Polyline { return self.mutate(key: key, value: value) } diff --git a/Sources/HTMLKit/Abstraction/Tokens/ValueTokens.swift b/Sources/HTMLKit/Abstraction/Tokens/ValueTokens.swift index 666b5a23..8a6cf2db 100644 --- a/Sources/HTMLKit/Abstraction/Tokens/ValueTokens.swift +++ b/Sources/HTMLKit/Abstraction/Tokens/ValueTokens.swift @@ -350,6 +350,9 @@ public enum Values { /// ``` public enum Target: String { + /// Opens the target in the current tab. + case current = "_self" + /// Opens the target in a separate tab or window. case blank = "_blank" @@ -880,5 +883,21 @@ public enum Values { case descending } } - + + public enum Shadow { + + /// An access mode for the shadow root. + /// + /// ```html + /// + /// ``` + public enum Mode: String { + + /// Opens the shadow root. It can be reached via javascript. + case open + + /// Closes the shadow root. It cannot be reached from the ouside. + case closed + } + } } diff --git a/Sources/HTMLKit/Framework/Helpers/Geometrics.swift b/Sources/HTMLKit/Framework/Helpers/Geometrics.swift deleted file mode 100644 index 99201e87..00000000 --- a/Sources/HTMLKit/Framework/Helpers/Geometrics.swift +++ /dev/null @@ -1,14 +0,0 @@ -/* - Abstract: - The file contains dimension definitions. - */ - -public enum Geometrics { - - /// The type is for - public struct Point { - - var x: Int - var y: Int - } -} diff --git a/Sources/HTMLKit/HTMLKit.docc/Framework/Components/Toggle.md b/Sources/HTMLKit/HTMLKit.docc/Framework/Components/Toggle.md deleted file mode 100644 index 3f80ba8b..00000000 --- a/Sources/HTMLKit/HTMLKit.docc/Framework/Components/Toggle.md +++ /dev/null @@ -1,15 +0,0 @@ -# Toggle - -A component that shows an on or off state. - -## Declaration - -```swift -struct Toggle : View -``` - -## Overview - -```swift -Toggle(name:) -``` diff --git a/Sources/HTMLKitComponents/Components/Button.swift b/Sources/HTMLKitComponents/Components/Button.swift index 5a771bf3..607cc5d4 100644 --- a/Sources/HTMLKitComponents/Components/Button.swift +++ b/Sources/HTMLKitComponents/Components/Button.swift @@ -66,22 +66,14 @@ extension Button: ButtonModifier { return self.mutate(buttonsize: size.rawValue) } - public func borderShape(_ shape: Tokens.BorderShape) -> Button { - return self.mutate(bordershape: shape.rawValue) - } - public func buttonStyle(_ style: Tokens.ButtonStyle) -> Button { return self.mutate(buttonstyle: style.rawValue) } - public func backgroundColor(_ color: Tokens.BackgroundColor) -> Button { - return self.mutate(backgroundcolor: color.rawValue) - } - public func disabled(_ condition: Bool) -> Button { if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) + return self.mutate(buttonstate: Tokens.ViewState.disabled.rawValue) } return self @@ -103,9 +95,46 @@ extension Button: PressModifier { } } +extension Button: ViewModifier { + + public func backgroundColor(_ color: Tokens.BackgroundColor) -> Button { + return self.mutate(backgroundcolor: color.rawValue) + } + + public func opacity(_ value: Tokens.OpacityValue) -> Button { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> Button { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> Button { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> Button { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> Button { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> Button { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> Button { + return self.mutate(bordercolor: color.rawValue) + } +} + /// A component that initiates an action. public struct LinkButton: View, Modifiable { + internal let target: HTMLKit.Values.Target + /// The url path of the target. internal let destination: String @@ -119,17 +148,28 @@ public struct LinkButton: View, Modifiable { internal var events: [String]? /// Creates a action button. - public init(destination: String, @ContentBuilder content: () -> [Content]) { + public init(destination: String, target: HTMLKit.Values.Target = .current, @ContentBuilder content: () -> [Content]) { self.destination = destination + self.target = target self.content = content() self.classes = ["button"] } /// Creates a action button. - internal init(destination: String, content: [Content], classes: [String], events: [String]?) { + public init(destination: URL, target: HTMLKit.Values.Target = .current, @ContentBuilder content: () -> [Content]) { + + self.destination = destination.absoluteString + self.target = target + self.content = content() + self.classes = ["button"] + } + + /// Creates a action button. + internal init(destination: String, target: HTMLKit.Values.Target, content: [Content], classes: [String], events: [String]?) { self.destination = destination + self.target = target self.content = content self.classes = classes self.events = events @@ -140,6 +180,7 @@ public struct LinkButton: View, Modifiable { self.content } .reference(self.destination) + .target(target) .class(self.classes.joined(separator: " ")) .role(.button) } @@ -151,24 +192,51 @@ extension LinkButton: ButtonModifier { return self.mutate(buttonsize: size.rawValue) } - public func borderShape(_ shape: Tokens.BorderShape) -> LinkButton { - return self.mutate(bordershape: shape.rawValue) - } - public func buttonStyle(_ style: Tokens.ButtonStyle) -> LinkButton { return self.mutate(buttonstyle: style.rawValue) } - public func backgroundColor(_ color: Tokens.BackgroundColor) -> LinkButton { - return self.mutate(backgroundcolor: color.rawValue) - } - public func disabled(_ condition: Bool) -> LinkButton { if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) + return self.mutate(buttonstate: Tokens.ViewState.disabled.rawValue) } return self } } + +extension LinkButton: ViewModifier { + + public func backgroundColor(_ color: Tokens.BackgroundColor) -> LinkButton { + return self.mutate(backgroundcolor: color.rawValue) + } + + public func opacity(_ value: Tokens.OpacityValue) -> LinkButton { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> LinkButton { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> LinkButton { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> LinkButton { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> LinkButton { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> LinkButton { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> LinkButton { + return self.mutate(bordercolor: color.rawValue) + } +} diff --git a/Sources/HTMLKitComponents/Components/Card.swift b/Sources/HTMLKitComponents/Components/Card.swift index 4e8ace51..a18da928 100644 --- a/Sources/HTMLKitComponents/Components/Card.swift +++ b/Sources/HTMLKitComponents/Components/Card.swift @@ -6,7 +6,7 @@ import HTMLKit /// A component that distinguish content. -public class Card: View { +public struct Card: View, Modifiable { /// The header of the card. public var header: [Content]? @@ -55,3 +55,39 @@ public class Card: View { .class(classes.joined(separator: " ")) } } + +extension Card: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> Card { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> Card { + return self.mutate(zindex: index.rawValue) + } + + public func backgroundColor(_ color: Tokens.BackgroundColor) -> Card { + return self.mutate(backgroundcolor: color.rawValue) + } + + public func hidden() -> Card { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> Card { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> Card { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> Card { + return self.mutate(class: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> Card { + return self.mutate(class: color.rawValue) + } +} + diff --git a/Sources/HTMLKitComponents/Components/Dropdown.swift b/Sources/HTMLKitComponents/Components/Dropdown.swift index 53ef62ff..2f0208a9 100644 --- a/Sources/HTMLKitComponents/Components/Dropdown.swift +++ b/Sources/HTMLKitComponents/Components/Dropdown.swift @@ -6,7 +6,7 @@ import HTMLKit /// A component that displays a list of actions. -public struct Dropdown: View { +public struct Dropdown: View, Modifiable { /// The label for the dropdown. internal var label: [Content] @@ -35,9 +35,10 @@ public struct Dropdown: View { public var body: Content { Division { - Division { + HTMLKit.Button { label } + .type(.button) .class("dropdown-label") Division { content @@ -45,6 +46,60 @@ public struct Dropdown: View { .class("dropdown-content") } .class(classes.joined(separator: " ")) - .tabIndex(1) + } +} + +extension Dropdown: ButtonModifier { + + public func buttonSize(_ size: Tokens.ButtonSize) -> Dropdown { + return self.mutate(buttonsize: size.rawValue) + } + + public func buttonStyle(_ style: Tokens.ButtonStyle) -> Dropdown { + return self.mutate(buttonstyle: style.rawValue) + } + + public func disabled(_ condition: Bool) -> Dropdown { + + if condition { + return self.mutate(buttonstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } +} + +extension Dropdown: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> Dropdown { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> Dropdown { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> Dropdown { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> Dropdown { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> Dropdown { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> Dropdown { + return self.mutate(bordercolor: color.rawValue) + } + + public func backgroundColor(_ color: Tokens.BackgroundColor) -> Dropdown { + return self.mutate(backgroundcolor: color.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> Dropdown { + return self.mutate(scheme: scheme.rawValue) } } diff --git a/Sources/HTMLKitComponents/Components/Form.swift b/Sources/HTMLKitComponents/Components/Form.swift index 8ab651d9..4fceb80a 100644 --- a/Sources/HTMLKitComponents/Components/Form.swift +++ b/Sources/HTMLKitComponents/Components/Form.swift @@ -138,7 +138,7 @@ public struct TextField: View, Modifiable { self.name = name self.prompt = prompt self.value = value - self.classes = ["input", "type:textfield"] + self.classes = ["textfield"] } /// Creates a text field. @@ -168,6 +168,42 @@ public struct TextField: View, Modifiable { extension TextField: InputModifier { + public func disabled(_ condition: Bool) -> TextField { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> TextField { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension TextField: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> TextField { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> TextField { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> TextField { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> TextField { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> TextField { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> TextField { return self.mutate(bordershape: shape.rawValue) } @@ -176,13 +212,8 @@ extension TextField: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> TextField { - - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } - - return self + public func colorScheme(_ scheme: Tokens.ColorScheme) -> TextField { + return self.mutate(scheme: scheme.rawValue) } } @@ -196,7 +227,7 @@ public struct TextEditor: View, Modifiable { internal let prompt: String? /// The number of lines. - internal var rows: Int = 1 + internal var rows: Int = 3 /// The content of the editor. internal var content: [String] @@ -213,7 +244,7 @@ public struct TextEditor: View, Modifiable { self.name = name self.prompt = prompt self.content = content() - self.classes = ["input", "type:texteditor"] + self.classes = ["texteditor"] } /// Creates a text editor. @@ -252,6 +283,42 @@ public struct TextEditor: View, Modifiable { extension TextEditor: InputModifier { + public func disabled(_ condition: Bool) -> TextEditor { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> TextEditor { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension TextEditor: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> TextEditor { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> TextEditor { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> TextEditor { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> TextEditor { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> TextEditor { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> TextEditor { return self.mutate(bordershape: shape.rawValue) } @@ -260,13 +327,8 @@ extension TextEditor: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> TextEditor { - - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } - - return self + public func colorScheme(_ scheme: Tokens.ColorScheme) -> TextEditor { + return self.mutate(scheme: scheme.rawValue) } } @@ -279,22 +341,27 @@ public struct CheckField: View, Modifiable { /// The content of the field. internal let value: String + /// The content of the field. + internal var content: String + /// The classes of the field. internal var classes: [String] /// Creates a check field. - public init(name: String, value: String) { + public init(name: String, value: String, content: () -> String) { self.name = name self.value = value - self.classes = ["input", "type:checkfield"] + self.content = content() + self.classes = ["checkfield"] } /// Creates a check field. - internal init(name: String, value: String, classes: [String]) { + internal init(name: String, value: String, content: String, classes: [String]) { self.name = name self.value = value + self.content = content self.classes = classes } @@ -305,11 +372,52 @@ public struct CheckField: View, Modifiable { .name(name) .value(value) .class(classes.joined(separator: " ")) + Label { + content + } + .class("label") + .for(name) } } extension CheckField: InputModifier { + public func disabled(_ condition: Bool) -> CheckField { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> CheckField { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension CheckField: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> CheckField { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> CheckField { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> CheckField { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> CheckField { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> CheckField { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> CheckField { return self.mutate(bordershape: shape.rawValue) } @@ -318,13 +426,8 @@ extension CheckField: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> CheckField { - - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } - - return self + public func colorScheme(_ scheme: Tokens.ColorScheme) -> CheckField { + return self.mutate(scheme: scheme.rawValue) } } @@ -337,22 +440,27 @@ public struct RadioSelect: View, Modifiable { /// The content of the select. internal let value: String + /// The content of the select. + internal var content: String + /// The classes of the select. internal var classes: [String] /// Creates a radio select. - public init(name: String, value: String) { + public init(name: String, value: String, content: () -> String) { self.name = name self.value = value - self.classes = ["input", "type:radioselect"] + self.content = content() + self.classes = ["radioselect"] } /// Creates a radio select. - internal init(name: String, value: String, classes: [String]) { + internal init(name: String, value: String, content: String, classes: [String]) { self.name = name self.value = value + self.content = content self.classes = classes } @@ -363,11 +471,52 @@ public struct RadioSelect: View, Modifiable { .name(name) .value(value) .class(classes.joined(separator: " ")) + Label { + content + } + .class("label") + .for(name) } } extension RadioSelect: InputModifier { + public func disabled(_ condition: Bool) -> RadioSelect { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> RadioSelect { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension RadioSelect: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> RadioSelect { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> RadioSelect { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> RadioSelect { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> RadioSelect { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> RadioSelect { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> RadioSelect { return self.mutate(bordershape: shape.rawValue) } @@ -376,13 +525,8 @@ extension RadioSelect: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> RadioSelect { - - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } - - return self + public func colorScheme(_ scheme: Tokens.ColorScheme) -> RadioSelect { + return self.mutate(scheme: scheme.rawValue) } } @@ -406,7 +550,7 @@ public struct SelectField: View, Modifiable { self.name = name self.content = content() - self.classes = ["input", "type:selectfield"] + self.classes = ["selectfield"] } /// Creates a select field. @@ -419,17 +563,59 @@ public struct SelectField: View, Modifiable { } public var body: Content { - Select { - content + Division { + Input() + .type(.text) + .id(name) + .name(name) + .class("selectfield-textfield") + Division { + content + } + .class("selectfield-optionlist") } - .id(name) - .name(name) .class(classes.joined(separator: " ")) } } extension SelectField: InputModifier { + public func disabled(_ condition: Bool) -> SelectField { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> SelectField { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension SelectField: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> SelectField { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> SelectField { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> SelectField { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> SelectField { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> SelectField { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> SelectField { return self.mutate(bordershape: shape.rawValue) } @@ -438,13 +624,43 @@ extension SelectField: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> SelectField { + public func colorScheme(_ scheme: Tokens.ColorScheme) -> SelectField { + return self.mutate(scheme: scheme.rawValue) + } +} + +/// A component that displays +public struct Option: View, Modifiable { + + /// The identifier of the field. + internal let value: String + + internal let content: String + + /// The classes of the field. + internal var classes: [String] + + /// Creates a select field. + public init(value: String, content: () -> String) { - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } + self.value = value + self.content = content() + self.classes = ["option"] + } + + /// Creates a select field. + internal init(value: String, content: String, classes: [String]) { - return self + self.value = value + self.content = content + self.classes = classes + } + + public var body: Content { + ListItem { + content + } + .class(classes.joined(separator: " ")) } } @@ -472,7 +688,7 @@ public struct SecureField: View, Modifiable { self.name = name self.prompt = prompt self.value = value - self.classes = ["input", "type:securefield"] + self.classes = ["securefield"] } /// Creates a password field. @@ -502,6 +718,42 @@ public struct SecureField: View, Modifiable { extension SecureField: InputModifier { + public func disabled(_ condition: Bool) -> SecureField { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> SecureField { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension SecureField: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> SecureField { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> SecureField { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> SecureField { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> SecureField { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> SecureField { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> SecureField { return self.mutate(bordershape: shape.rawValue) } @@ -510,18 +762,13 @@ extension SecureField: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> SecureField { - - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } - - return self + public func colorScheme(_ scheme: Tokens.ColorScheme) -> SecureField { + return self.mutate(scheme: scheme.rawValue) } } /// A component that displays -public struct Slider: View { +public struct Slider: View, Modifiable { /// The identifier of the slider. internal let name: String @@ -536,7 +783,7 @@ public struct Slider: View { public init(name: String) { self.name = name - self.classes = ["input", "type:slider"] + self.classes = ["slider"] } /// Creates a slider. @@ -556,6 +803,57 @@ public struct Slider: View { } } +extension Slider: InputModifier { + + public func disabled(_ condition: Bool) -> Slider { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> Slider { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension Slider: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> Slider { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> Slider { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> Slider { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> Slider { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> Slider { + return self.mutate(bordercolor: color.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> Slider { + return self.mutate(bordershape: shape.rawValue) + } + + public func backgroundColor(_ color: Tokens.BackgroundColor) -> Slider { + return self.mutate(backgroundcolor: color.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> Slider { + return self.mutate(scheme: scheme.rawValue) + } +} + /// A component that displays public struct DatePicker: View, Modifiable { @@ -576,7 +874,7 @@ public struct DatePicker: View, Modifiable { self.name = name self.value = value - self.classes = ["input", "type:datepicker"] + self.classes = ["datepicker"] } /// Creates a date picker. @@ -589,19 +887,134 @@ public struct DatePicker: View, Modifiable { } public var body: Content { - Input() - .type(.date) - .id(name) - .name(name) - .class(classes.joined(separator: " ")) - .modify(unwrap: value) { - $0.value($1) + Division { + Input() + .type(.text) + .class("datepicker-datefield") + .id(name) + .name(name) + .modify(unwrap: value) { + $0.value($1) + } + self.calendar + } + .class(classes.joined(separator: " ")) + } + + public var calendar: Content { + Division { + UnorderedList { + ListItem { + HTMLKit.Button { + Vector { + Polyline { + } + .points("10 2 4 8 10 14") + } + .viewBox("0 0 16 16") + .namespace("http://www.w3.org/2000/svg") + .fill("currentColor") + .strokeWidth(2) + .strokeLineCap(.round) + .strokeLineJoin(.round) + } + .type(.button) + .value("previous") + } + ListItem { + Bold { + } + .class("calendar-detail") + } + ListItem { + HTMLKit.Button { + Vector { + Polyline { + } + .points("6 2 12 8 6 14") + } + .viewBox("0 0 16 16") + .namespace("http://www.w3.org/2000/svg") + .fill("currentColor") + .strokeWidth(2) + .strokeLineCap(.round) + .strokeLineJoin(.round) + } + .type(.button) + .value("next") + } } + .class("calendar-navigation") + UnorderedList { + ListItem { + "Sun" + } + ListItem { + "Mon" + } + ListItem { + "Tue" + } + ListItem { + "Wed" + } + ListItem { + "Thu" + } + ListItem { + "Fri" + } + ListItem { + "Sat" + } + } + .class("calendar-week") + UnorderedList { + } + .class("calendar-days") + } + .class("datepicker-calendar") } } extension DatePicker: InputModifier { + public func disabled(_ condition: Bool) -> DatePicker { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> DatePicker { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension DatePicker: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> DatePicker { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> DatePicker { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> DatePicker { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> DatePicker { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> DatePicker { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> DatePicker { return self.mutate(bordershape: shape.rawValue) } @@ -610,13 +1023,8 @@ extension DatePicker: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> DatePicker { - - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } - - return self + public func colorScheme(_ scheme: Tokens.ColorScheme) -> DatePicker { + return self.mutate(scheme: scheme.rawValue) } } @@ -644,7 +1052,7 @@ public struct SearchField: View, Modifiable { self.name = name self.prompt = prompt self.value = value - self.classes = ["input", "type:searchfield"] + self.classes = ["searchfield"] } /// Creates a search field. @@ -674,6 +1082,42 @@ public struct SearchField: View, Modifiable { extension SearchField: InputModifier { + public func disabled(_ condition: Bool) -> SearchField { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> SearchField { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension SearchField: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> SearchField { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> SearchField { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> SearchField { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> SearchField { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> SearchField { + return self.mutate(bordercolor: color.rawValue) + } + public func borderShape(_ shape: Tokens.BorderShape) -> SearchField { return self.mutate(bordershape: shape.rawValue) } @@ -682,13 +1126,8 @@ extension SearchField: InputModifier { return self.mutate(backgroundcolor: color.rawValue) } - public func disabled(_ condition: Bool) -> SearchField { - - if condition { - return self.mutate(state: Tokens.ViewState.disabled.rawValue) - } - - return self + public func colorScheme(_ scheme: Tokens.ColorScheme) -> SearchField { + return self.mutate(scheme: scheme.rawValue) } } @@ -738,7 +1177,7 @@ public struct Progress: View { } /// A component to edit and format text content. -public struct TextPad: View { +public struct TextPad: View, Modifiable { /// The identifier of the textpad. internal let name: String @@ -747,7 +1186,7 @@ public struct TextPad: View { internal let prompt: String? /// The number of lines. - internal var rows: Int = 1 + internal var rows: Int = 3 /// The content of the textpad. internal var content: [String] @@ -801,6 +1240,22 @@ public struct TextPad: View { } } .class("toolbar-tool command:strikethrough") + ListItem { + Symbol(system: "text.alignleft") + } + .class("toolbar-tool") + ListItem { + Symbol(system: "text.aligncenter") + } + .class("toolbar-tool") + ListItem { + Symbol(system: "text.alignright") + } + .class("toolbar-tool") + ListItem { + Symbol(system: "text.alignjustify") + } + .class("toolbar-tool") } .class("textpad-toolbar") TextArea { @@ -823,3 +1278,54 @@ public struct TextPad: View { return newSelf } } + +extension TextPad: InputModifier { + + public func disabled(_ condition: Bool) -> TextPad { + + if condition { + return self.mutate(inputstate: Tokens.ViewState.disabled.rawValue) + } + + return self + } + + public func focusColor(_ color: Tokens.FocusColor) -> TextPad { + return self.mutate(focuscolor: color.rawValue) + } +} + +extension TextPad: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> TextPad { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> TextPad { + return self.mutate(zindex: index.rawValue) + } + + public func hidden() -> TextPad { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> TextPad { + return self.mutate(padding: length.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> TextPad { + return self.mutate(bordercolor: color.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> TextPad { + return self.mutate(bordershape: shape.rawValue) + } + + public func backgroundColor(_ color: Tokens.BackgroundColor) -> TextPad { + return self.mutate(backgroundcolor: color.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> TextPad { + return self.mutate(scheme: scheme.rawValue) + } +} diff --git a/Sources/HTMLKitComponents/Components/Image.swift b/Sources/HTMLKitComponents/Components/Image.swift index 418cdd09..c4ee10e8 100644 --- a/Sources/HTMLKitComponents/Components/Image.swift +++ b/Sources/HTMLKitComponents/Components/Image.swift @@ -29,12 +29,10 @@ public struct Image: View, Modifiable { } public var body: Content { - Division { - HTMLKit.Image() - .source(source) - .role(.img) - } - .class(classes.joined(separator: " ")) + HTMLKit.Image() + .source(source) + .role(.img) + .class(classes.joined(separator: " ")) } } @@ -68,6 +66,22 @@ extension Image: ViewModifier { } public func hidden() -> Image { - return self.mutate(state: Tokens.ViewState.hidden.rawValue) + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> Image { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> Image { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> Image { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> Image { + return self.mutate(bordercolor: color.rawValue) } } diff --git a/Sources/HTMLKitComponents/Components/Link.swift b/Sources/HTMLKitComponents/Components/Link.swift index 842b8690..f2bfc5ac 100644 --- a/Sources/HTMLKitComponents/Components/Link.swift +++ b/Sources/HTMLKitComponents/Components/Link.swift @@ -8,6 +8,9 @@ import Foundation /// A component that navigates to an target. public struct Link: View, Modifiable { + + /// The target for the destination + internal let target: HTMLKit.Values.Target /// The url path of the target. internal let destination: String @@ -22,17 +25,28 @@ public struct Link: View, Modifiable { internal var events: [String]? /// Creates a link. - public init(destination: String, @ContentBuilder content: () -> [Content]) { + public init(destination: String, target: HTMLKit.Values.Target = .current, @ContentBuilder content: () -> [Content]) { self.destination = destination + self.target = target + self.content = content() + self.classes = ["link"] + } + + /// Creates a link. + public init(destination: URL, target: HTMLKit.Values.Target = .current, @ContentBuilder content: () -> [Content]) { + + self.destination = destination.absoluteString + self.target = target self.content = content() self.classes = ["link"] } /// Creates a link. - internal init(destination: String, content: [Content], classes: [String], events: [String]?) { + internal init(destination: String, target: HTMLKit.Values.Target, content: [Content], classes: [String], events: [String]?) { self.destination = destination + self.target = target self.content = content self.classes = classes self.events = events @@ -43,6 +57,7 @@ public struct Link: View, Modifiable { content } .reference(destination) + .target(target) .class(classes.joined(separator: " ")) } } diff --git a/Sources/HTMLKitComponents/Components/List.swift b/Sources/HTMLKitComponents/Components/List.swift index 837f31e6..3b16399a 100644 --- a/Sources/HTMLKitComponents/Components/List.swift +++ b/Sources/HTMLKitComponents/Components/List.swift @@ -6,7 +6,7 @@ import HTMLKit /// A component that collects and arranges list items vertically. -public struct List: View, Actionable { +public struct List: View, Modifiable, Actionable { var id: String? diff --git a/Sources/HTMLKitComponents/Components/Snippet.swift b/Sources/HTMLKitComponents/Components/Snippet.swift index d7981022..e45c6ee9 100644 --- a/Sources/HTMLKitComponents/Components/Snippet.swift +++ b/Sources/HTMLKitComponents/Components/Snippet.swift @@ -6,7 +6,7 @@ import HTMLKit /// A component that displays code content. -public struct Snippet: View { +public struct Snippet: View, Modifiable { /// The content of the snippet. internal var content: [Content] @@ -43,3 +43,38 @@ public struct Snippet: View { .class(classes.joined(separator: " ")) } } + +extension Snippet: ViewModifier { + + public func opacity(_ value: Tokens.OpacityValue) -> Snippet { + return self.mutate(opacity: value.rawValue) + } + + public func zIndex(_ index: Tokens.PositionIndex) -> Snippet { + return self.mutate(zindex: index.rawValue) + } + + public func backgroundColor(_ color: Tokens.BackgroundColor) -> Snippet { + return self.mutate(backgroundcolor: color.rawValue) + } + + public func hidden() -> Snippet { + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> Snippet { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> Snippet { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> Snippet { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> Snippet { + return self.mutate(bordercolor: color.rawValue) + } +} diff --git a/Sources/HTMLKitComponents/Components/Stack.swift b/Sources/HTMLKitComponents/Components/Stack.swift index ce2aa52e..4ea6821e 100644 --- a/Sources/HTMLKitComponents/Components/Stack.swift +++ b/Sources/HTMLKitComponents/Components/Stack.swift @@ -91,7 +91,23 @@ extension HStack: ViewModifier { } public func hidden() -> HStack { - return self.mutate(state: Tokens.ViewState.hidden.rawValue) + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> HStack { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ value: Tokens.BoxPadding) -> HStack { + return self.mutate(padding: value.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> HStack { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> HStack { + return self.mutate(bordercolor: color.rawValue) } } @@ -170,7 +186,23 @@ extension VStack: ViewModifier { } public func hidden() -> VStack { - return self.mutate(state: Tokens.ViewState.hidden.rawValue) + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> VStack { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> VStack { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> VStack { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> VStack { + return self.mutate(bordercolor: color.rawValue) } } @@ -249,7 +281,23 @@ extension ZStack: ViewModifier { } public func hidden() -> ZStack { - return self.mutate(state: Tokens.ViewState.hidden.rawValue) + return self.mutate(viewstate: Tokens.ViewState.hidden.rawValue) + } + + public func colorScheme(_ scheme: Tokens.ColorScheme) -> ZStack { + return self.mutate(scheme: scheme.rawValue) + } + + public func padding(_ length: Tokens.BoxPadding) -> ZStack { + return self.mutate(padding: length.rawValue) + } + + public func borderShape(_ shape: Tokens.BorderShape) -> ZStack { + return self.mutate(bordershape: shape.rawValue) + } + + public func borderColor(_ color: Tokens.BorderColor) -> ZStack { + return self.mutate(bordercolor: color.rawValue) } } diff --git a/Sources/HTMLKitComponents/Components/Symbol.swift b/Sources/HTMLKitComponents/Components/Symbol.swift index b5895d71..31f5b84d 100644 --- a/Sources/HTMLKitComponents/Components/Symbol.swift +++ b/Sources/HTMLKitComponents/Components/Symbol.swift @@ -51,7 +51,6 @@ public struct Symbol: View, Modifiable { content } .viewBox("0 0 20 16") - .fill("currentColor") .class(classes.joined(separator: " ")) } diff --git a/Sources/HTMLKitComponents/Components/Toggle.swift b/Sources/HTMLKitComponents/Components/Toggle.swift deleted file mode 100644 index b2ae8176..00000000 --- a/Sources/HTMLKitComponents/Components/Toggle.swift +++ /dev/null @@ -1,48 +0,0 @@ -/* - Abstract: - The file contains everything related to the toggle component. - */ - -import HTMLKit - -/// A component that shows an on or off state. -public struct Toggle: View { - - /// The identifier of the toggle. - internal let name: String - - /// The classes of the toggle. - internal var classes: [String] - - /// The events of the toggle. - internal var events: [String]? - - /// Creates a toggle. - public init(name: String) { - - self.name = name - self.classes = ["toggle"] - } - - /// Creates a toggle. - internal init(name: String, classes: [String], events: [String]?) { - - self.name = name - self.classes = classes - self.events = events - } - - public var body: Content { - Label { - Input() - .type(.checkbox) - .id(name) - .name(name) - Division { - } - .class("toggle-slider") - } - .tabIndex(0) - .class(classes.joined(separator: " ")) - } -} diff --git a/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift b/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift index b097d921..5197af9b 100644 --- a/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift @@ -12,12 +12,6 @@ public protocol ButtonModifier { /// Sets the style of the button. func buttonStyle(_ style: Tokens.ButtonStyle) -> Self - /// Sets the shape of the button. - func borderShape(_ shape: Tokens.BorderShape) -> Self - - /// Sets the background color. - func backgroundColor(_ color: Tokens.BackgroundColor) -> Self - /// Sets the state of the view. func disabled(_ condition: Bool) -> Self } @@ -32,15 +26,7 @@ extension ButtonModifier where Self: Modifiable { return self.mutate(class: `class`) } - internal func mutate(bordershape class: String) -> Self { - return self.mutate(class: `class`) - } - - internal func mutate(backgroundcolor class: String) -> Self { - return self.mutate(class: `class`) - } - - internal func mutate(state class: String) -> Self { + internal func mutate(buttonstate class: String) -> Self { return self.mutate(class: `class`) } } diff --git a/Sources/HTMLKitComponents/Modifiers/InputModifier.swift b/Sources/HTMLKitComponents/Modifiers/InputModifier.swift index 07e10a83..7da42597 100644 --- a/Sources/HTMLKitComponents/Modifiers/InputModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/InputModifier.swift @@ -6,27 +6,20 @@ /// A type that describes the modifier of a input component. public protocol InputModifier { - /// Sets the border shape of the input. - func borderShape(_ shape: Tokens.BorderShape) -> Self - - /// Sets the background color of the input. - func backgroundColor(_ color: Tokens.BackgroundColor) -> Self - /// Sets the state of the view. func disabled(_ condition: Bool) -> Self + + /// Sets the focus color of the input. + func focusColor(_ color: Tokens.FocusColor) -> Self } extension InputModifier where Self: Modifiable { - internal func mutate(bordershape class: String) -> Self { - return self.mutate(class: `class`) - } - - internal func mutate(backgroundcolor class: String) -> Self { + internal func mutate(inputstate class: String) -> Self { return self.mutate(class: `class`) } - internal func mutate(state class: String) -> Self { + internal func mutate(focuscolor class: String) -> Self { return self.mutate(class: `class`) } } diff --git a/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift b/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift index a3be0178..00ec1363 100644 --- a/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift @@ -17,6 +17,18 @@ public protocol ViewModifier { /// Hides the view func hidden() -> Self + + /// Sets the color appearence + func colorScheme(_ scheme: Tokens.ColorScheme) -> Self + + /// Sets the box padding + func padding(_ length: Tokens.BoxPadding) -> Self + + /// Sets the shape of the button. + func borderShape(_ shape: Tokens.BorderShape) -> Self + + /// Sets the border color of the input + func borderColor(_ color: Tokens.BorderColor) -> Self } extension ViewModifier where Self: Modifiable { @@ -33,7 +45,23 @@ extension ViewModifier where Self: Modifiable { return self.mutate(class: `class`) } - internal func mutate(state class: String) -> Self { + internal func mutate(viewstate class: String) -> Self { + return self.mutate(class: `class`) + } + + internal func mutate(scheme class: String) -> Self { + return self.mutate(class: `class`) + } + + internal func mutate(padding class: String) -> Self { + return self.mutate(class: `class`) + } + + internal func mutate(bordershape class: String) -> Self { + return self.mutate(class: `class`) + } + + internal func mutate(bordercolor class: String) -> Self { return self.mutate(class: `class`) } } diff --git a/Sources/HTMLKitComponents/Resources/css/background.css b/Sources/HTMLKitComponents/Resources/css/background.css deleted file mode 100644 index 73fdd78e..00000000 --- a/Sources/HTMLKitComponents/Resources/css/background.css +++ /dev/null @@ -1,79 +0,0 @@ -/** - background component - */ - -.background\:black { - background-color: var(--blackColor); -} - -.background\:blue { - background-color: var(--blueColor); -} - -.background\:brown { - background-color: var(--brownColor); -} - -.background\:transparent { - background-color: transparent; -} - -.background\:cyan { - background-color: var(--cyanColor); -} - -.background\:gray { - background-color: var(--grayColor); -} - -.background\:green { - background-color: var(--greenColor); -} - -.background\:indigo { - background-color: var(--indigoColor); -} - -.background\:mint { - background-color: var(--mintColor); -} - -.background\:orange { - background-color: var(--orangeColor); -} - -.background\:pink { - background-color: var(--pinkColor); -} - -.background\:purple { - background-color: var(--purpleColor); -} - -.background\:red { - background-color: var(--redColor); -} - -.background\:teal { - background-color: var(--tealColor); -} - -.background\:white { - background-color: var(--whiteColor); -} - -.background\:yellow { - background-color: var(--yellowColor); -} - -.background\:silver { - background-color: var(--silverColor); -} - -.background\:primary { - background-color: var(--primaryColor); -} - -.background\:secondary { - background-color: var(--secondaryColor); -} diff --git a/Sources/HTMLKitComponents/Resources/css/button.css b/Sources/HTMLKitComponents/Resources/css/button.css deleted file mode 100644 index 5193b516..00000000 --- a/Sources/HTMLKitComponents/Resources/css/button.css +++ /dev/null @@ -1,83 +0,0 @@ -/** - button component - */ - -.button { - display: inline-flex; - flex-direction: row; - align-items: center; - justify-content: center; - padding-top: var(--buttonPaddingY); - padding-right: var(--buttonPaddingX); - padding-bottom: var(--buttonPaddingY); - padding-left: var(--buttonPaddingX); - font-family: var(--textFontFamily); - font-size: 16px; - text-decoration: none; - border-width: var(--buttonBorderWidth); - border-style: solid; - cursor: pointer; - -webkit-user-select: none; -} - -.button:hover { - filter: brightness(90%) saturate(90%); -} - -.button.style\:primary { - color: var(--whiteColor); - background-color: var(--primaryColor); - border-color: var(--primaryColor); -} - -.button.style\:secondary { - color: var(--whiteColor); - background-color: var(--secondaryColor); - border-color: var(--secondaryColor); -} - -.button.style\:outline { - color: var(--secondaryColor); - background-color: transparent; - border-color: var(--secondaryColor); -} - -.button.size\:full { - width: 100%; -} - -.button.size\:large { - width: 75%; -} - -.button.size\:medium { - width: 50%; -} - -.button.size\:small { - width: 25%; -} - -.button.shape\:smallrounded { - border-radius: 5px; -} - -.button.shape\:largerounded { - border-radius: 10px; -} - -.button.shape\:fullrounded { - border-radius: 25px; -} - -.button.shape\:circle { - border-radius: 50%; -} - -.button > .symbol + * { - margin-left: 15px; -} - -.button > * + .symbol { - margin-left: 15px; -} diff --git a/Sources/HTMLKitComponents/Resources/css/buttons/button.css b/Sources/HTMLKitComponents/Resources/css/buttons/button.css new file mode 100644 index 00000000..72369f61 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/buttons/button.css @@ -0,0 +1,109 @@ +/* + The stylesheet for the button component. + + default: + block padding: 8px + inline padding: 16px + font size: 16px + font weight: normal + line height: 1.5 + foreground color: #FFFFFF + border width: 1px + border color: #DFE3E7 + border radius: 0 + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + background color: #22262a + */ + +.button { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --inlineSize: fit-content(); + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --borderWidth: 1px; + --borderColor: 210, 9%, 31%; + --borderRadius: 0; + --foregroundColor: 0, 0%, 100%; + --backgroundColor: 210, 11%, 15%; + + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + inline-size: var(--inlineSize); + font-family: var(--fontFamily); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + text-decoration: none; + white-space: nowrap; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + background-color: hsla(var(--backgroundColor), 1.0); + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.button:hover { + filter: brightness(90%) saturate(90%); +} + +.button.style\:primary { + --foregroundColor: var(--whiteColor); + --backgroundColor: var(--primaryColor); + --borderColor: var(--primaryColor); +} + +.button.style\:secondary { + --foregroundColor: var(--whiteColor); + --backgroundColor: var(--secondaryColor); + --borderColor: var(--secondaryColor); +} + +.button.style\:outline { + --foregroundColor: var(--secondaryColor); + --backgroundColor: transparent; + --borderColor: var(--secondaryColor); +} + +.button.size\:full { + --inlineSize: 100%; +} + +.button.size\:large { + --inlineSize: 75%; +} + +.button.size\:medium { + --inlineSize: 50%; +} + +.button.size\:small { + --inlineSize: 25%; +} + +.button > .symbol + .text { + margin-inline-start: 0.938em; +} + +.button > .text + .symbol { + margin-inline-start: 0.938em; +} + +.scheme\:dark .button, .button.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/buttons/dropdown.css b/Sources/HTMLKitComponents/Resources/css/buttons/dropdown.css new file mode 100644 index 00000000..88e28ed9 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/buttons/dropdown.css @@ -0,0 +1,108 @@ +/* + The stylesheet for the dropdown component. + + default: + block padding: 8px + inline padding: 16px + font size: 16px + font weight: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + background color: #22262a + */ + +.dropdown { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --inlineSize: fit-content(); + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --borderWidth: 1px; + --borderColor: 210, 9%, 31%; + --borderRadius: 0; + --foregroundColor: 0, 0%, 100%; + --backgroundColor: 210, 11%, 15%; + + position: relative; +} + +.dropdown-label { + display: flex; + align-items: center; + justify-content: center; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + inline-size: var(--inlineSize); + font-family: var(--fontFamily); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + text-decoration: none; + white-space: nowrap; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.dropdown-label:hover { + filter: brightness(90%) saturate(90%); +} + +.dropdown .dropdown-content { + position: absolute; + z-index: 1; + display: none; + margin-block: 0.75rem; + inline-size: calc(var(--inlineSize) * 1.5); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + overflow: hidden; +} + +.dropdown .list .list-row:hover { + color: hsla(var(--whiteColor), 1.0); + background-color: hsla(var(--primaryColor), 1.0); +} + +.dropdown .list .list-row .link { + display: block; +} + +.dropdown.size\:full { + --inlineSize: 100%; +} + +.dropdown.size\:large { + --inlineSize: 75%; +} + +.dropdown.size\:medium { + --inlineSize: 50%; +} + +.scheme\:dark .dropdown, .dropdown.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --backgroundColor: 210, 11%, 15%; +} + + diff --git a/Sources/HTMLKitComponents/Resources/css/card.css b/Sources/HTMLKitComponents/Resources/css/card.css deleted file mode 100644 index 193bca4d..00000000 --- a/Sources/HTMLKitComponents/Resources/css/card.css +++ /dev/null @@ -1,38 +0,0 @@ -/** - card component - */ - -.card { - - display: flex; - flex-direction: column; - border-width: var(--cardBorderWidth); - border-style: solid; - border-radius: var(--cardBorderRadius); - border-color: var(--cardBorderColor); - background-color: var(--cardBackgroundColor); -} - -.card .card-header { - - max-height: var(--cardHeaderHight); - border-top-left-radius: inherit; - border-top-right-radius: inherit; - background-color: var(--cardHeaderBackgroundColor); - overflow: hidden; -} - -.card .card-header .image { - - border-radius: inherit; -} - -.card .card-body { - - padding-top: var(--cardPaddingY); - padding-right: var(--cardPaddingX); - padding-bottom: var(--cardPaddingY); - padding-left: var(--cardPaddingX); - border-bottom-left-radius: inherit; - border-bottom-right-radius: inherit; -} diff --git a/Sources/HTMLKitComponents/Resources/css/carousel.css b/Sources/HTMLKitComponents/Resources/css/carousel.css deleted file mode 100644 index 9b804249..00000000 --- a/Sources/HTMLKitComponents/Resources/css/carousel.css +++ /dev/null @@ -1,73 +0,0 @@ -/** - carousel component - */ - -.carousel { - display: flex; - flex-direction: column; - width: 100%; - height: 400px; -} - -.carousel-content { - display: grid; - grid-auto-flow: column; - grid-auto-columns: 100%; - height: 350px; - border-width: var(--carouselBorderWidth); - border-style: solid; - border-color: var(--carouselBorderColor); - border-radius: var(--carouselBorderRadius); - background-color: var(--carouselBackgroundColor); - overflow: hidden; -} - -.carousel-content::-webkit-scrollbar{ - display: none; -} - -.slide { - display: flex; - height: auto; - overflow: hidden; -} - -.slide-thumbnail { - width: 60%; -} - -.slide-thumbnail > img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.slide-caption { - width: 40%; - padding-block: 24px; - padding-inline: 24px; -} - -.carousel-indication { - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - height: 50px; -} - -.indicator { - width: 40px; - height: 6px; - border-width: 1px; - border-style: solid; - border-color: var(--indicatorBorderColor); - border-radius: var(--indicatorBorderRadius); - background-color: var(--indicatorBackgroundColor); - cursor: pointer; -} - -.indicator:hover { - border-color: var(--primaryColor); - background-color: var(--primaryColor); -} diff --git a/Sources/HTMLKitComponents/Resources/css/divider.css b/Sources/HTMLKitComponents/Resources/css/divider.css deleted file mode 100644 index eb697050..00000000 --- a/Sources/HTMLKitComponents/Resources/css/divider.css +++ /dev/null @@ -1,13 +0,0 @@ -/** - divider component - */ - -.divider { - display: block; - margin-top: 5px; - margin-bottom: 5px; - width: 100%; - height: 1px; - border: 0; - background-color: var(--dividerColor); -} diff --git a/Sources/HTMLKitComponents/Resources/css/dropdown.css b/Sources/HTMLKitComponents/Resources/css/dropdown.css deleted file mode 100644 index d548744c..00000000 --- a/Sources/HTMLKitComponents/Resources/css/dropdown.css +++ /dev/null @@ -1,33 +0,0 @@ -.dropdown { - display: inline-block; - position: relative; - cursor: pointer; - outline: none; -} - -.dropdown-label { - display: inline-block; -} - -.dropdown .dropdown-content { - display: none; - position: absolute; - padding: 6px 0px; - width: 200px; - border: 1px solid var(--dropdownBorderColor); - border-radius: var(--dropdownBorderRadius); - background-color: var(--dropdownBackgroundColor); - z-index: 1; -} - -.dropdown:focus .dropdown-content { - display: inline-block; -} - -.dropdown .list .list-row:hover { - background-color: var(--dropdownHoverColor); -} - -.dropdown .list .list-row .link { - display: block; -} diff --git a/Sources/HTMLKitComponents/Resources/css/form.css b/Sources/HTMLKitComponents/Resources/css/form.css deleted file mode 100644 index 87b1be1c..00000000 --- a/Sources/HTMLKitComponents/Resources/css/form.css +++ /dev/null @@ -1,146 +0,0 @@ -/** - form component - */ - -.form { - display: block; - width: 100%; - height: auto; -} - -.label { - display: inline-block; - margin-top: 20px; - margin-bottom: 10px; - font-family: var(--textFontFamily); - font-size: var(--textFontSize); - font-weight: var(--textFontHeight); - line-height: var(--textLineHeight); - color: var(--textBaseColor); -} - -.input { - padding-top: var(--inputPaddingY); - padding-right: var(--inputPaddingX); - padding-bottom: var(--inputPaddingY); - padding-left: var(--inputPaddingX); - border-width: var(--inputBorderWidth); - border-style: solid; - border-color: var(--inputBorderColor); - border-radius: var(--inputBorderRadius); - font-size: 1.0rem; - font-weight: 400; - color: var(--inputTextColor); - background-color: var(--inputBackgroundColor); - box-sizing: border-box; -} - -.input.shape\:smallrounded { - border-radius: 5px; -} - -.input.shape\:largerounded { - border-radius: 10px; -} - -.input.shape\:fullrounded { - border-radius: 25px; -} - -.input:focus { - outline: 0; - box-shadow: 0 0 0 4px var(--inputFocusColor); -} - -.input:invalid { - border-color: var(--redColor); -} - -.input.type\:textfield { - display: block; - width: 100%; -} - -.input.type\:texteditor { - display: block; - width: 100%; - min-height: 35px; - resize: none; -} - -.input.type\:checkbox { - display: inline-block; - height: 35px; -} - -.input.type\:checkfield { - display: inline-block; - width: 35px; -} - -.input.type\:radioselect { - display: inline-block; - width: 35px; -} - -.input.type\:slider { - display: block; - width: 100% !important; - height: 3px; - padding-top: 0; - padding-right: 0; - padding-bottom: 0; - background-color: var(--sliderBackgroundColor); - border-width: 1px; - border-style: solid; - border-color: var(--sliderBorderColor); - border-radius: 5px; - appearance: none; - -webkit-appearance: none; -} - -.input.type\:slider::-webkit-slider-thumb { - width: 20px; - height: 20px; - border-width: 1px; - border-style: solid; - border-color: var(--thumbBorderColor); - border-radius: 50%; - background-color: var(--thumbBackgroundColor); - appearance: none; - -webkit-appearance: none; - cursor: pointer; -} - -.input.type\:slider::-moz-range-thumb { - width: 20px; - height: 20px; - border-width: 1px; - border-style: solid; - border-color: var(--thumbBorderColor); - border-radius: 50%; - background-color: var(--thumbBackgroundColor); - cursor: pointer; -} - -.input.type\:datepicker { - display: block; - width: 100%; - height: 35px; -} - -.input.type\:securefield { - display: block; - width: 100%; -} - -.input.type\:searchfield { - display: block; - width: 100%; -} - -.input.type\:selectfield { - display: block; - width: 100%; - -webkit-appearance: none; -} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/checkfield.css b/Sources/HTMLKitComponents/Resources/css/forms/checkfield.css new file mode 100644 index 00000000..8cd17473 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/checkfield.css @@ -0,0 +1,64 @@ +/* + The rulesets for the checkfield component. + + defaults: + border width: 1px; + border color: #DFE3E7 + background color: #FFFFFF + + darkmode: + border color: #484F56 + background color: #FFFFFF + */ + +.checkfield { + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --controlColor: 0, 0%, 0%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + position: relative; + display: inline-grid; + place-content: center; + margin: 0; + padding: 0; + block-size: 1.5rem; + inline-size: 1.5rem; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.checkfield:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.checkfield:before { + content: ""; + block-size: 0.625rem; + inline-size: 0.625rem; + background-color: hsla(var(--controlColor), 1.0); + transform: scale(0); +} + +.checkfield:checked:before { + transform: scale(1); +} + +.checkfield + .label { + margin-inline-start: 1.0rem; +} + +.scheme\:dark .checkfield, .checkfield.scheme\:dark { + --borderColor: 210, 9%, 31%; + --backgroundColor: 210, 11%, 15%; + --controlColor: 0, 0%, 100%; + --focusColor: 210, 100%, 50%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/datepicker.css b/Sources/HTMLKitComponents/Resources/css/forms/datepicker.css new file mode 100644 index 00000000..e4958f60 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/datepicker.css @@ -0,0 +1,164 @@ +/* + The rulesets for the datepicker component. + + default: + block padding: 8px + inline padding: 16px + font size: + font weigth: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + focus color: #0080FF + background color: #22262A + */ + +.datepicker { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --fontSize: 1.0rem; + --fontWeight: normal; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + position: relative; +} + +.datepicker-datefield { + display: block; + inline-size: 100%; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.datepicker-datefield:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.datepicker-datefield:focus .datepicker-picker { + display: inline-block; +} + +.datepicker-datefield:invalid { + border-color: var(--redColor); +} + +.datepicker-calendar { + position: absolute; + z-index: 1; + display: none; + width: 100%; + margin-block: 0.75rem; + font-family: system-ui; + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + background-color: hsla(var(--backgroundColor), 1.0); +} + +.calendar-navigation { + display: flex; + align-items : center; + justify-content: space-between; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + list-style-type: none; + +} + +.calendar-navigation button { + display: inline-block; + padding: 0.5rem; + font-family: system-ui; + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: var(--foregroundColor); + border: none; + background-color: transparent; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.calendar-navigation button > svg { + + width: 1rem; + height: 1rem; + pointer-events: none; +} + +.calendar-week { + display: grid; + grid-template-columns: repeat(7, 1fr); + list-style-type: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.calendar-week > li { + padding: 0.5rem; + color: var(--primaryColor); + text-align: center; + text-decoration: uppercase; +} + +.calendar-days { + display: grid; + grid-template-columns: repeat(7, 1fr); + list-style-type: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.calendar-days > li:has(time) { + padding: 0.5rem; + text-align: center; + cursor: pointer; +} + +.calendar-days > li:hover:has(time) { + color: var(--whiteColor); + background-color: var(--primaryColor); +} + + +.scheme\:dark .datepicker, .datepicker.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/radioselect.css b/Sources/HTMLKitComponents/Resources/css/forms/radioselect.css new file mode 100644 index 00000000..b664c243 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/radioselect.css @@ -0,0 +1,68 @@ +/* + The rulesets for the radioselect component. + + defaults: + border width: 1px; + border color: #DFE3E7 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + border color: #484F56 + focus color: #0080FF + background color: #FFFFFF + */ + +.radioselect { + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --controlColor: 0, 0%, 0%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + position: relative; + display: inline-grid; + place-content: center; + margin: 0; + padding: 0; + block-size: 1.5rem; + inline-size: 1.5rem; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: 50%; + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.radioselect:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.radioselect:before { + content: ""; + block-size: 0.625rem; + inline-size: 0.625rem; + border-radius: 50%; + background-color: hsla(var(--controlColor), 1.0); + transform: scale(0); +} + +.radioselect:checked:before { + transform: scale(1); +} + +.radioselect + .label { + margin-inline-start: 1.0rem; +} + +.scheme\:dark .radioselect, .radioselect.scheme\:dark { + --borderColor: 210, 9%, 31%; + --controlColor: 0, 0%, 100%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/searchfield.css b/Sources/HTMLKitComponents/Resources/css/forms/searchfield.css new file mode 100644 index 00000000..f4114eae --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/searchfield.css @@ -0,0 +1,70 @@ +/* + The rulesets for the searchfield component. + + default: + block padding: 8px + inline padding: 16px + font size: + font weigth: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + focus color: #0080FF + background color: #22262A + */ + +.searchfield { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + display: block; + inline-size: 100%; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.searchfield:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.searchfield:invalid { + border-color: var(--redColor); +} + +.scheme\:dark .searchfield, .searchfield.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/securefield.css b/Sources/HTMLKitComponents/Resources/css/forms/securefield.css new file mode 100644 index 00000000..b5294957 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/securefield.css @@ -0,0 +1,70 @@ +/* + The rulesets for the securefield component. + + default: + block padding: 8px + inline padding: 16px + font size: + font weigth: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + focus color: #0080FF + background color: #22262A + */ + +.securefield { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --fontSize: 1.0rem; + --fontWeight: normal; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + display: block; + inline-size: 100%; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.securefield:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.securefield:invalid { + --borderColor: var(--redColor); +} + +.scheme\:dark .securefield, .securefield.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/selectfield.css b/Sources/HTMLKitComponents/Resources/css/forms/selectfield.css new file mode 100644 index 00000000..985d9fbc --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/selectfield.css @@ -0,0 +1,104 @@ +/* + The rulesets for the selectfield component. + + default: + block padding: 8px + inline padding: 16px + font size: + font weigth: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + focus color: #0080FF + background color: #22262A + */ + +.selectfield { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + position: relative; +} + +.selectfield-textfield { + display: block; + inline-size: 100%; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.selectfield-textfield:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.selectfield-textfield:invalid { + border-color: var(--redColor); +} + +.selectfield-optionlist { + position: absolute; + z-index: 1; + display: none; + width: 100%; + margin-block: 0.75rem; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + background-color: hsla(var(--backgroundColor), 1.0); + list-style-type: none; +} + +.option { + display: block; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-family: system-ui; + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + cursor: pointer; +} + +.option:hover { + color: hsla(var(--whiteColor), 1.0); + background-color: hsla(var(--primaryColor), 1.0); +} + +.scheme\:dark .selectfield, .selectfield.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/slider.css b/Sources/HTMLKitComponents/Resources/css/forms/slider.css new file mode 100644 index 00000000..5084707e --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/slider.css @@ -0,0 +1,67 @@ +/* + The rulesets for the slider component. + + defaults: + border width: 1px; + border color: #DFE3E7 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + border color: #484F56 + focus color: #0080FF + background color: #FFFFFF + */ + +.slider { + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --controlColor: 0, 0%, 0%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + display: block; + margin: 0; + padding: 0; + inline-size: 100%; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.slider:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.slider::-webkit-slider-thumb { + width: 1em; + height: 1em; + background-color: hsla(var(--controlColor), 1.0); + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.slider::-moz-range-thumb { + width: 1em; + height: 1em; + background-color: hsla(var(--controlColor), 1.0); + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.scheme\:dark .slider, .slider.scheme\:dark { + --borderColor: 210, 9%, 31%; + --controlColor: 0, 0%, 100%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/texteditor.css b/Sources/HTMLKitComponents/Resources/css/forms/texteditor.css new file mode 100644 index 00000000..90fd37d9 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/texteditor.css @@ -0,0 +1,71 @@ +/* + The rulesets for the texteditor component. + + default: + block padding: 8px + inline padding: 16px + font size: + font weigth: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + focus color: #0080FF + background color: #22262A + */ + +.texteditor { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + display: block; + inline-size: 100%; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + resize: none; +} + +.texteditor:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.texteditor:invalid { + border-color: var(--redColor); +} + +.scheme\:dark .texteditor, .texteditor.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/textfield.css b/Sources/HTMLKitComponents/Resources/css/forms/textfield.css new file mode 100644 index 00000000..ac435e5c --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/textfield.css @@ -0,0 +1,70 @@ +/* + The rulesets for the textfield component. + + default: + block padding: 8px + inline padding: 16px + font size: + font weigth: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + focus color: #0080FF + background color: #22262A + */ + +.textfield { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + display: block; + inline-size: 100%; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.textfield:focus { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.textfield:invalid { + border-color: var(--redColor); +} + +.scheme\:dark .textfield, .textfield.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --focusColor: 210, 9%, 31%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/forms/textpad.css b/Sources/HTMLKitComponents/Resources/css/forms/textpad.css new file mode 100644 index 00000000..28f7f1f5 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/forms/textpad.css @@ -0,0 +1,93 @@ +/* + The rulesets for the textpad component. + + default: + block padding: 8px + inline padding: 16px + font size: + font weigth: normal + line height: 1.5 + foreground color: #000000 + border width: 1px + border color: #DFE3E7 + border radius: 0 + focus color: #0080FF + background color: #FFFFFF + + darkmode: + foreground color: #FFFFFF + border color: #484F56 + focus color: #0080FF + background color: #22262A + */ + +.textpad { + --paddingBlock: 0.5rem; + --paddingInline: 1.0rem; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --focusColor: 210, 100%, 50%; + --backgroundColor: 0, 0%, 100%; + + display: flex; + flex-direction: column; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); +} + +.textpad:focus-within { + border-color: hsla(var(--focusColor), 1.0); + outline: 0; + box-shadow: 0 0 0 0.125rem hsla(var(--focusColor), 0.1); +} + +.textpad-toolbar { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + list-style: none; +} + +.textpad-toolbar > .toolbar-tool { + width: 1.25rem; + height: 1.25rem; + color: hsla(var(--foregroundColor), 1.0); + text-align: center; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.textpad-content { + display: block; + inline-size: 100%; + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + border: 0; + outline: 0; + background-color: transparent; + resize: none; +} + +.scheme\:dark .textpad, .textpad.scheme\:dark { + --foregroundColor: 0, 0%, 100%; + --borderColor: 210, 9%, 31%; + --focusColor: 210, 100%, 50%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/globals.css b/Sources/HTMLKitComponents/Resources/css/globals.css new file mode 100644 index 00000000..73f10253 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/globals.css @@ -0,0 +1,567 @@ +:root { + + /* + The variables for the typography. + + font family: system-ui...; (font-stack) + */ + + --fontFamily: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + + /* + The variables for the colors. + + black color: #000000 + white color: #FFFFFF + blue color: #4098D7 + brown color: #BF7140 + cyan color: #38BEC9 + green color: #57AE5B + indigo color: #647ACB + mint color: #91E697 + pink color: #DA4A91 + purple color: #724BB7 + red color: #D64545 + teal color: #3EBD93 + orange color: #E67635 + yellow color: #F7D070 + gray color: #9E9E9E + silver color: #F7F7F7 + primary color: #007FFF + secondary color: #132F4E + */ + + --redColor: 0, 64%, 55%; + --orangeColor: 22, 78%, 55%; + --yellowColor: 43, 89%, 70%; + --greenColor: 123, 35%, 51%; + --mintColor: 124, 63%, 74%; + --tealColor: 160, 51%, 49%; + --cyanColor: 185, 57%, 50%; + --blueColor: 205, 65%, 55%; + --indigoColor: 227, 50%, 59%; + --purpleColor: 262, 43%, 51%; + --pinkColor: 330, 66%, 57%; + --brownColor: 23, 50%, 50%; + --silverColor: 0, 0%, 97%; + --grayColor: 0, 0%, 62%; + --blackColor: 0, 0%, 0%; + --whiteColor: 0, 0%, 100%; + --primaryColor: 210, 100%, 50%; + --secondaryColor: 211, 60%, 19%; +} + +/* + dark version + */ + +.scheme\:dark { + + color-scheme: dark; + + /* + The variables for the colors. + + black color: #000000 + white color: #FFFFFF + blue color: #4098D7 + brown color: #BF7140 + cyan color: #38BEC9 + green color: #57AE5B + indigo color: #647ACB + mint color: #91E697 + pink color: #DA4A91 + purple color: #724BB7 + red color: #D64545 + teal color: #3EBD93 + orange color: #E67635 + yellow color: #F7D070 + gray color: #9E9E9E + silver color: #F7F7F7 + primary color: #007FFF + secondary color: #132F4E + */ + + --redColor: 0, 74%, 45%; + --orangeColor: 22, 88%, 45%; + --yellowColor: 3, 99%, 60%; + --greenColor: 123, 45%, 41%; + --mintColor: 124, 73%, 64%; + --tealColor: 160, 61%, 39%,; + --cyanColor: 185, 67%, 40%; + --blueColor: 205, 75%, 45%; + --indigoColor: 227, 60%, 49%; + --purpleColor: 262, 53%, 41%; + --pinkColor: 330, 76%, 47%; + --brownColor: 23, 60%, 40%,; + --silverColor: 240, 4%, 85%; + --grayColor: 25, 4%, 47%; + --blackColor: 30, 7%, 23%; + --whiteColor: 0, 0%, 100%; + --primaryColor: 210, 100%, 50%; + --secondaryColor: 211, 60%, 19%; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre, +body { + margin: 0; +} + +ol, +ul { + margin: 0; + padding: 0; +} + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle; +} + +img, +video { + max-width: 100%; + height: auto; +} + +@media (prefers-reduced-motion: no-preference) { + + html:focus-within { + scroll-behavior: smooth; + } +} + +/* + z-index + */ + +.zindex\:1 { + position: relative; + z-index: 1 !important; +} + +.zindex\:2 { + position: relative; + z-index: 2 !important; +} + +.zindex\:3 { + position: relative; + z-index: 3 !important; +} + +.zindex\:4 { + position: relative; + z-index: 4 !important; +} + +.zindex\:5 { + position: relative; + z-index: 5 !important; +} + +/* + states + */ + +.state\:active { + --borderColor: var(--primaryColor) !important; + --backgroundColor: var(--primaryColor) !important; +} + +.state\:disabled { + pointer-events: none !important; +} + +.state\:hidden { + display: none !important; +} + +.state\:visible { + display: block !important; +} + +/* + foreground + */ + +.color\:black { + --foregroundColor: var(--blackColor) !important; +} + +.color\:blue { + --foregroundColor: var(--blueColor) !important; +} + +.color\:brown { + --foregroundColor: var(--brownColor) !important; +} + +.color\:transparent { + --foregroundColor: transparent !important; +} + +.color\:cyan { + --foregroundColor: var(--cyanColor) !important; +} + +.color\:gray { + --foregroundColor: var(--grayColor) !important; +} + +.color\:green { + --foregroundColor: var(--greenColor) !important; +} + +.color\:indigo { + --foregroundColor: var(--indigoColor) !important; +} + +.color\:mint { + --foregroundColor: var(--mintColor) !important; +} + +.color\:orange { + --foregroundColor: var(--orangeColor) !important; +} + +.color\:pink { + --foregroundColor: var(--pinkColor) !important; +} + +.color\:purple { + --foregroundColor: var(--purpleColor) !important; +} + +.color\:red { + --foregroundColor: var(--redColor) !important; +} + +.color\:teal { + --foregroundColor: var(--tealColor) !important; +} + +.color\:white { + --foregroundColor: var(--whiteColor) !important; +} + +.color\:yellow { + --foregroundColor: var(--yellowColor) !important; +} + +.color\:silver { + --foregroundColor: var(--silverColor) !important; +} + +.color\:primary { + --foregroundColor: var(--primaryColor) !important; +} + +.color\:secondary { + --foregroundColor: var(--secondaryColor) !important; +} + + +/* + borders + */ + +.border\:black { + --borderColor: var(--blackColor) !important; +} + +.border\:blue { + --borderColor: var(--blueColor) !important; +} + +.border\:brown { + --borderWidth: 1px !important; + --borderColor: var(--brownColor) !important; +} + +.border\:cyan { + --borderWidth: 1px !important; + --borderColor: var(--cyanColor) !important; +} + +.border\:gray { + --borderWidth: 1px !important; + --borderColor: var(--grayColor) !important; +} + +.border\:green { + --borderWidth: 1px !important; + --borderColor: var(--greenColor) !important; +} + +.border\:indigo { + --borderWidth: 1px !important; + --borderColor: var(--indigoColor) !important; +} + +.border\:mint { + --borderWidth: 1px !important; + --borderColor: var(--mintColor) !important; +} + +.border\:orange { + --borderWidth: 1px !important; + --borderColor: var(--orangeColor) !important; +} + +.border\:pink { + --borderWidth: 1px !important; + --borderColor: var(--pinkColor) !important; +} + +.border\:purple { + --borderWidth: 1px !important; + --borderColor: var(--purpleColor) !important; +} + +.border\:red { + --borderWidth: 1px !important; + --borderColor: var(--redColor) !important; +} + +.border\:teal { + --borderWidth: 1px !important; + --borderColor: var(--tealColor) !important; +} + +.border\:white { + --borderWidth: 1px !important; + --borderColor: var(--whiteColor) !important; +} + +.border\:yellow { + --borderWidth: 1px !important; + --borderColor: var(--yellowColor) !important; +} + +.border\:silver { + --borderWidth: 1px !important; + --borderColor: var(--silverColor) !important; +} + +.border\:primary { + --borderWidth: 1px !important; + --borderColor: var(--primaryColor) !important; +} + +.border\:secondary { + --borderWidth: 1px !important; + --borderColor: var(--secondaryColor) !important; +} + +/* + backgrounds + */ + +.background\:black { + --backgroundColor: var(--blackColor) !important; +} + +.background\:blue { + --backgroundColor: var(--blueColor) !important; +} + +.background\:brown { + --backgroundColor: var(--brownColor) !important; +} + +.background\:cyan { + --backgroundColor: var(--cyanColor) !important; +} + +.background\:gray { + --backgroundColor: var(--grayColor) !important; +} + +.background\:green { + --backgroundColor: var(--greenColor) !important; +} + +.background\:indigo { + --backgroundColor: var(--indigoColor) !important; +} + +.background\:mint { + --backgroundColor: var(--mintColor) !important; +} + +.background\:orange { + --backgroundColor: var(--orangeColor) !important; +} + +.background\:pink { + --backgroundColor: var(--pinkColor) !important; +} + +.background\:purple { + --backgroundColor: var(--purpleColor) !important; +} + +.background\:red { + --backgroundColor: var(--redColor) !important; +} + +.background\:teal { + --backgroundColor: var(--tealColor) !important; +} + +.background\:white { + --backgroundColor: var(--whiteColor) !important; +} + +.background\:yellow { + --backgroundColor: var(--yellowColor) !important; +} + +.background\:silver { + --backgroundColor: var(--silverColor) !important; +} + +.background\:primary { + --backgroundColor: var(--primaryColor) !important; +} + +.background\:secondary { + --backgroundColor: var(--secondaryColor) !important; +} + +/* + focus + */ + +.focus\:black { + --focusColor: var(--blackColor) !important; +} + +.focus\:blue { + --focusColor: var(--blueColor) !important; +} + +.focus\:brown { + --focusColor: var(--brownColor) !important; +} + +.focus\:cyan { + --focusColor: var(--cyanColor) !important; +} + +.focus\:gray { + --focusColor: var(--grayColor) !important; +} + +.focus\:green { + --focusColor: var(--greenColor) !important; +} + +.focus\:indigo { + --focusColor: var(--indigoColor) !important; +} + +.focus\:mint { + --focusColor: var(--mintColor) !important; +} + +.focus\:orange { + --focusColor: var(--orangeColor) !important; +} + +.focus\:pink { + --focusColor: var(--pinkColor) !important; +} + +.focus\:purple { + --focusColor: var(--purpleColor) !important; +} + +.focus\:red { + --focusColor: var(--redColor) !important; +} + +.focus\:teal { + --focusColor: var(--tealColor) !important; +} + +.focus\:white { + --focusColor: var(--whiteColor) !important; +} + +.focus\:yellow { + --focusColor: var(--yellowColor) !important; +} + +.focus\:silver { + --focusColor: var(--silverColor) !important; +} + +.focus\:primary { + --focusColor: var(--primaryColor) !important; +} + +.focus\:secondary { + --focusColor: var(--secondaryColor) !important; +} + +/* + padding + */ + +.padding\:large { + --paddingInline: 2.25rem !important; + --paddingBlock: 2.25rem !important; +} + +.padding\:medium { + --paddingInline: 1.5rem !important; + --paddingBlock: 1.5rem !important; +} + +.padding\:small { + --paddingInline: 1rem !important; + --paddingBlock: 1rem !important; +} + +/* + shape + */ + +.shape\:smallrounded { + --borderRadius: 0.313rem !important; +} + +.shape\:largerounded { + --borderRadius: 0.625rem !important; +} + +.shape\:fullrounded { + --borderRadius: 1.563rem !important; +} diff --git a/Sources/HTMLKitComponents/Resources/css/group.css b/Sources/HTMLKitComponents/Resources/css/group.css deleted file mode 100644 index a7504571..00000000 --- a/Sources/HTMLKitComponents/Resources/css/group.css +++ /dev/null @@ -1,107 +0,0 @@ -/** - group component - */ - -.group { - display: inline-flex; - flex-direction: row; - align-items: center; -} - -.group > .button { - border-radius: 0; -} - -.group > .button:first-child { - border-top-left-radius: 5px; - border-bottom-left-radius: 5px; -} - -.group > .button:last-child { - border-top-right-radius: 5px; - border-bottom-right-radius: 5px; -} - -.group > .symbol + * { - margin-left: 15px; -} - -.group > * + .symbol { - margin-left: 15px; -} - -.group.color\:black { - color: var(--blackColor); -} - -.group.color\:blue { - color: var(--blueColor); -} - -.group.color\:brown { - color: var(--brownColor); -} - -.group.color\:cyan { - color: var(--cyanColor); -} - -.group.color\:gray { - color: var(--grayColor); -} - -.group.color\:green { - color: var(--greenColor); -} - -.group.color\:indigo { - color: var(--indigoColor); -} - -.group.color\:mint { - color: var(--mintColor); -} - -.group.color\:orange { - color: var(--orangeColor); -} - -.group.color\:pink { - color: var(--pinkColor); -} - -.group.color\:purple { - color: var(--purpleColor); -} - -.group.color\:red { - color: var(--redColor); -} - -.group.color\:teal { - color: var(--tealColor); -} - -.group.color\:white { - color: var(--whiteColor); -} - -.group.color\:yellow { - color: var(--yellowColor); -} - -.group.color\:silver { - color: var(--silverColor); -} - -.group.color\:highlight { - color: var(--highlightColor); -} - -.group.color\:primary { - color: var(--primaryColor); -} - -.group.color\:secondary { - color: var(--secondaryColor); -} diff --git a/Sources/HTMLKitComponents/Resources/css/helpers/group.css b/Sources/HTMLKitComponents/Resources/css/helpers/group.css new file mode 100644 index 00000000..2afb4018 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/helpers/group.css @@ -0,0 +1,9 @@ +/* + The stylesheet for the grouping component. + */ + +.group { + display: inline-flex; + flex-direction: row; + align-items: center; +} diff --git a/Sources/HTMLKitComponents/Resources/css/image.css b/Sources/HTMLKitComponents/Resources/css/image.css deleted file mode 100644 index 3db3d294..00000000 --- a/Sources/HTMLKitComponents/Resources/css/image.css +++ /dev/null @@ -1,74 +0,0 @@ -/** - image component - */ - -.image { - position: relative; - width: 100%; - height: 100%; - display: inline-block; - overflow: hidden; - border-width: 1px; - border-style: solid; - border-color: transparent; -} - -.image img { - width: 100%; - height: 100%; -} - -.image.fit\:contain img { - object-fit:contain; -} - -.image.fit\:cover img { - object-fit:cover; -} - -.image.fit\:fill img { - object-fit:fill; -} - -.image.fit\:scaledown img { - object-fit:scale-down; -} - -.image.fit\:none img { - object-fit:none; -} - -.image.opacity\:transparent { - opacity: 0; -} - -.image.opacity\:intransparent { - opacity: 1; -} - -.image.scale\:small { - width: 35px; - height: 35px; -} - -.image.scale\:medium { - width: 55px; - height: 55px; -} - -.image.scale\:large { - width: 100px; - height: 100px; -} - -.image.shape\:smallrounded { - border-radius: 5px; -} - -.image.shape\:largerounded { - border-radius: 10px; -} - -.image.shape\:circle { - border-radius: 50%; -} diff --git a/Sources/HTMLKitComponents/Resources/css/grid.css b/Sources/HTMLKitComponents/Resources/css/layout/grid.css similarity index 88% rename from Sources/HTMLKitComponents/Resources/css/grid.css rename to Sources/HTMLKitComponents/Resources/css/layout/grid.css index a6e59705..6248b52a 100644 --- a/Sources/HTMLKitComponents/Resources/css/grid.css +++ b/Sources/HTMLKitComponents/Resources/css/layout/grid.css @@ -1,18 +1,12 @@ -/** - grid component +/* + The stylesheet for the grid component. */ .grid { display: grid; - gap: var(--gridGapSize); list-style-type: none; } -.grid-item { - - overflow: hidden; -} - .grid.ratio\:50 { grid-template-columns: repeat(2, 1fr); } @@ -32,3 +26,7 @@ .grid.ratio\:15 { grid-template-columns: repeat(6, 1fr); } + +.grid-item { + overflow: hidden; +} diff --git a/Sources/HTMLKitComponents/Resources/css/layout/list.css b/Sources/HTMLKitComponents/Resources/css/layout/list.css new file mode 100644 index 00000000..fd66a703 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/layout/list.css @@ -0,0 +1,22 @@ +/* + The rulesets for the list component. + */ + +.list { + display: flex; + list-style-type: none; +} + +.list.direction\:horizontal { + flex-direction: row; + align-items: center; + justify-content: center; +} + +.list.direction\:vertical { + flex-direction: column; +} + +.list-row { + overflow: hidden; +} diff --git a/Sources/HTMLKitComponents/Resources/css/stack.css b/Sources/HTMLKitComponents/Resources/css/layout/stack.css similarity index 64% rename from Sources/HTMLKitComponents/Resources/css/stack.css rename to Sources/HTMLKitComponents/Resources/css/layout/stack.css index 51a82e86..304170d9 100644 --- a/Sources/HTMLKitComponents/Resources/css/stack.css +++ b/Sources/HTMLKitComponents/Resources/css/layout/stack.css @@ -1,11 +1,10 @@ -/** - stack component +/* + The rulsets for the horizontal stack component. */ .hstack { position: relative; - width: 100%; - height: 100%; + inline-size: 100%; display: flex; flex-direction: row; flex-wrap: nowrap; @@ -39,10 +38,13 @@ justify-content: space-evenly; } +/* + The rulesets for the vertical stack component. + */ + .vstack { position: relative; - width: 100%; - height: 100%; + inline-size: 100%; display: flex; flex-direction: column; } @@ -59,79 +61,84 @@ justify-content: flex-end; } +/* + The rulesets for the vertical stack component. + */ + .zstack { - position: relative; - width: 100%; - height: 100%; - display: flex; + position: absolute; + inset: 0; z-index: 1; + display: flex; + inline-size: 100%; } +/* + The rulesets for the stack column component. + */ + .stack-column { - padding-top: 5px; - padding-right: 5px; - padding-bottom: 5px; - padding-left: 5px; + position: relative; } .stack-column.size\:1 { flex: 0 0 auto; - width: 8.3333333333%; + inline-size: 8.3333333333%; } .stack-column.size\:2 { flex: 0 0 auto; - width: 16.6666666667%; + inline-size: 16.6666666667%; } .stack-column.size\:3 { flex: 0 0 auto; - width: 25%; + inline-size: 25%; } .stack-column.size\:4 { flex: 0 0 auto; - width: 33.3333333333%; + inline-size: 33.3333333333%; } .stack-column.size\:5 { flex: 0 0 auto; - width: 41.6666666667%; + inline-size: 41.6666666667%; } .stack-column.size\:6 { flex: 0 0 auto; - width: 50%; + inline-size: 50%; } .stack-column.size\:7 { flex: 0 0 auto; - width: 58.3333333333%; + inline-size: 58.3333333333%; } .stack-column.size\:8 { flex: 0 0 auto; - width: 66.6666666667%; + inline-size: 66.6666666667%; } .stack-column.size\:9 { flex: 0 0 auto; - width: 75%; + inline-size: 75%; } .stack-column.size\:10 { flex: 0 0 auto; - width: 83.3333333333%; + inline-size: 83.3333333333%; } .stack-column.size\:11 { flex: 0 0 auto; - width: 91.6666666667%; + inline-size: 91.6666666667%; } .stack-column.size\:12 { flex: 0 0 auto; - width: 100%; + inline-size: 100%; } .stack-column.alignment\:center { @@ -147,45 +154,45 @@ } .stack-column.offset\:1 { - margin-left: 8.3333333333%; + margin-inline-start: 8.3333333333%; } .stack-column.offset\:2 { - margin-left: 16.6666666667%; + margin-inline-start: 16.6666666667%; } .stack-column.offset\:3 { - margin-left: 25%; + margin-inline-start: 25%; } .stack-column.offset\:4 { - margin-left: 33.3333333333%; + margin-inline-start: 33.3333333333%; } .stack-column.offset\:5 { - margin-left: 41.6666666667%; + margin-inline-start: 41.6666666667%; } .stack-column.offset\:6 { - margin-left: 50%; + margin-inline-start: 50%; } .stack-column.offset\:7 { - margin-left: 58.3333333333%; + margin-inline-start: 58.3333333333%; } .stack-column.offset\:8 { - margin-left: 66.6666666667% + margin-inline-start: 66.6666666667%; } .stack-column.offset\:9 { - margin-left: 75%; + margin-inline-start: 75%; } .stack-column.offset\:10 { - margin-left: 83.3333333333%; + margin-inline-start: 83.3333333333%; } .stack-column.offset\:11 { - margin-left: 91.6666666667%; + margin-inline-start: 91.6666666667%; } diff --git a/Sources/HTMLKitComponents/Resources/css/link.css b/Sources/HTMLKitComponents/Resources/css/link.css deleted file mode 100644 index 5974f30a..00000000 --- a/Sources/HTMLKitComponents/Resources/css/link.css +++ /dev/null @@ -1,221 +0,0 @@ -/** - link component - */ - -.link { - font-family: var(--textFontFamily); - font-size: var(--textFontSize); - font-weight: var(--textFontHeight); - line-height: var(--textLineHeight); - color: var(--textBaseColor); - text-decoration: none; -} - -.link.size\:small { - font-size: var(--smallFontSize); - line-height: var(--smallLineHeight); -} - -.link.size\:medium { - font-size: var(--mediumFontSize); - line-height: var(--mediumLineHeight); -} - -.link.size\:large { - font-size: var(--largeFontSize); - line-height: var(--largeLineHeight); -} - -.link.weight\:thin { - font-weight: 100; -} - -.link.weight\:ultralight { - font-weight: 200; -} - -.link.weight\:light { - font-weight: 300; -} - -.link.weight\:regular { - font-weight: 400; -} - -.link.weight\:semibold { - font-weight: 500; -} - -.link.weight\:semibold { - font-weight: 600; -} - -.link.weight\:bold { - font-weight: 700; -} - -.link.weight\:heavy { - font-weight: 900; -} - -.link.weight\:black { - font-weight: 950; -} - -.link.color\:black { - color: var(--blackColor); -} - -.link.color\:blue { - color: var(--blueColor); -} - -.link.color\:brown { - color: var(--brownColor); -} - -.link.color\:cyan { - color: var(--cyanColor); -} - -.link.color\:gray { - color: var(--grayColor); -} - -.link.color\:green { - color: var(--greenColor); -} - -.link.color\:indigo { - color: var(--indigoColor); -} - -.link.color\:mint { - color: var(--mintColor); -} - -.link.color\:orange { - color: var(--orangeColor); -} - -.link.color\:pink { - color: var(--pinkColor); -} - -.link.color\:purple { - color: var(--purpleColor); -} - -.link.color\:red { - color: var(--redColor); -} - -.link.color\:teal { - color: var(--tealColor); -} - -.link.color\:white { - color: var(--whiteColor); -} - -.link.color\:yellow { - color: var(--yellowColor); -} - -.link.color\:silver { - color: var(--silverColor); -} - -.link.color\:primary { - color: var(--primaryColor); -} - -.link.color\:secondary { - color: var(--secondaryColor); -} - -.link.transformation\:uppercase { - text-transform: uppercase; -} - -.link.transformation\:lowercase { - text-transform: lowercase; -} - -.link.transformation\:capitalize { - text-transform: capitalize; -} - -.link.transformation\:underline { - text-decoration: underline; -} - -.link.transformation\:overline { - text-decoration: overline; -} - -.link.transformation\:strikethrough { - text-decoration: line-through; -} - -.link.transformation\:none { - text-decoration: none; -} - -.link.style\:italic { - font-style: italic; -} - -.link.style\:oblique { - font-style: oblique; -} - -.link.style\:title { - font-size: 2.441rem; - font-weight: 400; - line-height: 1.75; -} - -.link.style\:headline { - font-size: 1.953rem; - font-weight: 400; - line-height: 1.75; -} - -.link.style\:subheadline { - font-size: 1.25rem; - font-weight: 400; - line-height: 1.75; -} - -.link.style\:body { - font-size: 1rem; - font-weight: 400; - line-height: 1.75; -} - -.link.style\:callout { - font-size: 1rem; - font-weight: 400; - line-height: 1.75; -} - -.link.style\:caption { - font-size: 0.8rem; - font-weight: 400; - line-height: 1.75; -} - -.link.style\:footnote { - font-size: 0.8rem; - font-weight: 400; - line-height: 1.75 - color: var(--grayColor); -} - -.link.style\:code { - font-family: monospace; - font-size: 1rem; - font-weight: 400; - line-height: 1.75; -} diff --git a/Sources/HTMLKitComponents/Resources/css/list.css b/Sources/HTMLKitComponents/Resources/css/list.css deleted file mode 100644 index 344488cc..00000000 --- a/Sources/HTMLKitComponents/Resources/css/list.css +++ /dev/null @@ -1,46 +0,0 @@ -/** - list component - */ - -.list { - display: flex; - gap: var(--listGapSize); - list-style-type: none; -} - -.list.direction\:horizontal { - flex-direction: row; - align-items: center; - justify-content: center; - margin-right: 15px; - margin-left: 15px; -} - -.list.direction\:vertical { - flex-direction: column; - margin-top: 15px; - margin-bottom: 15px; -} - -.list.style\:grouped { - border-width: var(--listBorderWidth); - border-style: solid; - border-radius: var(--listBorderRadius); - border-color: var(--listBorderColor); - gap: normal; -} - -.list.style\:grouped > .list-row { - border-bottom: 1px solid var(--listBorderColor); -} - -.list.style\:grouped > .list-row:last-child { - border-bottom: none; -} - -.list-row { - padding-top: var(--listRowPaddingY); - padding-right: var(--listRowPaddingX); - padding-bottom: var(--listRowPaddingY); - padding-left: var(--listRowPaddingX); -} diff --git a/Sources/HTMLKitComponents/Resources/css/modal.css b/Sources/HTMLKitComponents/Resources/css/modal.css deleted file mode 100644 index 68c8aabe..00000000 --- a/Sources/HTMLKitComponents/Resources/css/modal.css +++ /dev/null @@ -1,18 +0,0 @@ -/** - modal component - */ - -.modal { - - width: 45%; - margin: auto; - padding-top: var(--modalPaddingY); - padding-right: var(--modalPaddingX); - padding-bottom: var(--modalPaddingY); - padding-left: var(--modalPaddingX); - border-width: var(--modalBorderWidth); - border-style: solid; - border-radius: var(--modalBorderRadius); - border-color: var(--modalBorderColor); - background-color: var(--modalBackgroundColor); -} diff --git a/Sources/HTMLKitComponents/Resources/css/roots.css b/Sources/HTMLKitComponents/Resources/css/roots.css deleted file mode 100644 index 299469a0..00000000 --- a/Sources/HTMLKitComponents/Resources/css/roots.css +++ /dev/null @@ -1,212 +0,0 @@ -:root { - - /** - neutral colors - - - black: #000000 - - white: #FFFFFF - */ - - --blackColor: hsla(0, 0%, 0%, 100%); - --whiteColor: hsla(0, 0%, 100%, 100%); - - /** - supporting colors - - - blue: #4098D7 - - brown: #BF7140 - - cyan: #38BEC9 - - green: #57AE5B - - indigo: #647ACB - - mint: #91E697 - - pink: #DA4A91 - - purple: #724BB7 - - red: #D64545 - - teal: #3EBD93 - - orange: #E67635 - - yellow: #F7D070 - - gray: #9E9E9E - - silver: #F7F7F7 - */ - - --blueColor: hsla(205, 65%, 55%, 100%); - --brownColor: hsla(23, 50%, 50%, 100%); - --cyanColor: hsla(185, 57%, 50%, 100%); - --greenColor: hsla(123, 35%, 51%, 100%); - --indigoColor: hsla(227, 50%, 59%, 100%); - --mintColor: hsla(124, 63%, 74%, 100%); - --pinkColor: hsla(330, 66%, 57%, 100%); - --purpleColor: hsla(262, 43%, 51%, 100%); - --redColor: hsla(0, 64%, 55%, 100%); - --tealColor: hsla(160, 51%, 49%, 100%); - --orangeColor: hsla(22, 78%, 55%, 100%); - --yellowColor: hsla(43, 89%, 70%, 100%); - --grayColor: hsla(0, 0%, 62%, 100%); - --silverColor: hsla(0, 0%, 97%, 100%); - - /** - custom colors - - primary: #007FFF - - secondary: #132F4E - **/ - - --primaryColor: hsla(210, 100%, 50%, 100%); - --secondaryColor: hsla(211, 60%, 19%, 100%); - - /** - toggle variables - - background #E6E6E6 - - active background: #77BF40 - - toggle border: #DFE3E7 - - toggle focus: #DFE3E7 - - slider background: #FFFFFF - - slider border: #FFFFFF - */ - - --toggleBackgroundColor: hsla(0, 0%, 90%, 100%); - --toggleActiveBackgroundColor: hsla(94, 50%, 50%, 100%); - --toggleBorderColor: hsla(210, 14%, 89%, 100%); - --toggleFocusColor: hsla(210, 14%, 89%, 40%); - --sliderBackgroundColor: hsla(0, 0%, 100%, 100%); - --sliderBorderColor: hsla(0, 0%, 100%, 100%); - - /** - text variables - */ - - --textFontFamily: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --textFontSize: 100%; - --textFontWeight: 400; - --textLineHeight: 1.1em; - --smallFontSize: 0.8em; - --smallFontWeight: 600; - --smallLineHeight: 1.6em; - --normalFontSize: 1.4em; - --normalFontWeight: 600; - --normalLineHeight: 1.8em; - --mediumFontSize: 1.6em; - --mediumFontWeight: 600; - --mediumLineHeight: 2.0em; - --largeFontSize: 2.3em; - --largeFontWeight: 900; - --largeLineHeight: 2.7em; - - /** - snippet variables - */ - - --snippetFontSize: 12px; - --snippetLineHeight: 20px; - --snippetBackgroundColor: #FFFFFF; - --snippetFontColor: #000000; - --lineNumberFontColor: #000000; - - /** - list variables - */ - - --listRowPaddingY: 0px; - --listRowPaddingX: 15px; - --listGapSize: 15px; - --listBorderColor: hsla(210, 14%, 89%, 100%); - --listBorderWidth: 1px; - --listBorderRadius: 5px; - - /** - input variables - */ - - --inputPaddingY: 12px; - --inputPaddingX: 16px; - --inputTextColor: var(--blackColor); - --inputBackgroundColor: var(--whiteColor); - --inputBorderColor: hsla(210, 14%, 89%, 100%); - --inputBorderWidth: 1px; - --inputBorderRadius: 5px; - --inputFocusColor: hsla(210, 14%, 89%, 40%); - - /** - slider variables - */ - - --sliderBackgroundColor: var(--whiteColor); - --sliderBorderColor: var(--whiteColor); - --thumbBorderColor: var(--whiteColor); - --thumbBackgroundColor: var(--whiteColor); - - /** - divider variables - */ - - --dividerColor: var(--grayColor); - - /** - button variables - */ - - --buttonPaddingY: 12px; - --buttonPaddingX: 16px; - --buttonBorderWidth: 1px; - --buttonBorderRadius: 5px; - --buttonFontSize: 14px; - --buttonFontWeight: normal; - --buttonLineHeight: 1.2rem; - - - /** - modal variables - */ - - --modalPaddingY: 12px; - --modalPaddingX: 16px; - --modalBorderWidth: 1px; - --modalBorderRadius: 5px; - --modalBorderColor: var(--grayColor); - --modalBackgroundColor: var(--whiteColor); - - /** - grid variables - */ - - --gridGapSize: 15px; - - /** - card variables - */ - - --cardPaddingY: 12px; - --cardPaddingX: 16px; - --cardBorderWidth: 1px; - --cardBorderRadius: 5px; - --cardBorderColor: var(--grayColor); - --cardBackgroundColor: var(--whiteColor); - --cardHeaderHight: 200px; - --cardHeaderBackgroundColor: var(--whiteColor); - - /** - dropdown variables - */ - - --dropdownBorderRadius: 5px; - --dropdownBorderColor: var(--grayColor); - --dropdownHoverColor: var(--silverColor); - --dropdownBackgroundColor: var(--whiteColor); - - /** - scrollview variables - */ - - --scrollViewGapSize: 25px; - --scrollViewBackgroundColor: transparent; - - /** - carousel variables - */ - --carouselBorderWidth: 1px; - --carouselBorderColor: #000000; - --carouselBorderRadius: 10px; - --carouselBackgroundColor: transparent; - --indicatorBorderRadius: 5px; - --indicatorBackgroundColor: transparent; - --indicatorBorderColor: #000000; -} diff --git a/Sources/HTMLKitComponents/Resources/css/snippet.css b/Sources/HTMLKitComponents/Resources/css/snippet.css deleted file mode 100644 index ee9cc4d7..00000000 --- a/Sources/HTMLKitComponents/Resources/css/snippet.css +++ /dev/null @@ -1,38 +0,0 @@ -/** - snippet component - */ - -.snippet { - position: relative; - width: 100%; - height: auto; - display: block; - counter-reset: line; - background-color: var(--snippetBackgroundColor); - overflow-x: auto; -} - -.snippet > p { - display: block; - font-size: var(--snippetFontSize); - line-height: var(--snippetLineHeight); - color: var(--snippetFontColor); - word-wrap: normal; - white-space: pre; - counter-increment: line; -} - -.snippet > p::before { - position: relative; - width: 1%; - min-width: 15px; - display: inline-block; - margin-right: 15px; - padding-right: 15px; - padding-left: 15px; - white-space: nowrap; - text-align: right; - content: counter(line); - color: var(--lineNumberFontColor); - -webkit-user-select: none; -} diff --git a/Sources/HTMLKitComponents/Resources/css/symbol.css b/Sources/HTMLKitComponents/Resources/css/symbol.css deleted file mode 100644 index 6444ae7e..00000000 --- a/Sources/HTMLKitComponents/Resources/css/symbol.css +++ /dev/null @@ -1,100 +0,0 @@ -/** - symbol component - */ - -.symbol { - display: inline-block; - width: 1.25em; - height: 1em; -} - -.symbol.size\:small { - font-size: var(--smallFontSize); - line-height: var(--smallLineHeight); -} - -.symbol.size\:medium { - font-size: var(--mediumFontSize); - line-height: var(--mediumLineHeight); -} - -.symbol.size\:large { - font-size: var(--largeFontSize); - line-height: var(--largeLineHeight); -} - -.symbol.color\:black { - color: var(--blackColor); -} - -.symbol.color\:blue { - color: var(--blueColor); -} - -.symbol.color\:brown { - color: var(--brownColor); -} - -.symbol.color\:cyan { - color: var(--cyanColor); -} - -.symbol.color\:gray { - color: var(--grayColor); -} - -.symbol.color\:green { - color: var(--greenColor); -} - -.symbol.color\:indigo { - color: var(--indigoColor); -} - -.symbol.color\:mint { - color: var(--mintColor); -} - -.symbol.color\:orange { - color: var(--orangeColor); -} - -.symbol.color\:pink { - color: var(--pinkColor); -} - -.symbol.color\:purple { - color: var(--purpleColor); -} - -.symbol.color\:red { - color: var(--redColor); -} - -.symbol.color\:teal { - color: var(--tealColor); -} - -.symbol.color\:white { - color: var(--whiteColor); -} - -.symbol.color\:yellow { - color: var(--yellowColor); -} - -.symbol.color\:silver { - color: var(--silverColor); -} - -.symbol.color\:highlight { - color: var(--highlightColor); -} - -.symbol.color\:primary { - color: var(--primaryColor); -} - -.symbol.color\:secondary { - color: var(--secondaryColor); -} diff --git a/Sources/HTMLKitComponents/Resources/css/text.css b/Sources/HTMLKitComponents/Resources/css/text.css deleted file mode 100644 index 2865ff0f..00000000 --- a/Sources/HTMLKitComponents/Resources/css/text.css +++ /dev/null @@ -1,219 +0,0 @@ -/** - text component - */ - -.text { - font-family: var(--textFontFamily); - font-size: var(--textFontSize); - font-weight: var(--textFontWeight); - line-height: var(--textLineHeight); -} - -.text.size\:small { - font-size: var(--smallFontSize); - line-height: var(--smallLineHeight); -} - -.text.size\:medium { - font-size: var(--mediumFontSize); - line-height: var(--mediumLineHeight); -} - -.text.size\:large { - font-size: var(--largeFontSize); - line-height: var(--largeLineHeight); -} - -.text.weight\:thin { - font-weight: 100; -} - -.text.weight\:ultralight { - font-weight: 200; -} - -.text.weight\:light { - font-weight: 300; -} - -.text.weight\:regular { - font-weight: 400; -} - -.text.weight\:medium { - font-weight: 500; -} - -.text.weight\:semibold { - font-weight: 600; -} - -.text.weight\:bold { - font-weight: 700; -} - -.text.weight\:heavy { - font-weight: 900; -} - -.text.weight\:black { - font-weight: 950; -} - -.text.color\:black { - color: var(--blackColor); -} - -.text.color\:blue { - color: var(--blueColor); -} - -.text.color\:brown { - color: var(--brownColor); -} - -.text.color\:cyan { - color: var(--cyanColor); -} - -.text.color\:gray { - color: var(--grayColor); -} - -.text.color\:green { - color: var(--greenColor); -} - -.text.color\:indigo { - color: var(--indigoColor); -} - -.text.color\:mint { - color: var(--mintColor); -} - -.text.color\:orange { - color: var(--orangeColor); -} - -.text.color\:pink { - color: var(--pinkColor); -} - -.text.color\:purple { - color: var(--purpleColor); -} - -.text.color\:red { - color: var(--redColor); -} - -.text.color\:teal { - color: var(--tealColor); -} - -.text.color\:white { - color: var(--whiteColor); -} - -.text.color\:yellow { - color: var(--yellowColor); -} - -.text.color\:silver { - color: var(--silverColor); -} - -.text.color\:primary { - color: var(--primaryColor); -} - -.text.color\:secondary { - color: var(--secondaryColor); -} - -.text.transformation\:uppercase { - text-transform: uppercase; -} - -.text.transformation\:lowercase { - text-transform: lowercase; -} - -.text.transformation\:capitalize { - text-transform: capitalize; -} - -.text.transformation\:underline { - text-decoration: underline; -} - -.text.transformation\:overline { - text-decoration: overline; -} - -.text.transformation\:strikethrough { - text-decoration: line-through; -} - -.text.transformation\:none { - text-decoration: none; -} - -.text.style\:italic { - font-style: italic; -} - -.text.style\:oblique { - font-style: oblique; -} - -.text.style\:title { - font-size: 2.441rem; - font-weight: 400; - line-height: 1.75; -} - -.text.style\:headline { - font-size: 1.953rem; - font-weight: 400; - line-height: 1.75; -} - -.text.style\:subheadline { - font-size: 1.25rem; - font-weight: 400; - line-height: 1.75; -} - -.text.style\:body { - font-size: 1rem; - font-weight: 400; - line-height: 1.75; -} - -.text.style\:callout { - font-size: 1rem; - font-weight: 400; - line-height: 1.75; -} - -.text.style\:caption { - font-size: 0.8rem; - font-weight: 400; - line-height: 1.75; -} - -.text.style\:footnote { - font-size: 0.8rem; - font-weight: 400; - line-height: 1.75 - color: var(--grayColor); -} - -.text.style\:code { - font-family: monospace; - font-size: 1rem; - font-weight: 400; - line-height: 1.75; -} diff --git a/Sources/HTMLKitComponents/Resources/css/textpad.css b/Sources/HTMLKitComponents/Resources/css/textpad.css deleted file mode 100644 index 0a70d84e..00000000 --- a/Sources/HTMLKitComponents/Resources/css/textpad.css +++ /dev/null @@ -1,72 +0,0 @@ -/** - textpad component - */ - -.textpad { - display: flex; - flex-direction: column; -} - -.textpad:focus-within { - border-radius: var(--inputBorderRadius); - box-shadow: 0 0 0 4px var(--inputFocusColor); -} - -.textpad:has(textarea:invalid) .textpad-toolbar { - border-color: var(--redColor); -} - -.textpad:has(textarea:invalid) .textpad-content { - border-color: var(--redColor); -} - -.textpad-toolbar { - display: flex; - flex-direction: row; - align-items: center; - gap: 20px; - padding-top: var(--inputPaddingY); - padding-right: var(--inputPaddingX); - padding-bottom: var(--inputPaddingY); - padding-left: var(--inputPaddingX); - border-top: var(--inputBorderWidth); - border-left: var(--inputBorderWidth); - border-right: var(--inputBorderWidth); - border-bottom: var(--inputBorderWidth); - border-style: solid; - border-color: var(--inputBorderColor); - border-top-left-radius: var(--inputBorderRadius); - border-top-right-radius: var(--inputBorderRadius); - list-style: none; -} - -.textpad-toolbar > .toolbar-tool { - width: 20px; - height: 20px; - text-align: center; - cursor: pointer; -} - -.textpad-content { - display: block; - width: 100%; - min-height: 35px; - resize: none; - padding-top: var(--inputPaddingY); - padding-right: var(--inputPaddingX); - padding-bottom: var(--inputPaddingY); - padding-left: var(--inputPaddingX); - border-top: 0; - border-left: var(--inputBorderWidth); - border-right: var(--inputBorderWidth); - border-bottom: var(--inputBorderWidth); - border-style: solid; - border-color: var(--inputBorderColor); - border-bottom-left-radius: var(--inputBorderRadius); - border-bottom-right-radius: var(--inputBorderRadius); - font-size: 1.0rem; - font-weight: 400; - color: var(--inputTextColor); - background-color: var(--inputBackgroundColor); - outline: 0; -} diff --git a/Sources/HTMLKitComponents/Resources/css/toggle.css b/Sources/HTMLKitComponents/Resources/css/toggle.css deleted file mode 100644 index d18373d3..00000000 --- a/Sources/HTMLKitComponents/Resources/css/toggle.css +++ /dev/null @@ -1,52 +0,0 @@ -/** - toggle component - */ - -.toggle { - position: relative; - display: inline-block; - width: 60px; - height: 30px; - border-width: 1px; - border-style: solid; - border-color: var(--toggleBorderColor); - border-radius: 31px; - overflow: hidden; - cursor: pointer; -} - -.toggle:focus { - outline: 0; - box-shadow: 0 0 0 4px var(--toggleFocusColor); -} - -.toggle > input { - display: none; -} - -.toggle-slider { - position: absolute; - width: 100%; - height: 100%; - background-color: var(--toggleBackgroundColor); -} - -.toggle-slider::before { - position: absolute; - content: ""; - width: 26px; - height: 26px; - border-width: 1px; - border-style: solid; - border-color: var(--sliderBorderColor); - border-radius: 50%; - background-color: var(--sliderBackgroundColor); -} - -.toggle > input:checked + .toggle-slider { - background-color: var(--toggleActiveBackgroundColor); -} - -.toggle > input:checked + .toggle-slider::before { - left: calc(100% - 26px); -} diff --git a/Sources/HTMLKitComponents/Resources/css/typography/link.css b/Sources/HTMLKitComponents/Resources/css/typography/link.css new file mode 100644 index 00000000..432f3230 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/typography/link.css @@ -0,0 +1,163 @@ +/* + The rulsets for the link component. + */ + +.link { + --fontSize: 1.0rem; + --lineHeight: 1.5; + --fontWeight: 400; + + font-family: var(--fontFamily); + font-size: var(--fontSize); + font-weight: var(--fontHeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + text-decoration: none; +} + +.link.size\:tiny { + --fontSize: 0.67rem; + --lineHeight: 1.25; +} + +.link.size\:small { + --fontSize: 0.83rem; + --lineHeight: 1.25; +} + +.link.size\:medium { + --fontSize: 1.17rem; + --lineHeight: 1.75; +} + +.link.size\:large { + --fontSize: 1.5rem; + --lineHeight: 2.0; +} + +.link.size\:extralarge { + --fontSize: 2rem; + --lineHeight: 2.25; +} + +.link.weight\:thin { + --fontWeight: 100; +} + +.link.weight\:ultralight { + --fontWeight: 200; +} + +.link.weight\:light { + --fontWeight: 300; +} + +.link.weight\:semibold { + --fontWeight: 500; +} + +.link.weight\:semibold { + --fontWeight: 600; +} + +.link.weight\:bold { + --fontWeight: 700; +} + +.link.weight\:heavy { + --fontWeight: 900; +} + +.link.weight\:black { + --fontWeight: 950; +} + +.link.transformation\:uppercase { + text-transform: uppercase; +} + +.link.transformation\:lowercase { + text-transform: lowercase; +} + +.link.transformation\:capitalize { + text-transform: capitalize; +} + +.link.transformation\:underline { + text-decoration: underline; +} + +.link.transformation\:overline { + text-decoration: overline; +} + +.link.transformation\:strikethrough { + text-decoration: line-through; +} + +.link.transformation\:none { + text-decoration: none; +} + +.link.style\:italic { + font-style: italic; +} + +.link.style\:oblique { + font-style: oblique; +} + +.link.style\:title { + --fontSize: 2.0rem; + --fontWeight: 400; + --lineHeight: 2.25; +} + +.link.style\:headline { + --fontSize: 1.5rem; + --fontWeight: 600; + --lineHeight: 2.0; +} + +.link.style\:subheadline { + --fontSize: 1.25rem; + --fontWeight: 400; + --lineHeight: 1.75; +} + +.link.style\:body { + --fontSize: 1rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.link.style\:callout { + --fontSize: 1rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.link.style\:caption { + --fontSize: 0.8rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.link.style\:footnote { + --fontSize: 0.8rem; + --fontWeight: 400; + --lineHeight: 1.75; + --foregroundColor: var(--grayColor) !important; +} + +.link.style\:code { + --fontFamily: monospace; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.scheme\:dark .link, .link.scheme\:dark { + --foregroundColor: 0, 0%, 100%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/typography/symbol.css b/Sources/HTMLKitComponents/Resources/css/typography/symbol.css new file mode 100644 index 00000000..a870562b --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/typography/symbol.css @@ -0,0 +1,48 @@ +/* + The stylesheet for the symbol component. + + default: + font size: 16px + line height: 1.5 + foreground color: #000000 + + darkmode: + foreground color: #FFFFFF + */ + +.symbol { + --fontSize: 1.0rem; + --lineHeight: 1.5; + + display: inline-block; + inline-size: 1.25rem; + block-size: 1.0rem; + fill: hsla(var(--foregroundColor), 1.0); + font-size: var(--fontSize); + line-height: var(--lineHeight); +} + +.symbol.size\:tiny { + --fontSize: 0.67rem; + --lineHeight: 1.25; +} + +.symbol.size\:small { + --fontSize: 0.83rem; + --lineHeight: 1.25; +} + +.symbol.size\:medium { + --fontSize: 1.17rem; + --lineHeight: 1.75; +} + +.symbol.size\:large { + --fontSize: 1.5rem; + --lineHeight: 2.0; +} + +.symbol.size\:extralarge { + --fontSize: 2rem; + --lineHeight: 2.25; +} diff --git a/Sources/HTMLKitComponents/Resources/css/typography/text.css b/Sources/HTMLKitComponents/Resources/css/typography/text.css new file mode 100644 index 00000000..cdb5099d --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/typography/text.css @@ -0,0 +1,166 @@ +/* + The rulesets for the text component. + + font size: 16px + line height: 1.5 + font weight: normal + */ + +.text { + --fontSize: 1.0rem; + --lineHeight: 1.5; + --fontWeight: 400; + + font-family: var(--fontFamily); + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); +} + +.text.size\:tiny { + --fontSize: 0.67rem; + --lineHeight: 1.25; +} + +.text.size\:small { + --fontSize: 0.83rem; + --lineHeight: 1.25; +} + +.text.size\:medium { + --fontSize: 1.17rem; + --lineHeight: 1.75; +} + +.text.size\:large { + --fontSize: 1.5rem; + --lineHeight: 2.0; +} + +.text.size\:extralarge { + --fontSize: 2rem; + --lineHeight: 2.25; +} + +.text.weight\:thin { + --fontWeight: 100; +} + +.text.weight\:ultralight { + --fontWeight: 200; +} + +.text.weight\:light { + --fontWeight: 300; +} + +.text.weight\:medium { + --fontWeight: 500; +} + +.text.weight\:semibold { + --fontWeight: 600; +} + +.text.weight\:bold { + --fontWeight: 700; +} + +.text.weight\:heavy { + --fontWeight: 900; +} + +.text.weight\:black { + --fontWeight: 950; +} + +.text.transformation\:uppercase { + text-transform: uppercase; +} + +.text.transformation\:lowercase { + text-transform: lowercase; +} + +.text.transformation\:capitalize { + text-transform: capitalize; +} + +.text.transformation\:underline { + text-decoration: underline; +} + +.text.transformation\:overline { + text-decoration: overline; +} + +.text.transformation\:strikethrough { + text-decoration: line-through; +} + +.text.transformation\:none { + text-decoration: none; +} + +.text.style\:italic { + font-style: italic; +} + +.text.style\:oblique { + font-style: oblique; +} + +.text.style\:title { + --fontSize: 2.0rem; + --fontWeight: 400; + --lineHeight: 2.25; +} + +.text.style\:headline { + --fontSize: 1.5rem; + --fontWeight: 600; + --lineHeight: 2.0; +} + +.text.style\:subheadline { + --fontSize: 1.25rem; + --fontWeight: 400; + --lineHeight: 1.75; +} + +.text.style\:body { + --fontSize: 1rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.text.style\:callout { + --fontSize: 1rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.text.style\:caption { + --fontSize: 0.8rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.text.style\:footnote { + --fontSize: 0.8rem; + --fontWeight: 400; + --lineHeight: 1.75; + --foregroundColor: var(--grayColor) !important; +} + +.text.style\:code { + --fontFamily: monospace; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; +} + +.scheme\:dark .text, .text.scheme\:dark { + --foregroundColor: 0, 0%, 100%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/utils.css b/Sources/HTMLKitComponents/Resources/css/utils.css deleted file mode 100644 index 5a6efefb..00000000 --- a/Sources/HTMLKitComponents/Resources/css/utils.css +++ /dev/null @@ -1,45 +0,0 @@ -/** - utils classes - */ - -.zindex\:1 { - position: relative; - z-index: 1; -} - -.zindex\:2 { - position: relative; - z-index: 2; -} - -.zindex\:3 { - position: relative; - z-index: 3; -} - -.zindex\:4 { - position: relative; - z-index: 4; -} - -.zindex\:5 { - position: relative; - z-index: 5; -} - -.state\:active { - border-color: var(--primaryColor) !important; - background-color: var(--primaryColor) !important; -} - -.state\:disabled { - pointer-events: none !important; -} - -.state\:hidden { - display: none !important; -} - -.state\:visible { - display: block !important; -} diff --git a/Sources/HTMLKitComponents/Resources/css/views/card.css b/Sources/HTMLKitComponents/Resources/css/views/card.css new file mode 100644 index 00000000..09ac51e7 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/views/card.css @@ -0,0 +1,51 @@ +/* + The stylesheet for the card component. + + default: + block padding: 12px + inline padding: 16px + border width: 1px; + border color: #DFE3E7 + border radius: 0 + background color: #FFFFFF + + darkmode: + border color: #484F56 + background color: #22262A + */ + +.card { + --paddingInline: 1.0rem; + --paddingBlock: 0.5rem; + --borderWidth: 1px; + --borderColor: 210, 14%, 89%; + --borderRadius: 0; + --backgroundColor: 0, 0%, 100%; + + position: relative; + display: flex; + flex-direction: column; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); +} + +.card .card-header { + border-start-start-radius: var(--borderRadius); + border-start-end-radius: var(--borderRadius); + overflow: hidden; +} + +.card .card-body { + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + border-end-start-radius: var(--borderRadius); + border-end-end-radius: var(--borderRadius); +} + +.scheme\:dark .card, .card.scheme\:dark { + --borderColor: 210, 9%, 31%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/views/carousel.css b/Sources/HTMLKitComponents/Resources/css/views/carousel.css new file mode 100644 index 00000000..c56d05f8 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/views/carousel.css @@ -0,0 +1,116 @@ +/* + The rulesets for the carousel component. + + default: + container height: 400px; + border width: 1px; + border color:; + background color:; + + darkmode: + border color:; + background color:; + */ + +.carousel { + --borderWidth: 1px; + --borderRadius: 10px; + --borderColor: 0, 0%, 0%; + --backgroundColor: transparent; + + display: flex; + flex-direction: column; + max-inline-size: 100%; + block-size: auto; +} + +.carousel-content { + display: grid; + grid-auto-flow: column; + grid-auto-columns: 100%; + block-size: 350px; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + background-color: hsla(var(--backgroundColor), 1.0); + overflow: hidden; +} + +.carousel-content::-webkit-scrollbar{ + display: none; +} + +.slide { + display: flex; + block-size: auto; + overflow: hidden; +} + +.slide-thumbnail { + inline-size: 60%; +} + +.slide-thumbnail > img { + inline-size: 100%; + blocks-ize: 100%; + object-fit: cover; +} + +.slide-caption { + inline-size: 40%; + padding-block: 24px; + padding-inline: 24px; +} + +.carousel-indication { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + block-size: 50px; +} + +.scheme\:dark .carousel, .carousel.scheme\:dark { + --borderColor: var(--blackColor); + --backgroundColor: transparent; +} + +/* + indicator component + + border width:; + border radius:; + border color:; + background color:; + + darkmode: + border color:; + background color:; + */ + +.indicator { + --borderRadius: 5px; + --backgroundColor: transparent; + --borderColor: #000000; + + position: relative; + display: inline-block; + inline-size: 40px; + block-size: 6px; + border-width: 1px; + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + cursor: pointer; +} + +.indicator:hover { + --borderColor: var(--primaryColor); + --backgroundColor: var(--primaryColor); +} + +.scheme\:dark .indicator, .indicator.scheme\:dark { + --backgroundColor: transparent; + --borderColor: #000000; +} diff --git a/Sources/HTMLKitComponents/Resources/css/views/divider.css b/Sources/HTMLKitComponents/Resources/css/views/divider.css new file mode 100644 index 00000000..a0c0b1de --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/views/divider.css @@ -0,0 +1,24 @@ +/* + The stylesheet for the divider component. + + default: + background color: #000000 + + darkmode: + background color: #FFFFFF + */ + +.divider { + --backgroundColor: 0, 0%, 0%; + + position: relative; + display: block; + inline-size: 100%; + block-size: 1px; + border-width: 0; + background-color: hsla(var(--backgroundColor), 1.0); +} + +.scheme\:dark .divider, .divider.scheme\:dark { + --backgroundColor: 0, 0%, 100%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/views/form.css b/Sources/HTMLKitComponents/Resources/css/views/form.css new file mode 100644 index 00000000..6dbdb033 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/views/form.css @@ -0,0 +1,34 @@ +/* + The rulsets for the form component. + */ + +.form { + display: block; + width: 100%; + height: auto; +} + +/* + The rulesets for the label component. + + --labelMarginBlockStart: 1.25rem (20px) + --labelMarginBlockEnd: 0.625rem (10px) + */ + +.label { + --foregroundColor: 0, 0%, 0%; + + display: inline-block; + margin-block-start: 1.25rem; + margin-block-end: 0.625rem; + font-family: system-ui; + font-size: var(--fontSize); + font-weight: var(--fontWeight); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); +} + +.scheme\:dark .label, .label.scheme\:dark { + --foregroundColor: 0, 0%, 100%; +} + diff --git a/Sources/HTMLKitComponents/Resources/css/views/image.css b/Sources/HTMLKitComponents/Resources/css/views/image.css new file mode 100644 index 00000000..a5c0f88d --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/views/image.css @@ -0,0 +1,72 @@ +/* + The rulesets for the image component. + + default: + inline size: 100% + border width: 0 + border-radius: 0 + + darkmode: + */ + +.image { + --inlineSize: 100%; + --borderWidth: 0; + --borderRadius: 0; + + position: relative; + max-inline-size: var(--inlineSize); + height: auto; + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); +} + +.image.fit\:contain { + object-fit:contain; +} + +.image.fit\:cover { + object-fit:cover; +} + +.image.fit\:fill { + object-fit:fill; +} + +.image.fit\:scaledown { + object-fit:scale-down; +} + +.image.fit\:none { + object-fit:none; +} + +.image.opacity\:transparent { + opacity: 0; +} + +.image.opacity\:intransparent { + opacity: 1; +} + +.image.scale\:small { + --inlineSize: 25%; +} + +.image.scale\:medium { + --inlineSize: 50%; +} + +.image.scale\:large { + --inlineSize: 75%; +} + +.image.shape\:circle { + --borderRadius: 50%; +} + +.scheme\:dark .image, .image.scheme\:dark { + filter: brightness(.8) contrast(1.2); +} diff --git a/Sources/HTMLKitComponents/Resources/css/views/modal.css b/Sources/HTMLKitComponents/Resources/css/views/modal.css new file mode 100644 index 00000000..7e138263 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/views/modal.css @@ -0,0 +1,38 @@ +/* + The stylesheet for the modal component. + + default: + block padding: 12px + inline padding: 16px + border width: 1px; + border color: #DFE3E7 + border radius: 0 + background color: #FFFFFF + + darkmode: + border color: #484F56 + background color: #22262A + */ + +.modal { + --paddingBlock: 0.75rem; + --paddingInline: 1.0rem; + --borderWidth: 1px; + --borderColor: 10, 14%, 89%; + --borderRadius: 0; + --backgroundColor: 0, 0%, 100%; + + padding-block: var(--paddingBlock); + padding-inline: var(--paddingInline); + border-width: var(--borderWidth); + border-style: solid; + border-color: hsla(var(--borderColor), 1.0); + border-radius: var(--borderRadius); + background-color: hsla(var(--backgroundColor), 1.0); + box-shadow: 0 15px 35px hsla(0,0%, 0%, 0.2); +} + +.scheme\:dark .modal, .modal.scheme\:dark { + --borderColor: 210, 9%, 31%; + --backgroundColor: 210, 11%, 15%; +} diff --git a/Sources/HTMLKitComponents/Resources/css/scrollview.css b/Sources/HTMLKitComponents/Resources/css/views/scrollview.css similarity index 66% rename from Sources/HTMLKitComponents/Resources/css/scrollview.css rename to Sources/HTMLKitComponents/Resources/css/views/scrollview.css index a5e22ad6..842e2a16 100644 --- a/Sources/HTMLKitComponents/Resources/css/scrollview.css +++ b/Sources/HTMLKitComponents/Resources/css/views/scrollview.css @@ -1,21 +1,19 @@ -/** - scrollview component +/* + The stylesheet for the scrollview component. */ .scrollview { display: grid; - gap: var(--scrollViewGapSize); - background-color: var(--scrollViewBackgroundColor); } -.scrollview::-webkit-scrollbar{ +.scrollview::-webkit-scrollbar { display: none; } .scrollview.direction\:horizontal { grid-auto-flow: column; grid-auto-columns: min-content; - height: inherit; + max-width: 100%; overflow-x: auto; overscroll-behavior-inline: contain; } @@ -23,7 +21,7 @@ .scrollview.direction\:vertical { grid-auto-flow: row; grid-auto-rows: min-content; - height: inherit; + max-height: 100%; overflow-y: auto; overscroll-behavior-y: contain; } diff --git a/Sources/HTMLKitComponents/Resources/css/views/snippet.css b/Sources/HTMLKitComponents/Resources/css/views/snippet.css new file mode 100644 index 00000000..611f8d1e --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/views/snippet.css @@ -0,0 +1,55 @@ +/* + The stylesheet for the snippet component. + + default: + inline padding: 16px + font size: 16px + font weight: normal + line height: 1.5; + foreground color: #000000 + + darkmode: + foreground color: #FFFFFF + */ + +.snippet { + --paddingInline: 1.0em; + --fontSize: 1.0rem; + --fontWeight: 400; + --lineHeight: 1.5; + --foregroundColor: 0, 0%, 0%; + + position: relative; + display: block; + inline-size: 100%; + block-size: auto; + counter-reset: line; + background-color: hsla(var(--backgroundColor), 1.0); + overflow-inline: auto; +} + +.snippet > p { + display: block; + font-size: var(--fontSize); + line-height: var(--lineHeight); + color: hsla(var(--foregroundColor), 1.0); + word-wrap: normal; + white-space: pre; + counter-increment: line; +} + +.snippet > p::before { + position: relative; + display: inline-block; + margin-inline-end: 0.938rem; + padding-inline: 0.938rem; + white-space: nowrap; + text-align: right; + content: counter(line); + color: hsla(var(--foregroundColor), 1.0); + -webkit-user-select: none; +} + +.scheme\:dark .snippet, .snippet.scheme\:dark { + --foregroundColor: 0, 0%, 100%; +} diff --git a/Sources/HTMLKitComponents/Resources/js/components/buttons/dropdown.js b/Sources/HTMLKitComponents/Resources/js/components/buttons/dropdown.js new file mode 100644 index 00000000..32addc14 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/js/components/buttons/dropdown.js @@ -0,0 +1,56 @@ +(function() { + + var Dropdown = function (element) { + + this.element = element; + this.label = element.getElementsByClassName('dropdown-label')[0]; + this.dropdownlist = element.getElementsByClassName('dropdown-content')[0]; + + this.initiateListener(); + }; + + /* + Initiates the listeners + */ + Dropdown.prototype.initiateListener = function () { + + var self = this; + + this.label.addEventListener('mousedown', function () { + self.showDropdownList(); + }); + + window.addEventListener('click', function (event) { + + if(!event.target.matches('.dropdown-label')) { + self.hideDropdownList(); + } + }); + }; + + /* + Shows the dropdown list + */ + Dropdown.prototype.showDropdownList = function () { + this.dropdownlist.classList.add('state:visible'); + }; + + /* + Hides the dropdown list + */ + Dropdown.prototype.hideDropdownList = function () { + this.dropdownlist.classList.remove('state:visible'); + }; + + var dropdown = document.getElementsByClassName('dropdown'); + + if(dropdown.length > 0) { + + for(var i = 0; i < dropdown.length; i++) { + + (function(i) { + new Dropdown(dropdown[i]); + })(i); + } + } +}()); diff --git a/Sources/HTMLKitComponents/Resources/js/components/forms/select.js b/Sources/HTMLKitComponents/Resources/js/components/forms/select.js new file mode 100644 index 00000000..5086a7bd --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/js/components/forms/select.js @@ -0,0 +1,75 @@ +(function() { + + var Selectfield = function (element) { + + this.element = element; + this.textfield = element.getElementsByClassName('selectfield-textfield')[0]; + this.optionlist = element.getElementsByClassName('selectfield-optionlist')[0]; + + this.initiateListener(); + }; + + /* + Initiates the listeners + */ + Selectfield.prototype.initiateListener = function () { + + var self = this; + + this.textfield.addEventListener('focus', function () { + self.showOptionList(); + }); + + this.textfield.addEventListener('focusout', function () { + self.hideOptionList(); + }); + + this.optionlist.addEventListener('mousedown', function (event) { + + event.preventDefault(); + + if (event.target.tagName == 'LI') { + + self.setInputValue(event.target.innerHTML); + + self.hideOptionList(); + } + }); + }; + + /* + Sets the value for the textfield + */ + Selectfield.prototype.setInputValue = function (value) { + + this.textfield.value = value; + }; + + /* + Shows the option list + */ + Selectfield.prototype.showOptionList = function () { + + this.optionlist.classList.add('state:visible'); + }; + + /* + Hides the option list + */ + Selectfield.prototype.hideOptionList = function () { + + this.optionlist.classList.remove('state:visible'); + }; + + var selectfield = document.getElementsByClassName('selectfield'); + + if(selectfield.length > 0) { + + for(var i = 0; i < selectfield.length; i++) { + + (function(i) { + new Selectfield(selectfield[i]); + })(i); + } + } +}()); diff --git a/Sources/HTMLKitComponents/Resources/js/textpad.js b/Sources/HTMLKitComponents/Resources/js/components/forms/textpad.js similarity index 88% rename from Sources/HTMLKitComponents/Resources/js/textpad.js rename to Sources/HTMLKitComponents/Resources/js/components/forms/textpad.js index 4b37a2df..826d25e2 100644 --- a/Sources/HTMLKitComponents/Resources/js/textpad.js +++ b/Sources/HTMLKitComponents/Resources/js/components/forms/textpad.js @@ -2,7 +2,7 @@ var Textpad = function (element) { - this.element = element + this.element = element; this.content = element.getElementsByClassName('textpad-content')[0]; this.toolbar = element.getElementsByClassName('textpad-toolbar')[0]; @@ -19,7 +19,7 @@ event.preventDefault(); - var replacement = ""; + var replacement = ''; var selection = self.getSelection(); @@ -53,15 +53,11 @@ if(textpad.length > 0) { - for(var i = 0; i < textpad.length; i++) { - - (function(i) { - - new Textpad(textpad[i]); + for(var i = 0; i < textpad.length; i++) { - })(i); - - } + (function(i) { + new Textpad(textpad[i]); + })(i); + } } - }()); diff --git a/Sources/HTMLKitComponents/Resources/js/carousel.js b/Sources/HTMLKitComponents/Resources/js/components/views/carousel.js similarity index 97% rename from Sources/HTMLKitComponents/Resources/js/carousel.js rename to Sources/HTMLKitComponents/Resources/js/components/views/carousel.js index 2a7137c7..72d25d4d 100644 --- a/Sources/HTMLKitComponents/Resources/js/carousel.js +++ b/Sources/HTMLKitComponents/Resources/js/components/views/carousel.js @@ -11,13 +11,13 @@ this.autoPlay(1); this.initiateListener(); - } + }; Carousel.prototype.initiateListener = function () { var self = this; - for (let indicator of this.indication.children) { + for (var indicator of this.indication.children) { indicator.addEventListener('click', function (event) { diff --git a/Sources/HTMLKitComponents/Resources/js/components/views/datepicker.js b/Sources/HTMLKitComponents/Resources/js/components/views/datepicker.js new file mode 100644 index 00000000..ad6d8e2f --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/js/components/views/datepicker.js @@ -0,0 +1,187 @@ +(function() { + + var Datepicker = function (element) { + + this.element = element; + this.datefield = element.getElementsByClassName('datepicker-datefield')[0]; + this.picker = element.getElementsByClassName('datepicker-calendar')[0]; + this.calendar = element.getElementsByClassName('calendar-days')[0]; + this.detail = element.getElementsByClassName('calendar-detail')[0]; + this.navigation = element.getElementsByClassName('calendar-navigation')[0]; + this.date = new Date().getDate(); + this.month = new Date().getMonth(); + this.year = new Date().getFullYear(); + this.day = new Date().getDay(); + + this.createCalendar(); + + this.initiateListener(); + }; + + /* + Initiates the listeners. + */ + Datepicker.prototype.initiateListener = function () { + + var self = this; + + this.datefield.addEventListener('focus', function () { + self.showPicker(); + }); + + this.datefield.addEventListener('focusout', function () { + self.hidePicker(); + }); + + this.navigation.addEventListener('mousedown', function (event) { + + event.preventDefault(); + + if(event.target.tagName == 'BUTTON') { + + switch(event.target.value) { + + case 'previous': + + self.browsePrevious(); + + break; + + case 'next': + + self.browseNext(); + + break; + } + } + + }); + + this.calendar.addEventListener('mousedown', function (event) { + + event.preventDefault(); + + if(event.target.tagName == 'LI') { + + self.setInputValue(event.target.firstChild.dateTime); + + self.hidePicker(); + } + + if(event.target.tagName == 'TIME') { + + self.setInputValue(event.target.dateTime); + + self.hidePicker(); + } + }); + }; + + /* + Recreates the calendar by the selection. + */ + Datepicker.prototype.createCalendar = function () { + + var calendar = ''; + + for(var j = 0; j < getFirstOfMonth(this.year, this.month); j++) { + calendar = calendar + '
  • '; + } + + for(var i = 1; i < getLastOfMonth(this.year, this.month) + 1; i++) { + calendar = calendar + '
  • '; + } + + this.setCalendarDetail(this.year, this.month); + + this.calendar.innerHTML = calendar; + }; + + /* + Updates the calendar details. + */ + Datepicker.prototype.setCalendarDetail = function (year, month) { + + const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + this.detail.innerHTML = months[month] + ' ' + year; + }; + + /* + Sets the input value. + */ + Datepicker.prototype.setInputValue = function (value) { + this.datefield.value = value; + }; + + /* + Shows the picker. + */ + Datepicker.prototype.showPicker = function () { + this.picker.classList.add('state:visible'); + }; + + /* + Hides the picker. + */ + Datepicker.prototype.hidePicker = function () { + this.picker.classList.remove('state:visible'); + }; + + /* + Browses onto the previous month. + */ + Datepicker.prototype.browsePrevious = function() { + + if (this.month < 1) { + + this.year -= 1; + this.month = 11; + + } else { + this.month -= 1; + } + + this.createCalendar(); + }; + + /* + Browses onto the next month. + */ + Datepicker.prototype.browseNext = function() { + + if (this.month > 10) { + + this.year += 1; + this.month = 0; + + } else { + this.month += 1; + } + + this.createCalendar(); + }; + + /// Calculates the weekday for the first of the month. + function getFirstOfMonth(year, month) { + return new Date(year, month, 1).getDay(); + } + + /// Calculates the total days of the month. + function getLastOfMonth(year, month) { + return new Date(year, month, 0).getDate(); + } + + var datepicker = document.getElementsByClassName('datepicker'); + + if(datepicker.length > 0) { + + for(var i = 0; i < datepicker.length; i++) { + + (function(i) { + new Datepicker(datepicker[i]); + })(i); + } + } + +}()); diff --git a/Sources/HTMLKitComponents/Resources/js/all.js b/Sources/HTMLKitComponents/Resources/js/interactions/all.js similarity index 60% rename from Sources/HTMLKitComponents/Resources/js/all.js rename to Sources/HTMLKitComponents/Resources/js/interactions/all.js index a0d1877e..14bb50e7 100644 --- a/Sources/HTMLKitComponents/Resources/js/all.js +++ b/Sources/HTMLKitComponents/Resources/js/interactions/all.js @@ -1,143 +1,144 @@ var $ = (function () { - + 'use strict'; - + var Self = function (selector) { + this.elems = document.querySelectorAll(selector); }; - /** - * Peforms when the pointer enters the target area. + /* + Peforms when the pointer enters the target area. */ Self.prototype.onHover = function (callback) { - this.elems[0].addEventListener("mouseenter", callback); + this.elems[0].addEventListener('mouseenter', callback); }; - /** - * Peforms when the pointer leaves the target area. + /* + Peforms when the pointer leaves the target area. */ Self.prototype.onLeave = function (callback) { - this.elems[0].addEventListener("mouseleave", callback); + this.elems[0].addEventListener('mouseleave', callback); }; - /** - * Performs when the target value changes. + /* + Performs when the target value changes. */ Self.prototype.onChange = function (callback) { - this.elems[0].addEventListener("change", callback); + this.elems[0].addEventListener('change', callback); }; - /** - * Performs when the target is clicked. + /* + Performs when the target is clicked. */ Self.prototype.onClick = function (callback) { - this.elems[0].addEventListener("click", callback); + this.elems[0].addEventListener('click', callback); }; - /** - * Performs when the target is touched. + /* + Performs when the target is touched. */ Self.prototype.onTapGesture = function (callback) { - this.elems[0].addEventListener("touchend", callback); + this.elems[0].addEventListener('touchend', callback); }; - /** - * Performs when the target is touched. + /* + Performs when the target is touched. */ Self.prototype.onLongPressGesture = function (callback) { - this.elems[0].addEventListener("touchstart", callback); + this.elems[0].addEventListener('touchstart', callback); }; - /** - * Performs when the target is dragged. + /* + Performs when the target is dragged. */ Self.prototype.onDrag = function (callback) { - this.elems[0].addEventListener("drag", callback); + this.elems[0].addEventListener('drag', callback); }; - /** - * Performs when the target is dropped. + /* + Performs when the target is dropped. */ Self.prototype.onDrop = function (callback) { - this.elems[0].addEventListener("drop", callback); + this.elems[0].addEventListener('drop', callback); }; - /** - * Performs when the target is focused. + /* + Performs when the target is focused. */ Self.prototype.onFocus = function (callback) { - this.elems[0].addEventListener("focus", callback); + this.elems[0].addEventListener('focus', callback); }; - /** - * Performs when the target is submitted. + /* + Performs when the target is submitted. */ Self.prototype.onSubmit = function (callback, validate) { if (validate) { - this.elems[0].setAttribute("novalidate", "novalidate"); + this.elems[0].setAttribute('novalidate', 'novalidate'); } - this.elems[0].addEventListener("submit", callback); + this.elems[0].addEventListener('submit', callback); }; - /** - * Shows the target. + /* + Shows the target. */ Self.prototype.show = function() { - const elements = document.getElementsByClassName("state:visible"); + const elements = document.getElementsByClassName('state:visible'); for (let element of elements){ - element.classList.remove("state:visible") + element.classList.remove('state:visible'); } - this.elems[0].classList.add("state:visible") + this.elems[0].classList.add('state:visible'); }; - /** - * Hides the target. + /* + Hides the target. */ Self.prototype.hide = function() { - this.elems[0].classList.add("state:hidden") + this.elems[0].classList.add('state:hidden'); }; - /** - * Animates the target. + /* + Animates the target. */ Self.prototype.animate = function({params}, speed) { this.elems[0].animate({params}, speed); }; - /** - * Opens the dialog. + /* + Opens the dialog. */ Self.prototype.open = function() { - this.elems[0].showModal() + this.elems[0].showModal(); }; - /** - * Closes the dialog. + /* + Closes the dialog. */ Self.prototype.close = function() { - this.elems[0].close() + this.elems[0].close(); }; - /** - * Validates a form. + /* + Validates a form. */ Self.prototype.validate = function(validators) { @@ -149,7 +150,7 @@ var $ = (function () { switch (validator.rule) { - case "value": + case 'value': if (!element.value) { element.setCustomValidity('The field must have a value.'); @@ -160,9 +161,9 @@ var $ = (function () { break; - case "email": + case 'email': - if (!element.value.includes("@")) { + if (!element.value.includes('@')) { element.setCustomValidity('The field must have a valid email format.'); } else { @@ -171,9 +172,9 @@ var $ = (function () { break; - case "url": + case 'url': - if (!element.value.includes(":")) { + if (!element.value.includes(':')) { element.setCustomValidity('The field must have a valid url format.'); } else { diff --git a/Sources/HTMLKitComponents/Tokens.swift b/Sources/HTMLKitComponents/Tokens.swift index 664a7dcf..d7096b5b 100644 --- a/Sources/HTMLKitComponents/Tokens.swift +++ b/Sources/HTMLKitComponents/Tokens.swift @@ -133,6 +133,9 @@ public enum Tokens { /// A size for text. public enum FontSize: String { + /// Changes the size to small. + case tiny = "size:tiny" + /// Changes the size to small. case small = "size:small" @@ -141,6 +144,9 @@ public enum Tokens { /// Changes the size to large. case large = "size:large" + + /// Changes the size to extra large. + case extralarge = "size:extralarge" } /// A transformation for the text. @@ -336,8 +342,6 @@ public enum Tokens { public enum ClipShape: String { - case smallrounded = "shape:smallrounded" - case largerounded = "shape:largerounded" case circle = "shape:circle" } @@ -446,7 +450,6 @@ public enum Tokens { case smallrounded = "shape:smallrounded" case largerounded = "shape:largerounded" case fullrounded = "shape:fullrounded" - case circle = "shape:cirlce" } /// A size of a button. @@ -507,4 +510,136 @@ public enum Tokens { /// Sets the state to visible. case visible = "state:visible" } + + public enum BorderColor: String { + + /// Changes the border color to black. + case black = "border:black" + + /// Changes the border color to white. + case white = "border:white" + + /// Changes the border color to blue. + case blue = "border:blue" + + /// Changes the border color to brown. + case brown = "border:brown" + + /// Changes the border color to cyan. + case cyan = "border:cyan" + + /// Changes the border color to green. + case green = "border:green" + + /// Changes the border color to indigo. + case indigo = "border:indigo" + + /// Changes the border color to mint. + case mint = "border:mint" + + /// Changes the border color to pink. + case pink = "border:pink" + + /// Changes the border color to purple. + case purple = "border:purple" + + /// Changes the border color to red. + case red = "border:red" + + /// Changes the border color to teal. + case teal = "border:teal" + + /// Changes the border color to ornage. + case orange = "border:orange" + + /// Changes the border color to yellow. + case yellow = "border:yellow" + + /// Changes the border color to gray. + case gray = "border:gray" + + /// Changes the border color to silver. + case silver = "border:silver" + + case primary = "border:primary" + + case secondary = "border:secondary" + + case transparent = "border:transparent" + } + + public enum FocusColor: String { + + /// Changes the border color to black. + case black = "focus:black" + + /// Changes the border color to white. + case white = "focus:white" + + /// Changes the border color to blue. + case blue = "focus:blue" + + /// Changes the border color to brown. + case brown = "focus:brown" + + /// Changes the border color to cyan. + case cyan = "focus:cyan" + + /// Changes the border color to green. + case green = "focus:green" + + /// Changes the border color to indigo. + case indigo = "focus:indigo" + + /// Changes the border color to mint. + case mint = "focus:mint" + + /// Changes the border color to pink. + case pink = "focus:pink" + + /// Changes the border color to purple. + case purple = "focus:purple" + + /// Changes the border color to red. + case red = "focus:red" + + /// Changes the border color to teal. + case teal = "focus:teal" + + /// Changes the border color to ornage. + case orange = "focus:orange" + + /// Changes the border color to yellow. + case yellow = "focus:yellow" + + /// Changes the border color to gray. + case gray = "focus:gray" + + /// Changes the border color to silver. + case silver = "focus:silver" + + case primary = "focus:primary" + + case secondary = "focus:secondary" + + case transparent = "focus:transparent" + } + + public enum ColorScheme: String { + + /// Changes the color scheme to dark. + case dark = "scheme:dark" + + /// Changes the color scheme to light. + case light = "scheme:light" + } + + public enum BoxPadding: String { + + case large = "padding:large" + + case medium = "padding:medium" + + case small = "padding:small" + } } diff --git a/Sources/HTMLKitConverter/Converter.swift b/Sources/HTMLKitConverter/Converter.swift index 632b7b04..3f3a45a0 100644 --- a/Sources/HTMLKitConverter/Converter.swift +++ b/Sources/HTMLKitConverter/Converter.swift @@ -16,15 +16,11 @@ public class Converter { private init() {} - private func convert(content: String) throws -> String { + private func convert(content: String, with name: String) throws -> String { let document = try XMLDocument(xmlString: content, options: [.documentIncludeContentTypeDeclaration]) - guard let root = document.rootElement() else { - throw ConverterError.rootNotFound - } - - return try Parser.shared.parse(node: root) + return try Parser.shared.parse(name: name, document: document) } public func convert(source path: URL) throws { @@ -34,7 +30,7 @@ public class Converter { let content = try String(contentsOf: path) if content.count > 1 { - print(try convert(content: content)) + print(try convert(content: content, with: path.deletingPathExtension().lastPathComponent)) } else { throw ConverterError.emptyFile @@ -56,7 +52,7 @@ public class Converter { let content = try String(contentsOf: path) if content.count > 1 { - print(try convert(content: content)) + print(try convert(content: content, with: path.deletingPathExtension().lastPathComponent)) } else { throw ConverterError.emptyFile @@ -76,7 +72,7 @@ public class Converter { if content.count > 1 { - let result = try convert(content: content) + let result = try convert(content: content, with: path.deletingPathExtension().lastPathComponent) try result.write(to: target, atomically: true, encoding: .utf8) @@ -101,7 +97,7 @@ public class Converter { if content.count > 1 { - let result = try convert(content: content) + let result = try convert(content: content, with: path.deletingPathExtension().lastPathComponent) try result.write(to: target, atomically: true, encoding: .utf8) diff --git a/Sources/HTMLKitConverter/Parser.swift b/Sources/HTMLKitConverter/Parser.swift index f2977444..4c00d5d3 100644 --- a/Sources/HTMLKitConverter/Parser.swift +++ b/Sources/HTMLKitConverter/Parser.swift @@ -36,14 +36,20 @@ internal class Parser { private init() {} + internal func parse(name: String, document: XMLDocument) throws -> String { + return try ViewLayout(name: name, document: document).build() + } + internal func parse(node: XMLNode, indent: Int? = nil) throws -> String { switch node.kind { case .text: - return TextElement(node: node).build() + + return TextElement(node: node, indent: indent).build() case .comment: - return CommentElement(node: node).build() + + return CommentElement(node: node, indent: indent).build() case .element: @@ -55,346 +61,349 @@ internal class Parser { switch localName { case "html": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "head": - return try ContentElement(element: element).build(verbatim: "Head") + return try ContentElement(element: element, indent: indent).build(verbatim: "Head") case "body": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "nav": - return try ContentElement(element: element).build(verbatim: "Navigation") + return try ContentElement(element: element, indent: indent).build(verbatim: "Navigation") case "link": - return try EmptyElement(element: element).build() + return try EmptyElement(element: element, indent: indent).build() case "aside": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "section": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "h1": - return try ContentElement(element: element).build(verbatim: "Heading1") + return try ContentElement(element: element, indent: indent).build(verbatim: "Heading1") case "h2": - return try ContentElement(element: element).build(verbatim: "Heading2") + return try ContentElement(element: element, indent: indent).build(verbatim: "Heading2") case "h3": - return try ContentElement(element: element).build(verbatim: "Heading3") + return try ContentElement(element: element, indent: indent).build(verbatim: "Heading3") case "h4": - return try ContentElement(element: element).build(verbatim: "Heading4") + return try ContentElement(element: element, indent: indent).build(verbatim: "Heading4") case "h5": - return try ContentElement(element: element).build(verbatim: "Heading5") + return try ContentElement(element: element, indent: indent).build(verbatim: "Heading5") case "h6": - return try ContentElement(element: element).build(verbatim: "Heading6") + return try ContentElement(element: element, indent: indent).build(verbatim: "Heading6") case "hgroup": - return try ContentElement(element: element).build(verbatim: "HeadingGroup") + return try ContentElement(element: element, indent: indent).build(verbatim: "HeadingGroup") case "header": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "footer": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "address": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "p": - return try ContentElement(element: element).build(verbatim: "Paragraph") + return try ContentElement(element: element, indent: indent).build(verbatim: "Paragraph") case "hr": - return try EmptyElement(element: element).build(verbatim: "HorizontalRule") + return try EmptyElement(element: element, indent: indent).build(verbatim: "HorizontalRule") case "pre": - return try ContentElement(element: element).build(verbatim: "PreformattedText") + return try ContentElement(element: element, indent: indent).build(verbatim: "PreformattedText") case "blockquote": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "ol": - return try ContentElement(element: element).build(verbatim: "OrderedList") + return try ContentElement(element: element, indent: indent).build(verbatim: "OrderedList") case "ul": - return try ContentElement(element: element).build(verbatim: "UnorderedList") + return try ContentElement(element: element, indent: indent).build(verbatim: "UnorderedList") case "dl": - return try ContentElement(element: element).build(verbatim: "DescriptionList") + return try ContentElement(element: element, indent: indent).build(verbatim: "DescriptionList") case "figure": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "a": - return try ContentElement(element: element).build(verbatim: "Anchor") + return try ContentElement(element: element, indent: indent).build(verbatim: "Anchor") case "em": - return try ContentElement(element: element).build(verbatim: "Emphasize") + return try ContentElement(element: element, indent: indent).build(verbatim: "Emphasize") case "small": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "s": - return try ContentElement(element: element).build(verbatim: "StrikeThrough") + return try ContentElement(element: element, indent: indent).build(verbatim: "StrikeThrough") case "main": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "div": - return try ContentElement(element: element).build(verbatim: "Division") + return try ContentElement(element: element, indent: indent).build(verbatim: "Division") case "dfn": - return try ContentElement(element: element).build(verbatim: "Definition") + return try ContentElement(element: element, indent: indent).build(verbatim: "Definition") case "cite": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "q": - return try ContentElement(element: element).build(verbatim: "ShortQuote") + return try ContentElement(element: element, indent: indent).build(verbatim: "ShortQuote") case "rt": - return try ContentElement(element: element).build(verbatim: "RubyText") + return try ContentElement(element: element, indent: indent).build(verbatim: "RubyText") case "rp": - return try ContentElement(element: element).build(verbatim: "RubyPronunciation") + return try ContentElement(element: element, indent: indent).build(verbatim: "RubyPronunciation") case "abbr": - return try ContentElement(element: element).build(verbatim: "Abbreviation") + return try ContentElement(element: element, indent: indent).build(verbatim: "Abbreviation") case "data": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "time": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "code": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "v": - return try ContentElement(element: element).build(verbatim: "Variable") + return try ContentElement(element: element, indent: indent).build(verbatim: "Variable") case "samp": - return try ContentElement(element: element).build(verbatim: "SampleOutput") + return try ContentElement(element: element, indent: indent).build(verbatim: "SampleOutput") case "kbd": - return try ContentElement(element: element).build(verbatim: "KeyboardOutput") + return try ContentElement(element: element, indent: indent).build(verbatim: "KeyboardOutput") case "sub": - return try ContentElement(element: element).build(verbatim: "Subscript") + return try ContentElement(element: element, indent: indent).build(verbatim: "Subscript") case "sup": - return try ContentElement(element: element).build(verbatim: "Superscript") + return try ContentElement(element: element, indent: indent).build(verbatim: "Superscript") case "i": - return try ContentElement(element: element).build(verbatim: "Italic") + return try ContentElement(element: element, indent: indent).build(verbatim: "Italic") case "b": - return try ContentElement(element: element).build(verbatim: "Bold") + return try ContentElement(element: element, indent: indent).build(verbatim: "Bold") case "strong": - return try ContentElement(element: element).build(verbatim: "Strong") + return try ContentElement(element: element, indent: indent).build(verbatim: "Strong") case "u": - return try ContentElement(element: element).build(verbatim: "SampleOutput") + return try ContentElement(element: element, indent: indent).build(verbatim: "SampleOutput") case "mark": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "bdi": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "bdo": - return try EmptyElement(element: element).build() + return try EmptyElement(element: element, indent: indent).build() case "span": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "br": - return try EmptyElement(element: element).build(verbatim: "LineBreak") + return try EmptyElement(element: element, indent: indent).build(verbatim: "LineBreak") case "wbr": - return try EmptyElement(element: element).build(verbatim: "WordBreak") + return try EmptyElement(element: element, indent: indent).build(verbatim: "WordBreak") case "ins": - return try ContentElement(element: element).build(verbatim: "InsertedText") + return try ContentElement(element: element, indent: indent).build(verbatim: "InsertedText") case "del": - return try ContentElement(element: element).build(verbatim: "DeletedText") + return try ContentElement(element: element, indent: indent).build(verbatim: "DeletedText") case "img": - return try EmptyElement(element: element).build(verbatim: "Image") + return try EmptyElement(element: element, indent: indent).build(verbatim: "Image") case "embed": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "iframe": - return try ContentElement(element: element).build(verbatim: "InlineFrame") + return try ContentElement(element: element, indent: indent).build(verbatim: "InlineFrame") case "param": - return try EmptyElement(element: element).build(verbatim: "Parameter") + return try EmptyElement(element: element, indent: indent).build(verbatim: "Parameter") case "dt": - return try ContentElement(element: element).build(verbatim: "TermName") + return try ContentElement(element: element, indent: indent).build(verbatim: "TermName") case "dd": - return try ContentElement(element: element).build(verbatim: "TermDefinition") + return try ContentElement(element: element, indent: indent).build(verbatim: "TermDefinition") case "figcaption": - return try ContentElement(element: element).build(verbatim: "FigureCaption") + return try ContentElement(element: element, indent: indent).build(verbatim: "FigureCaption") case "optgroup": - return try ContentElement(element: element).build(verbatim: "OptionGroup") + return try ContentElement(element: element, indent: indent).build(verbatim: "OptionGroup") case "option": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "legend": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "summary": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "li": - return try ContentElement(element: element).build(verbatim: "ListItem") + return try ContentElement(element: element, indent: indent).build(verbatim: "ListItem") case "colgroup": - return try ContentElement(element: element).build(verbatim: "ColumnGroup") + return try ContentElement(element: element, indent: indent).build(verbatim: "ColumnGroup") case "col": - return try ContentElement(element: element).build(verbatim: "Column") + return try ContentElement(element: element, indent: indent).build(verbatim: "Column") case "tbody": - return try ContentElement(element: element).build(verbatim: "TableBody") + return try ContentElement(element: element, indent: indent).build(verbatim: "TableBody") case "thead": - return try ContentElement(element: element).build(verbatim: "TableHead") + return try ContentElement(element: element, indent: indent).build(verbatim: "TableHead") case "tfoot": - return try ContentElement(element: element).build(verbatim: "TableFoot") + return try ContentElement(element: element, indent: indent).build(verbatim: "TableFoot") case "tr": - return try ContentElement(element: element).build(verbatim: "TableRow") + return try ContentElement(element: element, indent: indent).build(verbatim: "TableRow") case "td": - return try ContentElement(element: element).build(verbatim: "DataCell") + return try ContentElement(element: element, indent: indent).build(verbatim: "DataCell") case "th": - return try ContentElement(element: element).build(verbatim: "HeaderCell") + return try ContentElement(element: element, indent: indent).build(verbatim: "HeaderCell") case "textarea": - return try ContentElement(element: element).build(verbatim: "TextArea") + return try ContentElement(element: element, indent: indent).build(verbatim: "TextArea") case "input": - return try EmptyElement(element: element).build() + return try EmptyElement(element: element, indent: indent).build() case "video": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "audio": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "map": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "area": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "form": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "datalist": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "output": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "meter": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "details": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "dialog": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "script": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "noscript": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "template": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "canvas": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "table": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "fieldset": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "button": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "select": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "label": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "title": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "base": - return try EmptyElement(element: element).build() + return try EmptyElement(element: element, indent: indent).build() case "meta": - return try EmptyElement(element: element).build() + return try EmptyElement(element: element, indent: indent).build() case "style": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "source": - return try EmptyElement(element: element).build() + return try EmptyElement(element: element, indent: indent).build() case "track": - return try EmptyElement(element: element).build() + return try EmptyElement(element: element, indent: indent).build() case "article": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "progress": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "circle": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "rect": - return try ContentElement(element: element).build(verbatim: "Rectangle") + return try ContentElement(element: element, indent: indent).build(verbatim: "Rectangle") case "ellipse": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "line": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "polygon": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "path": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "use": - return try ContentElement(element: element).build() + return try ContentElement(element: element, indent: indent).build() case "g": - return try ContentElement(element: element).build(verbatim: "Group") + return try ContentElement(element: element, indent: indent).build(verbatim: "Group") + + case "svg": + return try ContentElement(element: element, indent: indent).build(verbatim: "Vector") default: throw ParserError.unkownElement(localName) @@ -784,6 +793,9 @@ internal class Parser { case "viewbox": return try ValueAttribute(node: node).build(verbatim: "viewBox") + case "xmlns": + return try ValueAttribute(node: node).build(verbatim: "namespace") + case "onafterprint": return try EventAttribute(node: node).build() @@ -1161,20 +1173,29 @@ internal class Parser { return "" } - internal struct PageLayout { + internal struct ViewLayout { private let name: String - private var content: String { + private var content: String? { get throws { - return try Parser.shared.parse(node: element) + + guard let root = document.rootElement() else { + return nil + } + + return try Parser.shared.parse(node: root, indent: level) } } - private var type: String { + private var doctype: String? { - if let name = doctype.name, let publicId = doctype.publicID, let systemId = doctype.systemID { + guard let dtd = document.dtd else { + return nil + } + + if let name = dtd.name, let publicId = dtd.publicID, let systemId = dtd.systemID { if let type = T(rawValue: "\(name) PUBLIC \"\(publicId)\" \"\(systemId)\"" as! T.RawValue) { return ".\(type)" @@ -1184,61 +1205,46 @@ internal class Parser { return ".html5" } - private let doctype: XMLDTD - - private let element: XMLElement - - internal init(name: String, doctype: XMLDTD, element: XMLElement) { - self.name = name.capitalized - self.doctype = doctype - self.element = element - } - - internal func build() throws -> String { - - return """ - import HTMLKit - - struct \(name)Page: Page { + private var level: Int { - public var body: AnyContent { - Document(type: \(type)) - \(try content) - } - } - """ - } - } - - internal struct ViewLayout { - - private let name: String - - private var content: String { - - get throws { - return try Parser.shared.parse(node: element) + if document.dtd != nil { + return 2 } + + return 1 } - private let element: XMLElement + private let document: XMLDocument - internal init(name: String, element: XMLElement) { + internal init(name: String, document: XMLDocument) { self.name = name.capitalized - self.element = element + self.document = document } internal func build() throws -> String { + if let doctype = doctype { + + return """ + import HTMLKit + + struct \(name)View: View { + + var body: Content { + Document(\(doctype)) + \(try content ?? "") + } + } + """ + } + return """ import HTMLKit struct \(name)View: View { - @TemplateValue(String.self) var context - - public var body: AnyContent { - \(try content) + var body: Content { + \(try content ?? "") } } """ @@ -1256,19 +1262,22 @@ internal class Parser { return value } + private var indent: Int? + private var level: Int { - return node.level - 1 + return node.level + (indent ?? 0) } private let node: XMLNode - internal init(node: XMLNode) { + internal init(node: XMLNode, indent: Int? = nil) { + self.indent = indent self.node = node } - internal func build(preindent: Int = 0) -> String { + internal func build() -> String { - let indent = String(repeating: "\t", count: (level + preindent)) + let indent = String(repeating: "\t", count: level) if let value = value { return "\(indent)Comment(\"\(value)\")\n" @@ -1289,19 +1298,22 @@ internal class Parser { return value } + private var indent: Int? + private var level: Int { - return node.level - 1 + return node.level + (indent ?? 0) } private let node: XMLNode - internal init(node: XMLNode) { + internal init(node: XMLNode, indent: Int? = nil) { + self.indent = indent self.node = node } - internal func build(preindent: Int = 0) -> String { + internal func build(preindent: Int? = nil) -> String { - let indent = String(repeating: "\t", count: (level + preindent)) + let indent = String(repeating: "\t", count: level) if let value = value { return "\(indent)\"\(value)\"\n" @@ -1348,24 +1360,27 @@ internal class Parser { } return try children.compactMap { child in - return try Parser.shared.parse(node: child) + return try Parser.shared.parse(node: child, indent: indent) } } } + private var indent: Int? + private var level: Int { - return element.level - 1 + return element.level + (indent ?? 0) } private let element: XMLElement - internal init(element: XMLElement) { + internal init(element: XMLElement, indent: Int? = nil) { + self.indent = indent self.element = element } - internal func build(preindent: Int = 0) throws -> String { + internal func build() throws -> String { - let indent = String(repeating: "\t", count: (level + preindent)) + let indent = String(repeating: "\t", count: level) var yield: String = "" @@ -1384,9 +1399,9 @@ internal class Parser { return yield } - internal func build(verbatim: String, preindent: Int = 0) throws -> String { + internal func build(verbatim: String) throws -> String { - let indent = String(repeating: "\t", count: (level + preindent)) + let indent = String(repeating: "\t", count: level) var yield: String = "" @@ -1434,19 +1449,22 @@ internal class Parser { } } + private var indent: Int? + private var level: Int { - return element.level - 1 + return element.level + (indent ?? 0) } private let element: XMLElement - internal init(element: XMLElement) { + internal init(element: XMLElement, indent: Int? = nil) { + self.indent = indent self.element = element } - internal func build(preindent: Int = 0) throws -> String { + internal func build() throws -> String { - let indent = String(repeating: "\t", count: (level + preindent)) + let indent = String(repeating: "\t", count: level) var yield: String = "" @@ -1459,9 +1477,9 @@ internal class Parser { return yield } - internal func build(verbatim: String, preindent: Int = 0) throws -> String { + internal func build(verbatim: String) throws -> String { - let indent = String(repeating: "\t", count: (level + preindent)) + let indent = String(repeating: "\t", count: level) var yield: String = "" diff --git a/Sources/Utilities/Minifier/Extensions/Minifier+Character.swift b/Sources/Utilities/Minifier/Extensions/Minifier+Character.swift new file mode 100644 index 00000000..c115929e --- /dev/null +++ b/Sources/Utilities/Minifier/Extensions/Minifier+Character.swift @@ -0,0 +1,299 @@ +import Foundation + +extension Character { + + /// A boolean value indicating whether this character represents an ampersand (U+0026). + public var isAmpersand: Bool { + + if self == "\u{0026}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a question mark (U+003F). + public var isQuestionMark: Bool { + + if self == "\u{003F}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a greater-than sign (U+003E). + public var isGreaterThanSign: Bool { + + if self == "\u{003E}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a less-than sign (U+003C). + public var isLessThanSign: Bool { + + if self == "\u{003C}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a solidus (U+002F). + public var isSolidus: Bool { + + if self == "\u{002F}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents an exclamation mark (U+0021). + public var isExclamationMark: Bool { + + if self == "\u{0021}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents an equal sign (U+003D). + public var isEqualSign: Bool { + + if self == "\u{003D}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents an apostrophe (U+0027). + public var isApostrophe: Bool { + + if self == "\u{0027}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents an quotation mark (U+0022) + public var isQuotationMark: Bool { + + if self == "\u{0022}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents an hyphen minus (U+002D). + public var isHyphenMinus: Bool { + + if self == "\u{002D}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a number sign (U+0023). + public var isNumberSign: Bool { + + if self == "\u{0023}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents an asterisk (U+002A). + public var isAsterisk: Bool { + + if self == "\u{002A}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a back tick (U+0060). + public var isBackTick: Bool { + + if self == "\u{0060}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a colon (U+003A) + public var isColon: Bool { + + if self == "\u{003A}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a semicolon (U+003B) + public var isSemicolon: Bool { + + if self == "\u{003B}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a period (U+002E). + public var isPeriod: Bool { + + if self == "\u{002E}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+002C) + public var isComma: Bool { + + if self == "\u{002C}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+0040) + public var isCommercialAt: Bool { + + if self == "\u{0040}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+0024) + public var isDollarSign: Bool { + + if self == "\u{0024}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+005F) + public var isUnderscore: Bool { + + if self == "\u{005F}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+0028) + public var isLeftParenthesis: Bool { + + if self == "\u{0028}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+0029) + public var isRightParenthesis: Bool { + + if self == "\u{0029}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+007B) + public var isLeftCurlyBracket: Bool { + + if self == "\u{007B}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a comma (U+007D) + public var isRightCurlyBracket: Bool { + + if self == "\u{007D}" { + return true + } + + return false + } + + public var isLeftSquareBracket: Bool { + + if self == "\u{005B}" { + return true + } + + return false + } + + public var isRightSquareBracket: Bool { + + if self == "\u{005D}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a bracket. + /// + /// For example, the following characters all represent brackets: + /// + /// - "(" (U+0028 LEFT PARENTHESIS) + /// - ")" (U+0029 RIGHT PARENTHESIS) + /// - "{" (U+007B LEFT CURLY BRACKET) + /// - "}" (U+007D RIGHT CURLY BRACKET) + /// - "[" (U+005B LEFT SQUARE BRACKET) + /// - "]" (U+005D RIGHT SQUARE BRACKET) + public var isBracket: Bool { + + if self == "\u{0028}" || self == "\u{0029}" || self == "\u{007B}" || self == "\u{007D}" || self == "\u{005B}" || self == "\u{005D}" { + return true + } + + return false + } + + /// A boolean value indicating whether this character represents a operator. + /// + /// For example, the following characters all represent brackets: + /// + /// - "<" (U+003C LESS-THAN SIGN) + /// - ">" (U+003E GREATER-THAN SIGN + /// - "+" (U+002B PLUS SIGN) + /// - "-" (U+2212 MINUS SIGN) + /// - "\*" (U+002A ASTERSIK SIGN) + public var isOperator: Bool { + + if self == "\u{003C}" || self == "\u{003E}" || self == "\u{002B}" || self == "\u{2212}" || self == "\u{002A}" || self == "\u{007E}" { + return true + } + + return false + } +} diff --git a/Sources/Utilities/Minifier/Minifier.swift b/Sources/Utilities/Minifier/Minifier.swift new file mode 100644 index 00000000..df89deb4 --- /dev/null +++ b/Sources/Utilities/Minifier/Minifier.swift @@ -0,0 +1,110 @@ +import Foundation + +public struct Minifier { + + public struct Compression: OptionSet { + + public var rawValue: Int + + public static let stripComments = Compression(rawValue: 1 << 0) + public static let removeWhitespaces = Compression(rawValue: 1 << 1) + public static let mangleVariables = Compression(rawValue: 1 << 2) + public static let omitUnits = Compression(rawValue: 2 << 3) + + public init(rawValue: Int) { + self.rawValue = rawValue + } + } + + /// The level of compression + private var compression: [Compression] + + /// Initiates a minifier + public init(compression: [Compression]) { + + self.compression = compression + } + + /// Minifies a stylesheet string + public func minify(css content: String) -> String { + + var tokens = Stylesheet(log: .debug).consume(content) + + if compression.contains(.stripComments) { + tokens.removeAll(where: { $0 is Stylesheet.CommentToken }) + } + + var yield = [Token]() + + if compression.contains(.removeWhitespaces) { + + for (index, token) in tokens.enumerated() { + + if token is Stylesheet.WhitespaceToken { + + let previous = tokens.index(before: index) + let next = tokens.index(after: index) + + if previous >= tokens.startIndex && next < tokens.endIndex { + + // keep the whitespace if its between two selectors + if tokens[previous] is Stylesheet.SelectorToken && tokens[next] is Stylesheet.SelectorToken { + yield.append(token) + } + + // keep the whitespace if its between one selector and value token + if tokens[previous] is Stylesheet.SelectorToken && tokens[next] is Stylesheet.LiteralToken { + yield.append(token) + } + + // keep the whitespace if its between two value tokens + if tokens[previous] is Stylesheet.ValueToken && tokens[next] is Stylesheet.ValueToken { + yield.append(token) + } + } + + } else { + yield.append(token) + } + } + } + + return yield.map({ $0.present() }).joined() + } + + /// Minifies a javascript string + public func minify(js content: String) -> String { + + var tokens = Javascript().consume(content) + + if compression.contains(.stripComments) { + tokens.removeAll(where: { $0 is Javascript.CommentToken }) + } + + var yield = [Token]() + + if compression.contains(.removeWhitespaces) { + + for (index, token) in tokens.enumerated() { + + if token is Javascript.WhitespaceToken { + + let previous = tokens.index(before: index) + let next = tokens.index(after: index) + + if previous >= tokens.startIndex && next < tokens.endIndex { + + if tokens[previous] is Javascript.WordToken && tokens[next] is Javascript.WordToken { + yield.append(token) + } + } + + } else { + yield.append(token) + } + } + } + + return yield.map({ $0.present() }).joined() + } +} diff --git a/Sources/Utilities/Minifier/Tokenization/Javascript/Javascript.swift b/Sources/Utilities/Minifier/Tokenization/Javascript/Javascript.swift new file mode 100644 index 00000000..fcd7ace7 --- /dev/null +++ b/Sources/Utilities/Minifier/Tokenization/Javascript/Javascript.swift @@ -0,0 +1,695 @@ +internal class Javascript { + + /// A enumeration of different states of the minifier + /// + /// Code is the initial state. + internal enum InsertionMode: String { + + case code + case beforecomment + case linecomment + case blockcomment + case hashbang + case aftercomment + case word + case string + case template + case numeric + } + + /// A enumeration of different level of the logging + /// + /// None is the initial state. + internal enum LogLevel { + + case none + case debug + } + + /// The token collection + private var tokens: [Token] + + /// The temporary slot for a token + private var token: Token? + + /// The insertion mode of the minifier + private var mode: InsertionMode + + /// The level of logging + private var level: LogLevel + + /// Initiates a the javascript minifier + internal init(mode: InsertionMode = .code, log level: LogLevel = .none) { + + self.tokens = [] + self.mode = mode + self.level = level + } + + /// Logs the steps of the minifier depending on the log level + private func verbose(function: String, character: Character) { + + switch self.level { + + case .debug: + + if character.isNewline { + print(function, "newline") + + } else if character.isWhitespace { + print(function, "whitespace") + + } else { + print(function, character) + } + + default: + break + } + } + + /// Assigns a temporary token + private func assign(token: Token) { + + self.verbose(function: #function, character: " ") + + if self.token != nil { + fatalError("Cannot assign the token. The previous token needs to be emitted first.") + } + + self.token = token + } + + /// Emits a token into the token collection + private func emit(token: Token) { + + self.verbose(function: #function, character: " ") + + self.tokens.append(token) + } + + /// Emits the temporary token into the token collection + private func emit() { + + self.verbose(function: #function, character: " ") + + if let token = self.token { + self.tokens.append(token) + } + + self.token = nil + } + + /// Consumes the content + internal func consume(_ content: String) -> [Token] { + + for character in content.enumerated() { + + switch self.mode { + case .beforecomment: + self.mode = consumeBeforeComment(character.element) + + case .blockcomment: + self.mode = consumeBlockComment(character.element) + + case .linecomment: + self.mode = consumeLineComment(character.element) + + case .aftercomment: + self.mode = consumeAfterComment(character.element) + + case .word: + self.mode = consumeWord(character.element) + + case .string: + self.mode = consumeStringLiteral(character.element) + + case .template: + self.mode = consumeTemplateLiteral(character.element) + + case .numeric: + self.mode = consumeNumericLiteral(character.element) + + default: + self.mode = consumeCode(character.element) + } + } + + return self.tokens + } + + /// Consumes the character + internal func consumeCode(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isNewline { + + self.emit(token: WhitespaceToken(type: .return ,value: String(character))) + + return .code + } + + if character.isWhitespace { + + self.emit(token: WhitespaceToken(type: .whitespace, value: String(character))) + + return .code + } + + if character.isSemicolon || character.isColon { + + self.emit(token: FormatToken(type: .terminator, value: String(character))) + + return .code + } + + if character.isSolidus || character.isNumberSign { + // ignore character + return .beforecomment + } + + if character.isBracket || character.isEqualSign || character.isComma || character.isPeriod { + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + if character.isApostrophe { + + self.assign(token: LiteralToken(type: .string, value: "")) + + return .string + } + + if character.isBackTick { + + self.assign(token: LiteralToken(type: .template, value: "")) + + return .template + } + + if character.isNumber { + + self.assign(token: LiteralToken(type: .numeric, value: String(character))) + + return .numeric + } + + if character.isOperator { + + self.emit(token: FormatToken(type: .operator, value: String(character))) + + return .code + } + + if character.isLetter || character.isDollarSign || character.isUnderscore { + + self.assign(token: WordToken(value: String(character))) + + return .word + } + + return .code + } + + /// Consumes the character before the comment + internal func consumeBeforeComment(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isAsterisk { + + self.assign(token: CommentToken(type: .block, value: "")) + + // ignore character + return .blockcomment + } + + if character.isSolidus { + + self.assign(token: CommentToken(type: .line, value: "")) + + // ignore character + return .linecomment + } + + if character.isExclamationMark { + + self.assign(token: CommentToken(type: .hashbang, value: "")) + + // ignore character + return .hashbang + } + + // ignore character + return .beforecomment + } + + /// Consumes the character of a comment + internal func consumeLineComment(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isNewline { + + self.emit() + + // ignore character + return .code + } + + if var token = self.token { + token.value.append(character) + } + + return .linecomment + } + + /// Consumes the character of a comment + internal func consumeBlockComment(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isAsterisk { + + self.emit() + + // ignore character + return .aftercomment + } + + if var token = self.token { + token.value.append(character) + } + + return .blockcomment + } + + /// Consumes the character after a comment + internal func consumeAfterComment(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isSolidus { + // ignore character + return .code + } + + // ignore character + return .aftercomment + } + + internal func consumeWord(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isWhitespace { + + self.emit() + + self.emit(token: WhitespaceToken(type: .whitespace, value: "")) + + return .code + } + + if character.isPeriod { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + if character.isColon { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + if character.isSemicolon { + + self.emit() + + self.emit(token: FormatToken(type: .terminator, value: String(character))) + + return .code + } + + if character.isBracket { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + if var token = self.token { + token.value.append(character) + + } else { + self.assign(token: WordToken(value: String(character))) + } + + return .word + } + + /// Consumes the character of a string literal + internal func consumeStringLiteral(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isApostrophe { + + self.emit() + + return .code + } + + if var token = self.token { + token.value.append(character) + } + + return .string + } + + /// Consumes the character of a template literal + internal func consumeTemplateLiteral(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isBackTick { + + self.emit() + + return .code + } + + if var token = self.token { + token.value.append(character) + } + + return .template + } + + /// Consumes the character of a numeric literal + internal func consumeNumericLiteral(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isSemicolon { + + self.emit() + + self.emit(token: FormatToken(type: .terminator, value: String(character))) + + return .code + } + + if character.isBracket { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + if var token = self.token { + token.value.append(character) + } + + return .numeric + } +} + +extension Javascript { + + internal class WhitespaceToken: Token { + + /// A enumeration of the variations of comment tokens + internal enum TokenType { + + /// Indicates a single line comment + case whitespace + + /// Indicates a multiline comment + case `return` + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a comment token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + internal func present() -> String { + + switch type { + case .return: + return "\r\n" + + case .whitespace: + return " " + } + } + } + + /// A type that represents a javascript comment + internal class CommentToken: Token { + + /// A enumeration of the variations of comment tokens + internal enum TokenType { + + /// Indicates a single line comment + case line + + /// Indicates a multiline comment + case block + + /// Indicates a hashbang comment + case hashbang + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a comment token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a comment token + internal func present() -> String { + + switch type { + case .line: + return "//\(value)" + + case .block: + return "/*\(value)*/" + + case .hashbang: + return "#!\(value)" + } + } + } + + /// A type that represents a javascript literal + internal class LiteralToken: Token { + + /// A enumeration of the variations of literal tokens + internal enum TokenType { + + /// Indiciates a boolean literal + case boolean + + /// Indiciates a string literal + case string + + /// Indiciates a template literal + case template + + /// Indiciates a numeric literal + case numeric + + /// Indiciates a null value + case null + + /// Indiciates regular expression + case regularexpression + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a literal token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a literal token + internal func present() -> String { + + switch type { + case .string: + return "'\(value)'" + + case .template: + return "`\(value)`" + + default: + return value + } + } + } + + /// A type that represents a format control token + internal class FormatToken: Token { + + /// A enumeration of the variation of format tokens + internal enum TokenType { + + /// Indicates a punctiation + case punctuator + + /// Indicates a line terminator character + case terminator + + /// Indicates a line operator character + case `operator` + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a format token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a format token + internal func present() -> String { + return value + } + } + + /// A type that represents a word token + internal class WordToken: Token { + + /// A enumeration of the variations of word tokens + internal enum TokenType { + + /// Indicates a reserved word + case keyword + + /// Indicates a identifier + case identifier + } + + /// The type of the token + internal var type: TokenType { + + if !keywords.contains(value) { + return .identifier + } + + return .keyword + } + + /// The value of the token + internal var value: String + + /// Initiates a word token + internal init(value: String) { + + self.value = value + } + + /// Minifies a word token + internal func present() -> String { + return value + } + + /// A set of keywords + private var keywords: Set { + return [ + "await", + "break", + "case", + "catch", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "else", + "enum", + "export", + "extends", + "false", + "finally", + "for", + "function", + "if", + "implements", + "import", + "in", + "instanceof", + "interface", + "let", + "new", + "null", + "package", + "private", + "protected", + "public", + "return", + "super", + "switch", + "static", + "this", + "throw", + "try", + "true", + "typeof", + "var", + "void", + "while", + "with", + "yield", + "of" + ] + } + } +} diff --git a/Sources/Utilities/Minifier/Tokenization/Stylesheet/Stylesheet.swift b/Sources/Utilities/Minifier/Tokenization/Stylesheet/Stylesheet.swift new file mode 100644 index 00000000..5a4ba043 --- /dev/null +++ b/Sources/Utilities/Minifier/Tokenization/Stylesheet/Stylesheet.swift @@ -0,0 +1,1072 @@ +internal class Stylesheet { + + /// A enumeration of different states of the minifier + /// + /// Code is the initial state. + internal enum InsertionMode: String { + + case code + case beforecomment + case comment + case aftercomment + case selector + case property + case beforecustomproperty + case customproperty + case beforevalue + case value + case stringvalue + case unidentified + case afterunidentified + case string + case rule + case argument + } + + /// A enumeration of different level of the logging + /// + /// None is the initial state. + internal enum LogLevel { + + case none + case debug + } + + /// The tree with nodes + private var tokens: [Token] + + /// The temporary slot for a token + private var token: Token? + + private var cache: String? + + /// The state of the minifier + private var mode: InsertionMode + + /// The level of logging + private var level: LogLevel + + /// Creates a minifier + internal init(mode: InsertionMode = .code, log level: LogLevel = .none) { + + self.tokens = [] + self.mode = mode + self.level = level + } + + /// Logs the steps of the minifier depending on the log level + private func verbose(function: String, character: Character) { + + switch self.level { + + case .debug: + + if character.isNewline { + print(function, "newline") + + } else if character.isWhitespace { + print(function, "whitespace") + + } else { + print(function, character) + } + + default: + break + } + } + + /// Caches the character for later + private func cache(character: Character) { + + self.verbose(function: #function, character: " ") + + if var cache = self.cache { + + cache.append(character) + + self.cache = cache + + } else { + self.cache = String(character) + } + } + + /// Clears the cache + private func clear() -> String { + + self.verbose(function: #function, character: " ") + + guard let value = self.cache else { + fatalError("Wait, there is nothing to clear") + } + + self.cache = nil + + return value + } + + /// Assigns a temporary token + private func assign(token: Token) { + + self.verbose(function: #function, character: " ") + + if self.token != nil { + fatalError("Cannot assign the token. The previous token needs to be emitted first.") + } + + self.token = token + } + + /// Emits a token into the token collection + private func emit(token: Token) { + + self.verbose(function: #function, character: " ") + + self.tokens.append(token) + } + + /// Emits the temporary token into the token collection + private func emit() { + + self.verbose(function: #function, character: " ") + + if let token = self.token { + self.tokens.append(token) + } + + self.token = nil + } + + /// Collects the character for the token value + private func collect(_ character: Character) { + + if var token = self.token { + token.value.append(character) + } + } + + /// Consumes the content by the state the minifier is currently in + public func consume(_ content: String) -> [Token] { + + for character in content.enumerated() { + + switch self.mode { + + case .beforecomment: + self.mode = consumeBeforeComment(character.element) + + case .comment: + self.mode = consumeComment(character.element) + + case .aftercomment: + self.mode = consumeAfterComment(character.element) + + case .selector: + self.mode = consumeSelector(character.element) + + case .property: + self.mode = consumeProperty(character.element) + + case .beforecustomproperty: + self.mode = consumeBeforeCustomProperty(character.element) + + case .customproperty: + self.mode = consumeCustomProperty(character.element) + + case .beforevalue: + self.mode = consumeBeforeValue(character.element) + + case .value: + self.mode = consumeValue(character.element) + + case .stringvalue: + self.mode = consumeStringValue(character.element) + + case .unidentified: + self.mode = consumeUnkown(character.element) + + case .afterunidentified: + self.mode = consumeAfterUnkown(character.element) + + case .string: + self.mode = consumeStringLiteral(character.element) + + case .rule: + self.mode = consumeRule(character.element) + + case .argument: + self.mode = consumeArgument(character.element) + + default: + self.mode = consumeCode(character.element) + } + } + + return tokens + } + + /// Consumes the character + internal func consumeCode(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isNewline { + + self.emit(token: WhitespaceToken(type: .return ,value: String(character))) + + return .code + } + + if character.isWhitespace { + + self.emit(token: WhitespaceToken(type: .whitespace, value: String(character))) + + return .code + } + + if character.isSolidus { + // ignore character + return .beforecomment + } + + if character.isPeriod { + + self.assign(token: SelectorToken(type: .class, value: "")) + + return .selector + } + + if character.isColon { + + self.assign(token: SelectorToken(type: .root, value: "")) + + return .selector + } + + if character.isNumberSign { + + self.assign(token: SelectorToken(type: .id, value: "")) + + return .selector + } + + if character.isLetter { + + let leftCurlyBrackets = self.tokens.filter({ $0.value == "{" }) + let rightCurlyBrackets = self.tokens.filter({ $0.value == "}" }) + + if leftCurlyBrackets.count != rightCurlyBrackets.count { + + self.cache(character: character) + + return .unidentified + } + + self.assign(token: SelectorToken(type: .type, value: String(character))) + + return .selector + } + + if character.isQuotationMark { + + self.assign(token: LiteralToken(value: "")) + + return .string + } + + if character.isCommercialAt { + + self.assign(token: SelectorToken(type: .rule, value: "")) + + return .selector + } + + if character.isHyphenMinus { + // ignore character + return .beforecustomproperty + } + + if character.isLeftParenthesis { + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + self.assign(token: RuleToken(value: "")) + + return .rule + } + + if character.isLeftCurlyBracket || character.isRightCurlyBracket || character.isComma { + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + if character.isLeftSquareBracket { + + self.assign(token: SelectorToken(type: .attribute, value: String(character))) + + return .selector + } + + if character.isOperator { + + self.emit(token: FormatToken(type: .operator, value: String(character))) + + return .code + } + + return .code + } + + /// Consumes the character before the comment + internal func consumeBeforeComment(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isSolidus { + + self.assign(token: CommentToken(type: .line, value: "")) + + // ignore character + return .comment + } + + if character.isAsterisk { + + self.assign(token: CommentToken(type: .block, value: "")) + + // ignore character + return .comment + } + + // ignore character + return .beforecomment + } + + /// Consumes the character of a comment + /// + /// ```css + /// /* comment */ + /// ``` + internal func consumeComment(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isAsterisk { + + self.emit() + + return .aftercomment + } + + self.collect(character) + + return .comment + } + + /// Consumes the character after a comment + internal func consumeAfterComment(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isSolidus { + + return .code + } + + // ignore character + return .aftercomment + } + + /// Consumes a selector + /// + /// ```css + /// .selector { + /// } + /// ``` + internal func consumeSelector(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isLeftCurlyBracket { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + if character.isWhitespace { + + self.emit() + + self.emit(token: WhitespaceToken(type: .whitespace, value: "")) + + return .code + } + + self.collect(character) + + return .selector + } + + /// Consumes the character of a property + /// + /// ```css + /// property: value; + /// ``` + internal func consumeProperty(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isWhitespace { + + self.emit(token: WhitespaceToken(type: .whitespace, value: "")) + + return .property + } + + if character.isColon { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .beforevalue + } + + self.collect(character) + + return .property + } + + /// Consumes a character before a custom property + internal func consumeBeforeCustomProperty(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isHyphenMinus { + + self.assign(token: PropertyToken(type: .custom, value: "")) + + return .customproperty + } + + if character.isLetter { + + self.assign(token: PropertyToken(type: .browser, value: String(character))) + + return .property + } + + return .beforecustomproperty + } + + /// Consumes a character of a custom property + /// + /// ```css + /// --customproperty; value; + /// ``` + internal func consumeCustomProperty(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isWhitespace { + + self.emit(token: WhitespaceToken(type: .whitespace, value: "")) + + return .customproperty + } + + if character.isColon { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .beforevalue + } + + self.collect(character) + + return .customproperty + } + + /// Consumes the character in front of a value + internal func consumeBeforeValue(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isSemicolon { + + self.emit() + + self.emit(token: FormatToken(type: .terminator, value: String(character))) + + return .code + } + + if character.isQuotationMark { + + self.assign(token: ValueToken(type: .string, value: "")) + + return .stringvalue + } + + if character.isNumber { + + self.assign(token: ValueToken(type: .numeric, value: String(character))) + + return .value + } + + if character.isLetter { + + self.assign(token: ValueToken(type: .keyword, value: String(character))) + + return .value + } + + if character.isExclamationMark { + + self.assign(token: ValueToken(type: .rule, value: String(character))) + + return .value + } + + return .beforevalue + } + + /// Consumes the character of a value + /// + /// ```css + /// property: value; + /// ``` + internal func consumeValue(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isSemicolon { + + self.emit() + + self.emit(token: FormatToken(type: .terminator, value: String(character))) + + return .code + } + + if character.isLeftParenthesis { + + self.emit() + + self.emit(token: FormatToken(type: .parenthesis, value: String(character))) + + self.assign(token: ValueToken(type: .function, value: "")) + + return .argument + } + + if character.isWhitespace { + + self.emit() + + self.emit(token: WhitespaceToken(type: .whitespace, value: String(character))) + + return .beforevalue + } + + if character.isComma { + + self.emit() + + self.emit(token: FormatToken(type: .terminator, value: String(character))) + + return .beforevalue + } + + self.collect(character) + + return .value + } + + /// Consumes a character of a string value + internal func consumeStringValue(_ character: Character) -> InsertionMode { + + if character.isQuotationMark { + + self.emit() + + return .value + } + + self.collect(character) + + return .stringvalue + } + + /// Consumes a unidentified character sequence + internal func consumeUnkown(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isColon { + + self.cache(character: character) + + return .afterunidentified + } + + if character.isWhitespace || character.isLeftCurlyBracket { + + self.emit(token: SelectorToken(type: .type, value: clear())) + + self.emit(token: WhitespaceToken(type: .whitespace, value: "")) + + return .code + } + + self.cache(character: character) + + return .unidentified + } + + func consumeAfterUnkown(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + // its a property + if character.isWhitespace { + + var cache = clear() + + // since we know its property now, take the colon + let colon: Character = cache.removeLast() + + self.emit(token: PropertyToken(type: .regular, value: cache)) + + self.emit(token: FormatToken(type: .terminator, value: String(colon))) + + return .beforevalue + } + + // its a pseudo element + if character.isColon { + + self.cache(character: character) + + return .unidentified + } + + // its a pseudo selector + if character.isLetter { + + self.cache(character: character) + + return .unidentified + } + + return .afterunidentified + } + + /// Consumes a chracter for a string literal + /// + /// ```css + /// "string" + /// ``` + /// + internal func consumeStringLiteral(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isQuotationMark { + + self.emit() + + return .code + } + + self.collect(character) + + return .string + } + + /// Consumes a character for a rule selector + /// + /// ```css + /// @rule { + /// } + /// ``` + internal func consumeRule(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isRightParenthesis { + + self.emit() + + self.emit(token: FormatToken(type: .punctuator, value: String(character))) + + return .code + } + + self.collect(character) + + return .rule + } + + /// Consumes a character of an function argument + /// + /// ```css + /// function(argument); + /// ``` + internal func consumeArgument(_ character: Character) -> InsertionMode { + + self.verbose(function: #function, character: character) + + if character.isRightParenthesis { + + let leftParenthesis = self.tokens.filter({ $0.value == "(" }) + let rightParenthesis = self.tokens.filter({ $0.value == ")" }) + + if (rightParenthesis.count + 1) != leftParenthesis.count { + + self.emit() + + self.emit(token: FormatToken(type: .parenthesis, value: String(character))) + + self.assign(token: ValueToken(type: .function, value: "")) + + return .argument + } + + self.emit() + + self.emit(token: FormatToken(type: .parenthesis, value: String(character))) + + return .beforevalue + } + + if character.isLeftParenthesis { + + self.emit() + + self.emit(token: FormatToken(type: .parenthesis, value: String(character))) + + self.assign(token: ValueToken(type: .function, value: "")) + + return .argument + } + + self.collect(character) + + return .argument + } +} + +extension Stylesheet { + + internal class WhitespaceToken: Token { + + /// A enumeration of the variations of comment tokens + internal enum TokenType { + + /// Indicates a single line comment + case whitespace + + /// Indicates a multiline comment + case `return` + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a comment token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + internal func present() -> String { + + switch type { + case .return: + return "\r\n" + + case .whitespace: + return " " + } + } + } + + /// A type that represents a css comment + internal class CommentToken: Token { + + /// A enumeration of the variations of comment tokens + internal enum TokenType { + + /// Indicates a single line comment + case line + + /// Indicates a multiline comment + case block + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a comment token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a comment token + internal func present() -> String { + + switch type { + case .line: + return "//\(value)" + + case .block: + return "/*\(value)*/" + } + } + } + + /// A type that represents a css comment + internal class SelectorToken: Token { + + /// A enumeration of the variations of comment tokens + internal enum TokenType { + + /// Indicates the set is about an type + case type + + /// Indicates the set is about a class + case `class` + + /// Indicates the set is about a id + case id + + /// Indicates the set is about a root + case root + + /// Indicates a universal selector + case universal + + /// Indicates a attribute selector + case attribute + + /// Indicates a rule selector + case rule + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a comment token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a comment token + internal func present() -> String { + + switch type { + case .type: + return "\(value)" + + case .class: + return ".\(value)" + + case .id: + return "#\(value)" + + case .root: + return ":\(value)" + + case .universal: + return value + + case .attribute: + return value + + case .rule: + return "@\(value)" + } + } + } + + /// A type that represents a format control token + internal class FormatToken: Token { + + /// A enumeration of the variation of format tokens + internal enum TokenType { + + /// Indicates a punctiation + case punctuator + + /// Indicates a line terminator character + case terminator + + /// Indicates a line operator character + case `operator` + + /// Indicates a parenthesis + case parenthesis + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a format token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a format token + internal func present() -> String { + return value + } + } + + /// A type that represents a format control token + internal class PropertyToken: Token { + + /// A enumeration of the variation of format tokens + internal enum TokenType { + + /// Indicates a regular property + case regular + + /// Indicates a custom property + case custom + + /// Indicates a browser property + case browser + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a format token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a format token + internal func present() -> String { + + switch type { + case .regular: + return "\(value)" + + case .browser: + return "-\(value)" + + case .custom: + return "--\(value)" + } + } + } + + /// A type that represents a format control token + internal class ValueToken: Token { + + /// A enumeration of the variation of format tokens + internal enum TokenType { + + /// Indicates a punctiation + case keyword + + /// Indicates a line terminator character + case numeric + + /// Indicates a function + case function + + /// Indicates a string + case string + + /// indicates a rule + case rule + } + + /// The type of the token + internal var type: TokenType + + /// The value of the token + internal var value: String + + /// Initiates a format token + internal init(type: TokenType, value: String) { + + self.type = type + self.value = value + } + + /// Minifies a format token + internal func present() -> String { + + switch type { + case .string: + return "\"\(value)\"" + + default: + return value + } + } + } + + internal class LiteralToken: Token { + + internal var value: String + + internal init(value: String) { + self.value = value + } + + internal func present() -> String { + return "\"\(value)\"" + } + } + + internal class RuleToken: Token { + + internal var value: String + + internal init(value: String) { + self.value = value + } + + internal func present() -> String { + return value + } + } +} diff --git a/Sources/Utilities/Minifier/Tokenization/Token.swift b/Sources/Utilities/Minifier/Tokenization/Token.swift new file mode 100644 index 00000000..a0de49cf --- /dev/null +++ b/Sources/Utilities/Minifier/Tokenization/Token.swift @@ -0,0 +1,7 @@ +/// A Type that represents a javascript token +internal protocol Token { + + var value: String { get set } + + func present() -> String +} diff --git a/Tests/HTMLKitComponentsTests/ComponentTests.swift b/Tests/HTMLKitComponentsTests/ComponentTests.swift index 75e8469a..8423add0 100644 --- a/Tests/HTMLKitComponentsTests/ComponentTests.swift +++ b/Tests/HTMLKitComponentsTests/ComponentTests.swift @@ -21,7 +21,7 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - Button + Button """ ) } @@ -120,7 +120,7 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - + """ ) } @@ -135,7 +135,7 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - + """ ) } @@ -148,7 +148,7 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - + """ ) } @@ -161,7 +161,40 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - +
    \ + \ +
    \ +
      \ +
    • \ + \ +
    • \ +
    • \ + \ +
    • \ +
    • \ + \ +
    • \ +
    \ +
      \ +
    • Sun
    • \ +
    • Mon
    • \ +
    • Tue
    • \ +
    • Wed
    • \ +
    • Thu
    • \ +
    • Fri
    • \ +
    • Sat
    • \ +
    \ +
      \ +
      \ +
      """ ) } @@ -174,7 +207,7 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - + """ ) } @@ -182,12 +215,15 @@ final class ComponentTests: XCTestCase { func testCheckField() throws { let view = TestView { - CheckField(name: "name", value: "value") + CheckField(name: "name", value: "value") { + "Label" + } } XCTAssertEqual(try renderer.render(view: view), """ - + \ + """ ) } @@ -195,12 +231,15 @@ final class ComponentTests: XCTestCase { func testRadioSelect() throws { let view = TestView { - RadioSelect(name: "name", value: "value") + RadioSelect(name: "name", value: "value") { + "Label" + } } XCTAssertEqual(try renderer.render(view: view), """ - + \ + """ ) } @@ -214,8 +253,10 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - +
      \ + \ +
      \ +
      """ ) } @@ -228,9 +269,7 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ -
      \ - \ -
      + """ ) } @@ -273,7 +312,7 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ - Link + Link """ ) } @@ -387,22 +426,6 @@ final class ComponentTests: XCTestCase { ) } - func testToggle() throws { - - let view = TestView { - Toggle(name: "name") - } - - XCTAssertEqual(try renderer.render(view: view), - """ - - """ - ) - } - func testCard() throws { let view = TestView { @@ -481,9 +504,8 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ -