From c811b23726af68162bf0c4d3870f7c881b574b8c Mon Sep 17 00:00:00 2001 From: Mattes Mohr Date: Tue, 28 Feb 2023 20:11:06 +0100 Subject: [PATCH] Upgrade the package (#119) * Make the storage manager public To make the environment for htmlkit work, the manager needs to be cached in the application storage. * Add the press modifier to the text component * Add the environment value to the content rendering In a specific case the environment value didn't get caught by the renderer. * Fix the styling of the divider component * Add a dropdown component * Revise the card component * Remove the css from the grid item The grid should only be for alignment. * Add a scrollview component * Add a carousel component * Extend the carousel component * Add more component tests * Fix test failing * Add motion to the carousel by adding javascript * Add autoplay to the carousel * Fix tests * Add a test for the symbol component * Add the possibility to disable views * Fix the css classes for the position index * Add a few new symbols * Add more symbols * Add the possibility to hide views * Revise the input fields to accept a placeholder value * Add the submit event to the form component * Add comments * Refactor code and rearrange files * Add form validation * Fix some sort of things * Change the js initialization for the carousel component * Add a textpad component --- Package.swift | 3 +- Sources/HTMLKit/Abstraction/README.md | 10 + .../{Core => }/Builders/ContentBuilder.swift | 0 .../{Storage => }/Environment.swift | 0 .../{Storage => }/EnvironmentKeys.swift | 0 .../{Storage => }/EnvironmentModifier.swift | 0 .../{Storage => }/EnvironmentObject.swift | 0 .../{Storage => }/EnvironmentValue.swift | 0 .../Environment/{Storage => }/Manager.swift | 10 +- .../Extensions/Datatypes+Content.swift | 0 .../{Core => }/Helpers/Geometrics.swift | 0 .../Localization/Localizable.swift | 0 .../Localization/LocalizedStringKey.swift | 0 .../Primitives/Attributes/Attribute.swift | 0 .../Primitives/Elements/Element.swift | 0 .../Primitives/Elements/Elements.swift | 0 .../Primitives/Layouts/Layouts.swift | 0 .../{Core => }/Primitives/Nodes/Node.swift | 0 .../{Core => }/Primitives/Nodes/Nodes.swift | 0 .../Primitives/Shared/Content.swift | 0 Sources/HTMLKit/Framework/README.md | 17 ++ .../Framework/Rendering/Renderer.swift | 12 +- Sources/HTMLKitComponents/Actions.swift | 49 +++- .../HTMLKitComponents/Components/Button.swift | 18 ++ .../HTMLKitComponents/Components/Card.swift | 44 +++- .../Components/Carousel.swift | 114 +++++++++ .../Components/Dropdown.swift | 50 ++++ .../HTMLKitComponents/Components/Form.swift | 227 +++++++++++++++++- .../HTMLKitComponents/Components/Grid.swift | 2 +- .../HTMLKitComponents/Components/Group.swift | 1 + .../HTMLKitComponents/Components/Image.swift | 6 +- .../HTMLKitComponents/Components/Modal.swift | 19 ++ .../Components/ScrollView.swift | 37 +++ .../HTMLKitComponents/Components/Stack.swift | 14 +- .../HTMLKitComponents/Components/Symbol.swift | 5 +- .../HTMLKitComponents/Components/Text.swift | 15 ++ .../HTMLKitComponents/Components/Toggle.swift | 2 +- Sources/HTMLKitComponents/Events.swift | 23 +- .../Modifiers/ButtonModifier.swift | 28 +-- .../Modifiers/DragModifier.swift | 9 + .../Modifiers/FormModifier.swift | 23 ++ .../Modifiers/HoverModifier.swift | 9 + .../Modifiers/ImageModifier.swift | 16 +- .../Modifiers/InputModifier.swift | 18 +- .../Modifiers/PressModifier.swift | 10 + .../Modifiers/TextModifier.swift | 37 +-- .../Modifiers/ViewModifier.swift | 23 +- .../Properties/Actionable.swift | 7 + .../Properties/Identifiable.swift | 7 +- .../Properties/Modifiable.swift | 3 + .../HTMLKitComponents/Resources/css/card.css | 31 ++- .../Resources/css/carousel.css | 73 ++++++ .../Resources/css/divider.css | 2 + .../Resources/css/dropdown.css | 33 +++ .../HTMLKitComponents/Resources/css/form.css | 5 +- .../HTMLKitComponents/Resources/css/grid.css | 9 - .../HTMLKitComponents/Resources/css/roots.css | 42 +++- .../Resources/css/scrollview.css | 29 +++ .../HTMLKitComponents/Resources/css/utils.css | 31 ++- Sources/HTMLKitComponents/Resources/js/all.js | 132 +++++++--- .../Resources/js/carousel.js | 92 +++++++ .../HTMLKitComponents/Resources/js/textpad.js | 67 ++++++ .../Resources/symbols/chart.pie.svg | 3 + .../Resources/symbols/clock.svg | 3 + .../Resources/symbols/envelope.svg | 3 + .../Resources/symbols/file.svg | 3 + .../Resources/symbols/folder.svg | 12 +- .../Resources/symbols/house.svg | 3 + .../Resources/symbols/lock.svg | 3 + .../Resources/symbols/person.svg | 3 + .../Resources/symbols/photo.svg | 3 + .../Resources/symbols/text.aligncenter.svg | 3 + .../Resources/symbols/text.alignjustify.svg | 3 + .../Resources/symbols/text.alignleft.svg | 3 + .../Resources/symbols/text.alignright.svg | 3 + Sources/HTMLKitComponents/Tokens.swift | 34 ++- Sources/HTMLKitComponents/Validator.swift | 41 ++++ Sources/HTMLKitVapor/Environment.swift | 25 ++ .../Extensions/Vapor+HTMLKit.swift | 43 +++- ...Configuration.swift => Localization.swift} | 14 +- Sources/HTMLKitVapor/ViewRenderer.swift | 4 +- .../ComponentTests.swift | 148 +++++++++++- Tests/HTMLKitVaporTests/ProviderTests.swift | 4 +- 83 files changed, 1522 insertions(+), 253 deletions(-) create mode 100644 Sources/HTMLKit/Abstraction/README.md rename Sources/HTMLKit/Framework/{Core => }/Builders/ContentBuilder.swift (100%) rename Sources/HTMLKit/Framework/Environment/{Storage => }/Environment.swift (100%) rename Sources/HTMLKit/Framework/Environment/{Storage => }/EnvironmentKeys.swift (100%) rename Sources/HTMLKit/Framework/Environment/{Storage => }/EnvironmentModifier.swift (100%) rename Sources/HTMLKit/Framework/Environment/{Storage => }/EnvironmentObject.swift (100%) rename Sources/HTMLKit/Framework/Environment/{Storage => }/EnvironmentValue.swift (100%) rename Sources/HTMLKit/Framework/Environment/{Storage => }/Manager.swift (68%) rename Sources/HTMLKit/Framework/{Core => }/Extensions/Datatypes+Content.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Helpers/Geometrics.swift (100%) rename Sources/HTMLKit/Framework/{Environment => }/Localization/Localizable.swift (100%) rename Sources/HTMLKit/Framework/{Environment => }/Localization/LocalizedStringKey.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Primitives/Attributes/Attribute.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Primitives/Elements/Element.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Primitives/Elements/Elements.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Primitives/Layouts/Layouts.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Primitives/Nodes/Node.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Primitives/Nodes/Nodes.swift (100%) rename Sources/HTMLKit/Framework/{Core => }/Primitives/Shared/Content.swift (100%) create mode 100644 Sources/HTMLKit/Framework/README.md create mode 100644 Sources/HTMLKitComponents/Components/Carousel.swift create mode 100644 Sources/HTMLKitComponents/Components/Dropdown.swift create mode 100644 Sources/HTMLKitComponents/Components/ScrollView.swift create mode 100644 Sources/HTMLKitComponents/Modifiers/FormModifier.swift create mode 100644 Sources/HTMLKitComponents/Resources/css/carousel.css create mode 100644 Sources/HTMLKitComponents/Resources/css/dropdown.css create mode 100644 Sources/HTMLKitComponents/Resources/css/scrollview.css create mode 100644 Sources/HTMLKitComponents/Resources/js/carousel.js create mode 100644 Sources/HTMLKitComponents/Resources/js/textpad.js create mode 100644 Sources/HTMLKitComponents/Resources/symbols/chart.pie.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/clock.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/envelope.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/file.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/house.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/lock.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/person.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/photo.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/text.aligncenter.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/text.alignjustify.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/text.alignleft.svg create mode 100644 Sources/HTMLKitComponents/Resources/symbols/text.alignright.svg create mode 100644 Sources/HTMLKitComponents/Validator.swift create mode 100644 Sources/HTMLKitVapor/Environment.swift rename Sources/HTMLKitVapor/{LingoConfiguration.swift => Localization.swift} (78%) diff --git a/Package.swift b/Package.swift index d97ee005..c80cdbac 100644 --- a/Package.swift +++ b/Package.swift @@ -33,7 +33,8 @@ let package = Package( dependencies: [ .product(name: "Lingo", package: "Lingo"), .product(name: "Collections", package: "swift-collections") - ] + ], + exclude: ["Abstraction/README.md", "Framework/README.md"] ), .target( name: "HTMLKitConverter", diff --git a/Sources/HTMLKit/Abstraction/README.md b/Sources/HTMLKit/Abstraction/README.md new file mode 100644 index 00000000..388e4a3b --- /dev/null +++ b/Sources/HTMLKit/Abstraction/README.md @@ -0,0 +1,10 @@ +# Abstraction + +This directory contains the HTML abstraction. + +### Attributes + +### Elements + +### Tokens + diff --git a/Sources/HTMLKit/Framework/Core/Builders/ContentBuilder.swift b/Sources/HTMLKit/Framework/Builders/ContentBuilder.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Builders/ContentBuilder.swift rename to Sources/HTMLKit/Framework/Builders/ContentBuilder.swift diff --git a/Sources/HTMLKit/Framework/Environment/Storage/Environment.swift b/Sources/HTMLKit/Framework/Environment/Environment.swift similarity index 100% rename from Sources/HTMLKit/Framework/Environment/Storage/Environment.swift rename to Sources/HTMLKit/Framework/Environment/Environment.swift diff --git a/Sources/HTMLKit/Framework/Environment/Storage/EnvironmentKeys.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentKeys.swift similarity index 100% rename from Sources/HTMLKit/Framework/Environment/Storage/EnvironmentKeys.swift rename to Sources/HTMLKit/Framework/Environment/EnvironmentKeys.swift diff --git a/Sources/HTMLKit/Framework/Environment/Storage/EnvironmentModifier.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentModifier.swift similarity index 100% rename from Sources/HTMLKit/Framework/Environment/Storage/EnvironmentModifier.swift rename to Sources/HTMLKit/Framework/Environment/EnvironmentModifier.swift diff --git a/Sources/HTMLKit/Framework/Environment/Storage/EnvironmentObject.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentObject.swift similarity index 100% rename from Sources/HTMLKit/Framework/Environment/Storage/EnvironmentObject.swift rename to Sources/HTMLKit/Framework/Environment/EnvironmentObject.swift diff --git a/Sources/HTMLKit/Framework/Environment/Storage/EnvironmentValue.swift b/Sources/HTMLKit/Framework/Environment/EnvironmentValue.swift similarity index 100% rename from Sources/HTMLKit/Framework/Environment/Storage/EnvironmentValue.swift rename to Sources/HTMLKit/Framework/Environment/EnvironmentValue.swift diff --git a/Sources/HTMLKit/Framework/Environment/Storage/Manager.swift b/Sources/HTMLKit/Framework/Environment/Manager.swift similarity index 68% rename from Sources/HTMLKit/Framework/Environment/Storage/Manager.swift rename to Sources/HTMLKit/Framework/Environment/Manager.swift index 2a34b1ae..0927214b 100644 --- a/Sources/HTMLKit/Framework/Environment/Storage/Manager.swift +++ b/Sources/HTMLKit/Framework/Environment/Manager.swift @@ -1,19 +1,19 @@ import Foundation /// A type, that manages the environment storage -internal class Manager { +public class Manager { /// The storage of the environment - internal var storage: [AnyKeyPath: Any] + private var storage: [AnyKeyPath: Any] /// Initiates a manager - internal init() { + public init() { self.storage = [:] } /// Retrieves an item from storage by its path - internal func retrieve(for path: AnyKeyPath) -> Any? { + public func retrieve(for path: AnyKeyPath) -> Any? { if let value = self.storage[path] { return value @@ -23,7 +23,7 @@ internal class Manager { } /// Adds und updates an item to the storage - internal func upsert(_ value: T, for path: AnyKeyPath) { + public func upsert(_ value: T, for path: AnyKeyPath) { self.storage[path] = value } } diff --git a/Sources/HTMLKit/Framework/Core/Extensions/Datatypes+Content.swift b/Sources/HTMLKit/Framework/Extensions/Datatypes+Content.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Extensions/Datatypes+Content.swift rename to Sources/HTMLKit/Framework/Extensions/Datatypes+Content.swift diff --git a/Sources/HTMLKit/Framework/Core/Helpers/Geometrics.swift b/Sources/HTMLKit/Framework/Helpers/Geometrics.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Helpers/Geometrics.swift rename to Sources/HTMLKit/Framework/Helpers/Geometrics.swift diff --git a/Sources/HTMLKit/Framework/Environment/Localization/Localizable.swift b/Sources/HTMLKit/Framework/Localization/Localizable.swift similarity index 100% rename from Sources/HTMLKit/Framework/Environment/Localization/Localizable.swift rename to Sources/HTMLKit/Framework/Localization/Localizable.swift diff --git a/Sources/HTMLKit/Framework/Environment/Localization/LocalizedStringKey.swift b/Sources/HTMLKit/Framework/Localization/LocalizedStringKey.swift similarity index 100% rename from Sources/HTMLKit/Framework/Environment/Localization/LocalizedStringKey.swift rename to Sources/HTMLKit/Framework/Localization/LocalizedStringKey.swift diff --git a/Sources/HTMLKit/Framework/Core/Primitives/Attributes/Attribute.swift b/Sources/HTMLKit/Framework/Primitives/Attributes/Attribute.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Primitives/Attributes/Attribute.swift rename to Sources/HTMLKit/Framework/Primitives/Attributes/Attribute.swift diff --git a/Sources/HTMLKit/Framework/Core/Primitives/Elements/Element.swift b/Sources/HTMLKit/Framework/Primitives/Elements/Element.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Primitives/Elements/Element.swift rename to Sources/HTMLKit/Framework/Primitives/Elements/Element.swift diff --git a/Sources/HTMLKit/Framework/Core/Primitives/Elements/Elements.swift b/Sources/HTMLKit/Framework/Primitives/Elements/Elements.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Primitives/Elements/Elements.swift rename to Sources/HTMLKit/Framework/Primitives/Elements/Elements.swift diff --git a/Sources/HTMLKit/Framework/Core/Primitives/Layouts/Layouts.swift b/Sources/HTMLKit/Framework/Primitives/Layouts/Layouts.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Primitives/Layouts/Layouts.swift rename to Sources/HTMLKit/Framework/Primitives/Layouts/Layouts.swift diff --git a/Sources/HTMLKit/Framework/Core/Primitives/Nodes/Node.swift b/Sources/HTMLKit/Framework/Primitives/Nodes/Node.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Primitives/Nodes/Node.swift rename to Sources/HTMLKit/Framework/Primitives/Nodes/Node.swift diff --git a/Sources/HTMLKit/Framework/Core/Primitives/Nodes/Nodes.swift b/Sources/HTMLKit/Framework/Primitives/Nodes/Nodes.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Primitives/Nodes/Nodes.swift rename to Sources/HTMLKit/Framework/Primitives/Nodes/Nodes.swift diff --git a/Sources/HTMLKit/Framework/Core/Primitives/Shared/Content.swift b/Sources/HTMLKit/Framework/Primitives/Shared/Content.swift similarity index 100% rename from Sources/HTMLKit/Framework/Core/Primitives/Shared/Content.swift rename to Sources/HTMLKit/Framework/Primitives/Shared/Content.swift diff --git a/Sources/HTMLKit/Framework/README.md b/Sources/HTMLKit/Framework/README.md new file mode 100644 index 00000000..7ec64b69 --- /dev/null +++ b/Sources/HTMLKit/Framework/README.md @@ -0,0 +1,17 @@ +# Framework + +This directory contains sources for + +### Builders + +### Environment + +### Extensions + +### Helpers + +### Localization + +### Primitives + +### Rendering diff --git a/Sources/HTMLKit/Framework/Rendering/Renderer.swift b/Sources/HTMLKit/Framework/Rendering/Renderer.swift index 568112cd..9e769183 100644 --- a/Sources/HTMLKit/Framework/Rendering/Renderer.swift +++ b/Sources/HTMLKit/Framework/Rendering/Renderer.swift @@ -54,8 +54,12 @@ public class Renderer { self.lingo = lingo } - public func add(model: T) where T: Encodable { - manager.upsert(model, for: \T.self) + /// Initiates the renderer. + public init(lingo: Lingo? = nil, manager: Manager) { + + self.environment = Environment() + self.manager = manager + self.lingo = lingo } /// Renders a view @@ -112,6 +116,10 @@ public class Renderer { result += try render(modifier: modifier) } + if let value = content as? EnvironmentValue { + result += try render(value: value) + } + if let element = content as? String { result += element } diff --git a/Sources/HTMLKitComponents/Actions.swift b/Sources/HTMLKitComponents/Actions.swift index aed9d6b2..f4d19532 100644 --- a/Sources/HTMLKitComponents/Actions.swift +++ b/Sources/HTMLKitComponents/Actions.swift @@ -1,16 +1,42 @@ /* Abstract: - The file contains the actions for the components. + The file contains the action stencils for the components. */ +import Foundation + +/// A collection of actions, that can be triggered by events. public enum Actions { + /// Shows the target. case show(_ target: String) + + /// Hides the target. case hide(_ target: String) + + /// Animates the target. case animate(_ target: String) + + /// Opens the target. case open(_ target: String) + + /// Closes the target. case close(_ target: String) + /// Validates the form. + case valdiate(_ target: String, _ rules: [Validator]) + + public var description: String { + + switch self { + case .valdiate(_, _): + return "validate" + default: + return "default" + } + } + + /// The script for the action. public var script: String { switch self { @@ -28,26 +54,47 @@ public enum Actions { case .close(let target): return close(target) + + case .valdiate(let target, let rules): + return validate(target, rules) } } + /// Returns a show action stencil. private func show(_ target: String) -> String { return "$('#\(target)').show();" } + /// Returns a hide action stencil. private func hide(_ target: String) -> String { return "$('#\(target)').hide();" } + /// Returns a animate action stencil. private func animate(_ target: String) -> String { return "$('#\(target)').animate();" } + /// Returns a open action stencil. private func open(_ target: String) -> String { return "$('#\(target)').open();" } + /// Returns a close action stencil. private func close(_ target: String) -> String { return "$('#\(target)').close();" } + + /// Returns a close action stencil. + private func validate(_ target: String, _ validators: [Validator]) -> String { + + if let data = try? JSONEncoder().encode(validators) { + + if let result = String(data: data, encoding: .utf8) { + return "$('#\(target)').validate('\(result)');" + } + } + + return "$('#\(target)').validate('[]');" + } } diff --git a/Sources/HTMLKitComponents/Components/Button.swift b/Sources/HTMLKitComponents/Components/Button.swift index c42f56a7..5a771bf3 100644 --- a/Sources/HTMLKitComponents/Components/Button.swift +++ b/Sources/HTMLKitComponents/Components/Button.swift @@ -77,6 +77,15 @@ extension Button: ButtonModifier { 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 + } } extension Button: PressModifier { @@ -153,4 +162,13 @@ extension LinkButton: ButtonModifier { 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 + } } diff --git a/Sources/HTMLKitComponents/Components/Card.swift b/Sources/HTMLKitComponents/Components/Card.swift index 2f73d355..4e8ace51 100644 --- a/Sources/HTMLKitComponents/Components/Card.swift +++ b/Sources/HTMLKitComponents/Components/Card.swift @@ -1,21 +1,57 @@ +/* + Abstract: + The file contains a card component. + */ + import HTMLKit -public struct Card: View, Modifiable { +/// A component that distinguish content. +public class Card: View { + + /// The header of the card. + public var header: [Content]? - internal var content: [Content] + /// The content of the card. + public var content: [Content] + /// The classes of the content. internal var classes: [String] + /// Creates a card. public init(@ContentBuilder content: () -> [Content]) { self.content = content() self.classes = ["card"] } + /// Creates a card. + public init(@ContentBuilder content: () -> [Content], + @ContentBuilder header: () -> [Content]) { + + self.content = content() + self.header = header() + self.classes = ["card"] + } + + /// Creates a card. + internal init(header: [Content]?, content: [Content], classes: [String]) { + + self.header = header + self.content = content + self.classes = classes + } + public var body: Content { Division { - content + Division { + header + } + .class("card-header") + Division { + content + } + .class("card-body") } - .class(self.classes.joined(separator: " ")) + .class(classes.joined(separator: " ")) } } diff --git a/Sources/HTMLKitComponents/Components/Carousel.swift b/Sources/HTMLKitComponents/Components/Carousel.swift new file mode 100644 index 00000000..c3c484e0 --- /dev/null +++ b/Sources/HTMLKitComponents/Components/Carousel.swift @@ -0,0 +1,114 @@ +/* + Abstract: + The file contains a carousel component. + */ + +import HTMLKit + +/// A compnonent that cycles through an amount of views. +public struct Carousel: View { + + /// The indication for the carousel. + internal var indication: [Content] + + /// The content of the carousel. + internal var content: [Content] + + /// The classes of the carousel. + internal var classes: [String] + + /// Creates a carousel. + public init(@ContentBuilder content: () -> [Content], + @ContentBuilder indication: () -> [Content]) { + + self.content = content() + self.indication = indication() + self.classes = ["carousel"] + } + + /// Creates a carousel. + internal init(indication: [Content], content: [Content], classes: [String]) { + + self.indication = indication + self.content = content + self.classes = classes + } + + public var body: Content { + Division { + Division { + content + } + .class("carousel-content") + Division { + indication + } + .class("carousel-indication") + } + .class(classes.joined(separator: " ")) + } +} + +public struct Slide: View, Identifiable, Modifiable { + + internal var id: String? + + internal var source: String + + internal var classes: [String] + + internal var caption: [Content] + + public init(source: String, @ContentBuilder caption: () -> [Content]) { + + self.source = source + self.caption = caption() + self.classes = ["slide"] + } + + internal init(id: String?, source: String, caption: [Content], classes: [String]) { + + self.id = id + self.source = source + self.caption = caption + self.classes = classes + } + + public var body: Content { + Division { + Division { + HTMLKit.Image() + .source(source) + } + .class("slide-thumbnail") + Division { + caption + } + .class("slide-caption") + } + .class(classes.joined(separator: " ")) + .modify(unwrap: id) { + $0.id($1) + } + } + + public func tag(_ value: String) -> Slide { + return self.mutate(id: value) + } +} + +public struct Indicator: View { + + internal var tag: String + + public init(for tag: String) { + self.tag = "#" + tag + } + + public var body: Content { + Anchor { + } + .class("indicator") + .reference(tag) + } +} diff --git a/Sources/HTMLKitComponents/Components/Dropdown.swift b/Sources/HTMLKitComponents/Components/Dropdown.swift new file mode 100644 index 00000000..53ef62ff --- /dev/null +++ b/Sources/HTMLKitComponents/Components/Dropdown.swift @@ -0,0 +1,50 @@ +/* + Abstract: + The file contains a dropdown component. + */ + +import HTMLKit + +/// A component that displays a list of actions. +public struct Dropdown: View { + + /// The label for the dropdown. + internal var label: [Content] + + /// The content of the dropdown. + internal var content: [Content] + + /// The classes of the dropdown. + internal var classes: [String] + + /// Creates a dropdown. + public init(@ContentBuilder content: () -> [Content], @ContentBuilder label: () -> [Content]) { + + self.label = label() + self.content = content() + self.classes = ["dropdown"] + } + + /// Creates a dropdown. + internal init(label: [Content], content: [Content], classes: [String]) { + + self.label = label + self.content = content + self.classes = classes + } + + public var body: Content { + Division { + Division { + label + } + .class("dropdown-label") + Division { + content + } + .class("dropdown-content") + } + .class(classes.joined(separator: " ")) + .tabIndex(1) + } +} diff --git a/Sources/HTMLKitComponents/Components/Form.swift b/Sources/HTMLKitComponents/Components/Form.swift index 3a5210fb..51093492 100644 --- a/Sources/HTMLKitComponents/Components/Form.swift +++ b/Sources/HTMLKitComponents/Components/Form.swift @@ -1,12 +1,15 @@ /* Abstract: - The file contains everything related to form. + The file contains everything related to form component. */ import HTMLKit /// A component that collects form controls. -public struct Form: View { +public struct Form: View, Actionable { + + /// The identifier of the form. + internal var id: String? internal var method: HTMLKit.Values.Method @@ -19,7 +22,7 @@ public struct Form: View { /// The events of the container. internal var events: [String]? - /// Creates a form container. + /// Creates a form. public init(method: Values.Method, @ContentBuilder content: () -> [FormElement]) { self.method = method @@ -27,13 +30,14 @@ public struct Form: View { self.classes = ["form"] } - /// Creates a form container. - internal init(method: Values.Method, content: [FormElement], classes: [String], events: [String]?) { + /// Creates a form. + internal init(method: Values.Method, content: [FormElement], classes: [String], events: [String]?, id: String?) { self.method = method self.content = content self.classes = classes self.events = events + self.id = id } public var body: Content { @@ -42,6 +46,30 @@ public struct Form: View { } .method(method) .class(classes.joined(separator: " ")) + .modify(unwrap: id) { + $0.id($1) + } + if let events = self.events { + Script { + events + } + } + } + + public func id(_ value: String) -> Form { + return self.mutate(id: value) + } +} + +extension Form: FormModifier { + + public func onSubmit(perfom action: Actions) -> Form { + + if action.description == "validate" { + return self.mutate(submitevent: action.script, validation: true) + } + + return self.mutate(submitevent: action.script, validation: false) } } @@ -92,6 +120,9 @@ public struct TextField: View, Modifiable { /// The identifier of the field. internal let name: String + /// The placeholder for the field value. + internal let prompt: String? + /// The content of the field. internal let value: String? @@ -102,17 +133,19 @@ public struct TextField: View, Modifiable { internal var events: [String]? /// Creates a text field. - public init(name: String, value: String? = nil) { + public init(name: String, prompt: String? = nil, value: String? = nil) { self.name = name + self.prompt = prompt self.value = value self.classes = ["input", "type:textfield"] } /// Creates a text field. - internal init(name: String, value: String?, classes: [String], events: [String]?) { + internal init(name: String, prompt: String?, value: String?, classes: [String], events: [String]?) { self.name = name + self.prompt = prompt self.value = value self.classes = classes self.events = events @@ -127,6 +160,9 @@ public struct TextField: View, Modifiable { .modify(unwrap: value) { $0.value($1) } + .modify(unwrap: prompt) { + $0.placeholder($1) + } } } @@ -139,6 +175,15 @@ extension TextField: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> TextField { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> TextField { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays a editable and expandable form control. @@ -147,6 +192,9 @@ public struct TextEditor: View, Modifiable { /// The identifier of the editor. internal let name: String + /// The placeholder for the field value. + internal let prompt: String? + /// The number of lines. internal var rows: Int = 1 @@ -160,17 +208,19 @@ public struct TextEditor: View, Modifiable { internal var events: [String]? /// Creates a text editor. - public init(name: String, @ContentBuilder content: () -> [String]) { + public init(name: String, prompt: String? = nil, @ContentBuilder content: () -> [String]) { self.name = name + self.prompt = prompt self.content = content() self.classes = ["input", "type:texteditor"] } /// Creates a text editor. - internal init(name: String, rows: Int, content: [String], classes: [String], events: [String]?) { + internal init(name: String, prompt: String?, rows: Int, content: [String], classes: [String], events: [String]?) { self.name = name + self.prompt = prompt self.rows = rows self.content = content self.classes = classes @@ -185,6 +235,9 @@ public struct TextEditor: View, Modifiable { .name(name) .class(classes.joined(separator: " ")) .rows(rows) + .modify(unwrap: prompt) { + $0.placeholder($1) + } } /// Sets the limit of the maximum lines. @@ -206,6 +259,15 @@ extension TextEditor: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> TextEditor { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> TextEditor { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays a form control @@ -255,6 +317,15 @@ extension CheckField: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> CheckField { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> CheckField { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays @@ -304,6 +375,15 @@ extension RadioSelect: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> RadioSelect { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> RadioSelect { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays @@ -357,6 +437,15 @@ extension SelectField: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> SelectField { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> SelectField { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays @@ -365,6 +454,9 @@ public struct SecureField: View, Modifiable { /// The identifier of the field. internal let name: String + /// The placeholder for the field value. + internal let prompt: String? + /// The content of the field. internal let value: String? @@ -375,17 +467,19 @@ public struct SecureField: View, Modifiable { internal var events: [String]? /// Creates a password field. - public init(name: String, value: String? = nil) { + public init(name: String, prompt: String? = nil, value: String? = nil) { self.name = name + self.prompt = prompt self.value = value self.classes = ["input", "type:securefield"] } /// Creates a password field. - internal init(name: String, value: String?, classes: [String], events: [String]?) { + internal init(name: String, prompt: String?, value: String?, classes: [String], events: [String]?) { self.name = name + self.prompt = prompt self.value = value self.classes = classes self.events = events @@ -400,6 +494,9 @@ public struct SecureField: View, Modifiable { .modify(unwrap: value) { $0.value($1) } + .modify(unwrap: prompt) { + $0.placeholder($1) + } } } @@ -412,6 +509,15 @@ extension SecureField: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> SecureField { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> SecureField { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays @@ -503,6 +609,15 @@ extension DatePicker: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> DatePicker { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> DatePicker { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays @@ -511,6 +626,9 @@ public struct SearchField: View, Modifiable { /// The identifier of the search field. internal let name: String + /// The placeholder for the field value. + internal let prompt: String? + /// The content of the field. internal let value: String? @@ -521,17 +639,19 @@ public struct SearchField: View, Modifiable { internal var events: [String]? /// Creates a search field. - public init(name: String, value: String? = nil) { + public init(name: String, prompt: String? = nil, value: String? = nil) { self.name = name + self.prompt = prompt self.value = value self.classes = ["input", "type:searchfield"] } /// Creates a search field. - internal init(name: String, value: String?, classes: [String], events: [String]?) { + internal init(name: String, prompt: String?, value: String?, classes: [String], events: [String]?) { self.name = name + self.prompt = prompt self.value = value self.classes = classes self.events = events @@ -546,6 +666,9 @@ public struct SearchField: View, Modifiable { .modify(unwrap: value) { $0.value($1) } + .modify(unwrap: prompt) { + $0.placeholder($1) + } } } @@ -558,6 +681,15 @@ extension SearchField: InputModifier { public func backgroundColor(_ color: Tokens.BackgroundColor) -> SearchField { return self.mutate(backgroundcolor: color.rawValue) } + + public func disabled(_ condition: Bool) -> SearchField { + + if condition { + return self.mutate(state: Tokens.ViewState.disabled.rawValue) + } + + return self + } } /// A component that displays the progress of a task. @@ -604,3 +736,72 @@ public struct Progress: View { .class(classes.joined(separator: " ")) } } + +/// A component to edit and format text content. +public struct TextPad: View { + + /// The identifier of the textpad. + internal let name: String + + /// The content of the textpad. + internal var content: [String] + + /// The classes of the textpad. + internal var classes: [String] + + /// Creates a textpad + public init(name: String, @ContentBuilder content: () -> [String]) { + + self.name = name + self.content = content() + self.classes = ["textpad"] + } + + /// Creates a textpad. + internal init(name: String, content: [String], classes: [String]) { + + self.name = name + self.content = content + self.classes = classes + } + + public var body: Content { + Division { + UnorderedList { + ListItem { + Paragraph { + "B" + } + } + .class("toolbar-tool command:bold") + ListItem { + Paragraph { + "I" + } + } + .class("toolbar-tool command:italic") + ListItem { + Paragraph { + "U" + } + } + .class("toolbar-tool command:underline") + ListItem { + Paragraph { + "S" + } + } + .class("toolbar-tool command:strikethrough") + } + .class("textpad-toolbar") + TextArea { + content + } + .id(name) + .name(name) + .rows(5) + .class("textpad-content") + } + .class(classes.joined(separator: " ")) + } +} diff --git a/Sources/HTMLKitComponents/Components/Grid.swift b/Sources/HTMLKitComponents/Components/Grid.swift index 7d84f0d5..7bc9fd6c 100644 --- a/Sources/HTMLKitComponents/Components/Grid.swift +++ b/Sources/HTMLKitComponents/Components/Grid.swift @@ -1,6 +1,6 @@ /* Abstract: - The file contains everything related to the collection. + The file contains everything related to the grid component. */ import HTMLKit diff --git a/Sources/HTMLKitComponents/Components/Group.swift b/Sources/HTMLKitComponents/Components/Group.swift index 46e2c03c..4355f81d 100644 --- a/Sources/HTMLKitComponents/Components/Group.swift +++ b/Sources/HTMLKitComponents/Components/Group.swift @@ -1,5 +1,6 @@ /* Abstract: + The file contains everything related to the group component. */ import HTMLKit diff --git a/Sources/HTMLKitComponents/Components/Image.swift b/Sources/HTMLKitComponents/Components/Image.swift index 14aa4820..418cdd09 100644 --- a/Sources/HTMLKitComponents/Components/Image.swift +++ b/Sources/HTMLKitComponents/Components/Image.swift @@ -1,6 +1,6 @@ /* Abstract: - The file contains everything related to images. + The file contains everything related to image component. */ import HTMLKit @@ -66,4 +66,8 @@ extension Image: ViewModifier { public func opacity(_ value: Tokens.OpacityValue) -> Image { return self.mutate(opacity: value.rawValue) } + + public func hidden() -> Image { + return self.mutate(state: Tokens.ViewState.hidden.rawValue) + } } diff --git a/Sources/HTMLKitComponents/Components/Modal.swift b/Sources/HTMLKitComponents/Components/Modal.swift index 0a415e45..61e709ec 100644 --- a/Sources/HTMLKitComponents/Components/Modal.swift +++ b/Sources/HTMLKitComponents/Components/Modal.swift @@ -1,21 +1,40 @@ +/* + Abstract: + The file contains a modal component. + */ + import HTMLKit +/// A component that presents a dialog on top of other views. public struct Modal: View, Modifiable, Actionable { internal var id: String? + /// The content of the modal. internal var content: [Content] + /// The classes of the modal. internal var classes: [String] + /// The events of the modal. internal var events: [String]? + /// Creates a modal. public init(@ContentBuilder content: () -> [Content]) { self.content = content() self.classes = ["modal"] } + /// Createsa model. + internal init(content: [Content], classes: [String], events: [String]?, id: String?) { + + self.content = content + self.classes = classes + self.events = events + self.id = id + } + public var body: Content { Dialog { content diff --git a/Sources/HTMLKitComponents/Components/ScrollView.swift b/Sources/HTMLKitComponents/Components/ScrollView.swift new file mode 100644 index 00000000..e9e98785 --- /dev/null +++ b/Sources/HTMLKitComponents/Components/ScrollView.swift @@ -0,0 +1,37 @@ +/* + Abstract: + The file contains everything related to the scrollview component. + */ + +import HTMLKit + +/// A component that displays content in its scrollable area. +public struct ScrollView: View { + + /// The content of the scrollview. + internal var content: [Content] + + /// The classes of the scrollview. + internal var classes: [String] + + /// Creates a scrollview. + public init(direction: Tokens.FlowDirection, @ContentBuilder content: () -> [Content]) { + + self.content = content() + self.classes = ["scrollview", direction.rawValue] + } + + /// Creates a scrollview + internal init(content: [Content], classes: [String]) { + + self.content = content + self.classes = classes + } + + public var body: Content { + Division { + content + } + .class(classes.joined(separator: " ")) + } +} diff --git a/Sources/HTMLKitComponents/Components/Stack.swift b/Sources/HTMLKitComponents/Components/Stack.swift index e5c098f2..ce2aa52e 100644 --- a/Sources/HTMLKitComponents/Components/Stack.swift +++ b/Sources/HTMLKitComponents/Components/Stack.swift @@ -77,7 +77,7 @@ extension HStack: HoverModifier { } extension HStack: ViewModifier { - + public func backgroundColor(_ color: Tokens.BackgroundColor) -> HStack { return self.mutate(backgroundcolor: color.rawValue) } @@ -89,6 +89,10 @@ extension HStack: ViewModifier { public func zIndex(_ index: Tokens.PositionIndex) -> HStack { return self.mutate(zindex: index.rawValue) } + + public func hidden() -> HStack { + return self.mutate(state: Tokens.ViewState.hidden.rawValue) + } } /// A component that arranges content vertically. @@ -164,6 +168,10 @@ extension VStack: ViewModifier { public func zIndex(_ index: Tokens.PositionIndex) -> VStack { return self.mutate(zindex: index.rawValue) } + + public func hidden() -> VStack { + return self.mutate(state: Tokens.ViewState.hidden.rawValue) + } } /// A component that overlays content. @@ -239,6 +247,10 @@ extension ZStack: ViewModifier { public func zIndex(_ index: Tokens.PositionIndex) -> ZStack { return self.mutate(zindex: index.rawValue) } + + public func hidden() -> ZStack { + return self.mutate(state: Tokens.ViewState.hidden.rawValue) + } } /// A component that represents a stack column. diff --git a/Sources/HTMLKitComponents/Components/Symbol.swift b/Sources/HTMLKitComponents/Components/Symbol.swift index 1073c308..b5895d71 100644 --- a/Sources/HTMLKitComponents/Components/Symbol.swift +++ b/Sources/HTMLKitComponents/Components/Symbol.swift @@ -11,6 +11,7 @@ import OrderedCollections import FoundationXML #endif +/// A component that displays symbols. public struct Symbol: View, Modifiable { /// The content of the symbol. @@ -49,9 +50,7 @@ public struct Symbol: View, Modifiable { Vector { content } - .width(16) - .height(16) - .viewBox("0 0 16 16") + .viewBox("0 0 20 16") .fill("currentColor") .class(classes.joined(separator: " ")) } diff --git a/Sources/HTMLKitComponents/Components/Text.swift b/Sources/HTMLKitComponents/Components/Text.swift index 285ca745..a3293e03 100644 --- a/Sources/HTMLKitComponents/Components/Text.swift +++ b/Sources/HTMLKitComponents/Components/Text.swift @@ -65,6 +65,21 @@ extension Text: HoverModifier { } } +extension Text: PressModifier { + + public func onClick(perfom action: Actions) -> Text { + return self.mutate(clickevent: action.script) + } + + public func onTap(perfom action: Actions) -> Text { + return self.mutate(tapevent: action.script) + } + + public func onPress(perfom action: Actions) -> Text { + return self.mutate(pressevent: action.script) + } +} + extension Text: TextModifier { public func font(_ style: Tokens.TextStyle) -> Text { diff --git a/Sources/HTMLKitComponents/Components/Toggle.swift b/Sources/HTMLKitComponents/Components/Toggle.swift index fa2c3e9d..b2ae8176 100644 --- a/Sources/HTMLKitComponents/Components/Toggle.swift +++ b/Sources/HTMLKitComponents/Components/Toggle.swift @@ -1,6 +1,6 @@ /* Abstract: - The file contains everything related to the text component. + The file contains everything related to the toggle component. */ import HTMLKit diff --git a/Sources/HTMLKitComponents/Events.swift b/Sources/HTMLKitComponents/Events.swift index 54e8fb2a..66bc6627 100644 --- a/Sources/HTMLKitComponents/Events.swift +++ b/Sources/HTMLKitComponents/Events.swift @@ -1,10 +1,14 @@ /* Abstract: - The file contains the script stencils for the js. + The file contains the event stencils for the components. */ +import Foundation + +/// A collection of events, that can occur on components. public enum Events { + /// Returns a hover event stencil. static func hover(selector: String, script: String) -> String { return """ @@ -14,6 +18,7 @@ public enum Events { """ } + /// Returns a leave event stencil. static func leave(selector: String, script: String) -> String { return """ @@ -23,6 +28,7 @@ public enum Events { """ } + /// Returns a change event stencil. static func change(selector: String, script: String) -> String { return """ @@ -32,6 +38,7 @@ public enum Events { """ } + /// Returns a click event stencil. static func click(selector: String, script: String) -> String { return """ @@ -41,6 +48,7 @@ public enum Events { """ } + /// Returns a tap gesture stencil. static func tap(selector: String, script: String) -> String { return """ @@ -50,6 +58,7 @@ public enum Events { """ } + /// Returns a long press gesture stencil. static func press(selector: String, script: String) -> String { return """ @@ -59,6 +68,7 @@ public enum Events { """ } + /// Returns a drag event stencil. static func drag(selector: String, script: String) -> String { return """ @@ -68,6 +78,7 @@ public enum Events { """ } + /// Returns a drop event stencil. static func drop(selector: String, script: String) -> String { return """ @@ -77,6 +88,7 @@ public enum Events { """ } + /// Returns a focus event stencil. static func focus(selector: String, script: String) -> String { return """ @@ -86,12 +98,17 @@ public enum Events { """ } - static func submit(selector: String, script: String) -> String { + /// Returns a submit event stencil. + static func submit(selector: String, script: String, validation: Bool) -> String { return """ $('#\(selector)').onSubmit(function() { + + event.preventDefault(); + \(script) - }) + + }, \(validation)); """ } } diff --git a/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift b/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift index f3c79641..b097d921 100644 --- a/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/ButtonModifier.swift @@ -3,39 +3,23 @@ The file contains the modifiers for button components. */ +/// A type that describes the modifier of a button component. public protocol ButtonModifier { /// Sets the size of the button. - /// - /// - Parameters: - /// - size: - /// - /// - Returns: A component func buttonSize(_ size: Tokens.ButtonSize) -> Self /// Sets the style of the button. - /// - /// - Parameters: - /// - style: - /// - /// - Returns: A component func buttonStyle(_ style: Tokens.ButtonStyle) -> Self /// Sets the shape of the button. - /// - /// - Parameters: - /// - shape: - /// - /// - Returns: A component func borderShape(_ shape: Tokens.BorderShape) -> Self /// Sets the background color. - /// - /// - Parameters: - /// - color: - /// - /// - Returns: A component func backgroundColor(_ color: Tokens.BackgroundColor) -> Self + + /// Sets the state of the view. + func disabled(_ condition: Bool) -> Self } extension ButtonModifier where Self: Modifiable { @@ -55,4 +39,8 @@ extension ButtonModifier where Self: Modifiable { internal func mutate(backgroundcolor class: String) -> Self { return self.mutate(class: `class`) } + + internal func mutate(state class: String) -> Self { + return self.mutate(class: `class`) + } } diff --git a/Sources/HTMLKitComponents/Modifiers/DragModifier.swift b/Sources/HTMLKitComponents/Modifiers/DragModifier.swift index 1d96e790..21da6a24 100644 --- a/Sources/HTMLKitComponents/Modifiers/DragModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/DragModifier.swift @@ -1,9 +1,18 @@ +/* + Abstract: + The file contains the modifiers for components with drag interaction. + */ + +/// A type that describes the modifier of a draggable component. public protocol DragModifier { + /// The identifier of the component. func id(_ value: String) -> Self + /// Acts on a drag event. func onDrag(perfom action: Actions) -> Self + /// Acts on a drop event func onDrop(perfom action: Actions) -> Self } diff --git a/Sources/HTMLKitComponents/Modifiers/FormModifier.swift b/Sources/HTMLKitComponents/Modifiers/FormModifier.swift new file mode 100644 index 00000000..3aac43a1 --- /dev/null +++ b/Sources/HTMLKitComponents/Modifiers/FormModifier.swift @@ -0,0 +1,23 @@ +/* + Abstract: + The file contains the modifiers for form component. + */ + +/// A type that describes the modifier of a form component. +public protocol FormModifier { + + /// Acts on a submit event. + func onSubmit(perfom action: Actions) -> Self +} + +extension FormModifier where Self: Actionable { + + internal func mutate(submitevent script: String, validation: Bool) -> Self { + + guard let identifier = self.id else { + fatalError("Initiative identifier unkown.") + } + + return self.mutate(event: Events.submit(selector: identifier, script: script, validation: validation)) + } +} diff --git a/Sources/HTMLKitComponents/Modifiers/HoverModifier.swift b/Sources/HTMLKitComponents/Modifiers/HoverModifier.swift index 502dff10..20f62137 100644 --- a/Sources/HTMLKitComponents/Modifiers/HoverModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/HoverModifier.swift @@ -1,9 +1,18 @@ +/* + Abstract: + The file contains the modifiers for components with hover interaction. + */ + +/// A type that describes the modifier of a hoverable component. public protocol HoverModifier { + /// The identifier of the component. func id(_ value: String) -> Self + /// Acts on a hover event. func onHover(perfom action: Actions) -> Self + /// Acts on a leave event. func onLeave(perfom action: Actions) -> Self } diff --git a/Sources/HTMLKitComponents/Modifiers/ImageModifier.swift b/Sources/HTMLKitComponents/Modifiers/ImageModifier.swift index c6033160..7e529eef 100644 --- a/Sources/HTMLKitComponents/Modifiers/ImageModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/ImageModifier.swift @@ -3,30 +3,16 @@ The file contains the modifiers for image components. */ +/// A type that describes the modifier of a image component. public protocol ImageModifier { /// Sets how the content should be resized to fit its parent. - /// - /// - Parameters: - /// - fit: The fit - /// - /// - Returns: A component func objectFit(_ fit: Tokens.ObjectFit) -> Self /// Sets the scale of the image. - /// - /// - Parameters: - /// - scale: The scale - /// - /// - Returns: A component func imageScale(_ scale: Tokens.ImageScale) -> Self /// Sets the fill style to use. - /// - /// - Parameters: - /// - shape: The fill style to use. - /// - /// - Returns: A component func clipShape(_ shape: Tokens.ClipShape) -> Self } diff --git a/Sources/HTMLKitComponents/Modifiers/InputModifier.swift b/Sources/HTMLKitComponents/Modifiers/InputModifier.swift index 427b01ce..07e10a83 100644 --- a/Sources/HTMLKitComponents/Modifiers/InputModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/InputModifier.swift @@ -3,23 +3,17 @@ The file contains the modifiers for input components. */ +/// A type that describes the modifier of a input component. public protocol InputModifier { /// Sets the border shape of the input. - /// - /// - Parameters: - /// - shape: - /// - /// - Returns: A component func borderShape(_ shape: Tokens.BorderShape) -> Self /// Sets the background color of the input. - /// - /// - Parameters: - /// - color: - /// - /// - Returns: A component func backgroundColor(_ color: Tokens.BackgroundColor) -> Self + + /// Sets the state of the view. + func disabled(_ condition: Bool) -> Self } extension InputModifier where Self: Modifiable { @@ -31,5 +25,9 @@ extension InputModifier where Self: Modifiable { internal func mutate(backgroundcolor class: String) -> Self { return self.mutate(class: `class`) } + + internal func mutate(state class: String) -> Self { + return self.mutate(class: `class`) + } } diff --git a/Sources/HTMLKitComponents/Modifiers/PressModifier.swift b/Sources/HTMLKitComponents/Modifiers/PressModifier.swift index ab44e46e..4e27cd7d 100644 --- a/Sources/HTMLKitComponents/Modifiers/PressModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/PressModifier.swift @@ -1,11 +1,21 @@ +/* + Abstract: + The file contains the modifiers for components with press interaction. + */ + +/// A type thats describes the modifier of a pressable component. public protocol PressModifier { + /// The identifier of the component. func id(_ value: String) -> Self + /// Acts on a click event. func onClick(perfom action: Actions) -> Self + /// Acts on a tap event. func onTap(perfom action: Actions) -> Self + /// Acts on a press event. func onPress(perfom action: Actions) -> Self } diff --git a/Sources/HTMLKitComponents/Modifiers/TextModifier.swift b/Sources/HTMLKitComponents/Modifiers/TextModifier.swift index 2f65e87d..33a69a94 100644 --- a/Sources/HTMLKitComponents/Modifiers/TextModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/TextModifier.swift @@ -3,69 +3,34 @@ The file contains the modifiers for text components. */ +/// A type that describes the modifier of a text component. public protocol TextModifier { /// Sets the style of the text. - /// - /// - Parameters: - /// - style: - /// - /// - Returns: A component func font(_ style: Tokens.TextStyle) -> Self /// Sets the foreground color of the text. - /// - /// - Parameters: - /// - color: - /// - /// - Returns: A component func foregroundColor(_ color: Tokens.ForegroundColor) -> Self /// Sets the size of the text. - /// - /// - Parameters: - /// - size: - /// - /// - Returns: A component func fontSize(_ size: Tokens.FontSize) -> Self /// Sets the weight of the text. - /// - /// - Parameters: - /// - weight: - /// - /// - Returns: func fontWeight(_ weight: Tokens.FontWeight) -> Self /// Sets the transformation for the text. - /// - /// - Parameters: - /// - transformation: - /// - /// - Returns: A component func fontTransformation(_ transformation: Tokens.TextTransformation) -> Self /// Sets the style of the font. - /// - /// - Parameters: - /// - style: - /// - /// - Returns: A component func fontStyle(_ style: Tokens.FontStyle) -> Self /// Applies a bold font weight to the text. - /// - /// - Returns: A component func bold() -> Self /// Applies italics to the text. - /// - /// - Returns: A component func italic() -> Self /// Applies an underline to the text. - /// - /// - Returns: A component func underline() -> Self } diff --git a/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift b/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift index 0c3f1896..a3be0178 100644 --- a/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift +++ b/Sources/HTMLKitComponents/Modifiers/ViewModifier.swift @@ -3,31 +3,20 @@ The file contains the modifiers for view components. */ +/// A type that describes the modifier of a view. public protocol ViewModifier { /// Sets the opacity of the view. - /// - /// - Parameters: - /// - value: - /// - /// - Returns: A component func opacity(_ value: Tokens.OpacityValue) -> Self /// Sets the position of the view. - /// - /// - Parameters: - /// - index: - /// - /// - Returns: A component func zIndex(_ index: Tokens.PositionIndex) -> Self /// Sets the background color of the view. - /// - /// - Parameters: - /// - color: - /// - /// - Returns: A component func backgroundColor(_ color: Tokens.BackgroundColor) -> Self + + /// Hides the view + func hidden() -> Self } extension ViewModifier where Self: Modifiable { @@ -43,4 +32,8 @@ extension ViewModifier where Self: Modifiable { internal func mutate(backgroundcolor class: String) -> Self { return self.mutate(class: `class`) } + + internal func mutate(state class: String) -> Self { + return self.mutate(class: `class`) + } } diff --git a/Sources/HTMLKitComponents/Properties/Actionable.swift b/Sources/HTMLKitComponents/Properties/Actionable.swift index fcc4efd5..7b650b53 100644 --- a/Sources/HTMLKitComponents/Properties/Actionable.swift +++ b/Sources/HTMLKitComponents/Properties/Actionable.swift @@ -1,5 +1,12 @@ +/* + Abstract: + The file contains + */ + +/// A type that describes a component with events. internal protocol Actionable: Identifiable { + /// The events of the component. var events: [String]? { get set } } diff --git a/Sources/HTMLKitComponents/Properties/Identifiable.swift b/Sources/HTMLKitComponents/Properties/Identifiable.swift index 27f508d1..955ee72d 100644 --- a/Sources/HTMLKitComponents/Properties/Identifiable.swift +++ b/Sources/HTMLKitComponents/Properties/Identifiable.swift @@ -1,10 +1,5 @@ -// -// File.swift -// -// -// Created by Mattes Mohr on 25.12.22. -// +/// A type that describes a component with an identifier. internal protocol Identifiable { /// The identifier of the component. diff --git a/Sources/HTMLKitComponents/Properties/Modifiable.swift b/Sources/HTMLKitComponents/Properties/Modifiable.swift index b47c9d8e..5e60e812 100644 --- a/Sources/HTMLKitComponents/Properties/Modifiable.swift +++ b/Sources/HTMLKitComponents/Properties/Modifiable.swift @@ -1,5 +1,8 @@ + +/// A type that describes a component with classes. internal protocol Modifiable { + /// The classes of the component. var classes: [String] { get set } } diff --git a/Sources/HTMLKitComponents/Resources/css/card.css b/Sources/HTMLKitComponents/Resources/css/card.css index cf5ead72..193bca4d 100644 --- a/Sources/HTMLKitComponents/Resources/css/card.css +++ b/Sources/HTMLKitComponents/Resources/css/card.css @@ -3,13 +3,36 @@ */ .card { - padding-top: var(--cardPaddingY); - padding-right: var(--cardPaddingX); - padding-bottom: var(--cardPaddingY); - padding-left: var(--cardPaddingX); + + 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 new file mode 100644 index 00000000..9b804249 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/carousel.css @@ -0,0 +1,73 @@ +/** + 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 index a5a96d63..eb697050 100644 --- a/Sources/HTMLKitComponents/Resources/css/divider.css +++ b/Sources/HTMLKitComponents/Resources/css/divider.css @@ -7,5 +7,7 @@ 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 new file mode 100644 index 00000000..d548744c --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/dropdown.css @@ -0,0 +1,33 @@ +.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 index 7e537a9d..1db797af 100644 --- a/Sources/HTMLKitComponents/Resources/css/form.css +++ b/Sources/HTMLKitComponents/Resources/css/form.css @@ -141,7 +141,6 @@ -webkit-appearance: none; } -.required\:true:after { - content: " *"; - color: var(--redColor); +.input:invalid { + border: 1px solid red !important; } diff --git a/Sources/HTMLKitComponents/Resources/css/grid.css b/Sources/HTMLKitComponents/Resources/css/grid.css index ab57661b..a6e59705 100644 --- a/Sources/HTMLKitComponents/Resources/css/grid.css +++ b/Sources/HTMLKitComponents/Resources/css/grid.css @@ -10,15 +10,6 @@ .grid-item { - padding-top: var(--gridItemPaddingY); - padding-right: var(--gridItemPaddingX); - padding-bottom: var(--gridItemPaddingY); - padding-left: var(--gridItemPaddingX); - border-width: var(--gridItemBorderWidth); - border-style: solid; - border-color: var(--gridItemBorderColor); - border-radius: var(--gridItemBorderRadius); - overflow: hidden; } diff --git a/Sources/HTMLKitComponents/Resources/css/roots.css b/Sources/HTMLKitComponents/Resources/css/roots.css index 76b53d9c..299469a0 100644 --- a/Sources/HTMLKitComponents/Resources/css/roots.css +++ b/Sources/HTMLKitComponents/Resources/css/roots.css @@ -138,7 +138,7 @@ divider variables */ - --dividerColor: #FFFFFF; + --dividerColor: var(--grayColor); /** button variables @@ -169,22 +169,44 @@ */ --gridGapSize: 15px; - --gridItemPaddingY: 12px; - --gridItemPaddingX: 16px; - --gridItemBorderWidth: 1px; - --gridItemBorderColor: var(--grayColor); - --gridItemBorderRadius: 5px; - --gridItemBackgroundColor: var(--whiteColor); - /** - grid variables + card variables */ --cardPaddingY: 12px; --cardPaddingX: 16px; --cardBorderWidth: 1px; - --cardBorderRadius: 15px; + --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/scrollview.css b/Sources/HTMLKitComponents/Resources/css/scrollview.css new file mode 100644 index 00000000..a5e22ad6 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/css/scrollview.css @@ -0,0 +1,29 @@ +/** + scrollview component + */ + +.scrollview { + display: grid; + gap: var(--scrollViewGapSize); + background-color: var(--scrollViewBackgroundColor); +} + +.scrollview::-webkit-scrollbar{ + display: none; +} + +.scrollview.direction\:horizontal { + grid-auto-flow: column; + grid-auto-columns: min-content; + height: inherit; + overflow-x: auto; + overscroll-behavior-inline: contain; +} + +.scrollview.direction\:vertical { + grid-auto-flow: row; + grid-auto-rows: min-content; + height: inherit; + overflow-y: auto; + overscroll-behavior-y: contain; +} diff --git a/Sources/HTMLKitComponents/Resources/css/utils.css b/Sources/HTMLKitComponents/Resources/css/utils.css index 824336e2..5a6efefb 100644 --- a/Sources/HTMLKitComponents/Resources/css/utils.css +++ b/Sources/HTMLKitComponents/Resources/css/utils.css @@ -2,31 +2,44 @@ utils classes */ -.display\:block { - display: block !important; -} - -zindex\:1 { +.zindex\:1 { position: relative; z-index: 1; } -zindex\:2 { +.zindex\:2 { position: relative; z-index: 2; } -zindex\:3 { +.zindex\:3 { position: relative; z-index: 3; } -zindex\:4 { +.zindex\:4 { position: relative; z-index: 4; } -zindex\:5 { +.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/js/all.js b/Sources/HTMLKitComponents/Resources/js/all.js index c91dae7f..f6ee7b4c 100644 --- a/Sources/HTMLKitComponents/Resources/js/all.js +++ b/Sources/HTMLKitComponents/Resources/js/all.js @@ -2,138 +2,196 @@ var $ = (function () { 'use strict'; - var constructor = function (selector) { + var Self = function (selector) { this.elems = document.querySelectorAll(selector); }; /** - * This function is for + * Peforms when the pointer enters the target area. */ - constructor.prototype.onHover = function (callback) { + Self.prototype.onHover = function (callback) { this.elems[0].addEventListener("mouseenter", callback); }; /** - * This function is for + * Peforms when the pointer leaves the target area. */ - constructor.prototype.onLeave = function (callback) { + Self.prototype.onLeave = function (callback) { this.elems[0].addEventListener("mouseleave", callback); }; /** - * This function is for + * Performs when the target value changes. */ - constructor.prototype.onChange = function (callback) { + Self.prototype.onChange = function (callback) { this.elems[0].addEventListener("change", callback); }; /** - * This function is for + * Performs when the target is clicked. */ - constructor.prototype.onClick = function (callback) { + Self.prototype.onClick = function (callback) { this.elems[0].addEventListener("click", callback); }; /** - * This function is for + * Performs when the target is touched. */ - constructor.prototype.onTapGesture = function (callback) { + Self.prototype.onTapGesture = function (callback) { this.elems[0].addEventListener("touchend", callback); }; /** - * This function is for + * Performs when the target is touched. */ - constructor.prototype.onLongPressGesture = function (callback) { + Self.prototype.onLongPressGesture = function (callback) { this.elems[0].addEventListener("touchstart", callback); }; /** - * This function is for + * Performs when the target is dragged. */ - constructor.prototype.onDrag = function (callback) { + Self.prototype.onDrag = function (callback) { this.elems[0].addEventListener("drag", callback); }; /** - * This function is for + * Performs when the target is dropped. */ - constructor.prototype.onDrop = function (callback) { + Self.prototype.onDrop = function (callback) { this.elems[0].addEventListener("drop", callback); }; /** - * This function is for + * Performs when the target is focused. */ - constructor.prototype.onFocus = function (callback) { + Self.prototype.onFocus = function (callback) { this.elems[0].addEventListener("focus", callback); }; /** - * This function is for + * Performs when the target is submitted. */ - constructor.prototype.onSubmit = function (callback) { + Self.prototype.onSubmit = function (callback) { + + if (validate) { + this.elems[0].setAttribute("novalidate", "novalidate"); + } this.elems[0].addEventListener("submit", callback); }; /** - * This function is for + * Shows the target. */ - constructor.prototype.show = function() { + Self.prototype.show = function() { - const elements = document.getElementsByClassName("display:block"); + const elements = document.getElementsByClassName("state:visible"); for (let element of elements){ - element.classList.remove("display:block") + element.classList.remove("state:visible") } - this.elems[0].classList.add("display:block") + this.elems[0].classList.add("state:visible") }; /** - * This function is for + * Hides the target. */ - constructor.prototype.hide = function() { - - this.elems[0].classList.remove("display:block") + Self.prototype.hide = function() { + + this.elems[0].classList.add("state:hidden") }; /** - * This function is for + * Animates the target. */ - constructor.prototype.animate = function({params}, speed) { + Self.prototype.animate = function({params}, speed) { this.elems[0].animate({params}, speed); }; /** - * This function is for + * Opens the dialog. */ - constructor.prototype.open = function() { + Self.prototype.open = function() { this.elems[0].showModal() }; /** - * This function is for + * Closes the dialog. */ - constructor.prototype.close = function() { + Self.prototype.close = function() { this.elems[0].close() }; + /** + * Validates a form. + */ + Self.prototype.validate = function(validators) { + + const form = this.elems[0]; + + for (let validator of JSON.parse(validators)) { + + const element = form.elements[validator.field]; + + switch (validator.rule) { + + case "value": + + if (!element.value) { + element.setCustomValidity('The field must have a value.'); + + } else { + element.setCustomValidity(''); + } + + break; + + case "email": + + if (!element.value.includes("@")) { + element.setCustomValidity('The field must have a valid email format.'); + + } else { + element.setCustomValidity(''); + } + + break; + + case "url": + + if (!element.value.includes(":")) { + element.setCustomValidity('The field must have a valid url format.'); + + } else { + element.setCustomValidity(''); + } + } + } + + if (!form.checkValidity()) { + form.reportValidity(); + + } else { + form.submit(); + } + }; + var instantiate = function (selector) { - return new constructor(selector); + return new Self(selector); }; return instantiate; diff --git a/Sources/HTMLKitComponents/Resources/js/carousel.js b/Sources/HTMLKitComponents/Resources/js/carousel.js new file mode 100644 index 00000000..2a7137c7 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/js/carousel.js @@ -0,0 +1,92 @@ +(function() { + + var Carousel = function (element) { + + this.element = element; + this.slides = element.getElementsByClassName('carousel-content')[0]; + this.indication = element.getElementsByClassName('carousel-indication')[0]; + + this.toggleState(0); + + this.autoPlay(1); + + this.initiateListener(); + } + + Carousel.prototype.initiateListener = function () { + + var self = this; + + for (let indicator of this.indication.children) { + + indicator.addEventListener('click', function (event) { + + event.preventDefault(); + + self.slideTo(self.getPosition(event.target.getAttribute('href').replace('#', ''))); + + }); + } + }; + + Carousel.prototype.autoPlay = function (position) { + + var self = this; + + setInterval(function () { + + if (position < self.slides.children.length) { + + self.slideTo(position); + + position += 1; + + } else { + position = 0; + } + }, 7000); + }; + + Carousel.prototype.getPosition = function (name) { + + for(var position = 0; position < this.slides.children.length; position++) { + + if (this.slides.children[position].id == name) { + return position; + } + } + }; + + Carousel.prototype.toggleState = function (position) { + + for(let indicator of this.indication.children) { + indicator.classList.remove('state:active'); + } + + this.indication.children[position].classList.add('state:active'); + }; + + Carousel.prototype.slideTo = function (position) { + + var self = this; + + this.slides.scrollTo({ + left: (position * this.slides.children[position].offsetWidth), behavior: 'smooth' + }); + + self.toggleState(position); + }; + + var carousel = document.getElementsByClassName('carousel'); + + if(carousel.length > 0) { + + for(var i = 0; i < carousel.length; i++) { + + (function(i) { + new Carousel(carousel[i]); + })(i); + } + } + +}()); diff --git a/Sources/HTMLKitComponents/Resources/js/textpad.js b/Sources/HTMLKitComponents/Resources/js/textpad.js new file mode 100644 index 00000000..4b37a2df --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/js/textpad.js @@ -0,0 +1,67 @@ +(function() { + + var Textpad = function (element) { + + this.element = element + this.content = element.getElementsByClassName('textpad-content')[0]; + this.toolbar = element.getElementsByClassName('textpad-toolbar')[0]; + + this.initiateListener(); + }; + + Textpad.prototype.initiateListener = function () { + + var self = this; + + for (let tool of this.toolbar.children) { + + tool.addEventListener('click', function (event) { + + event.preventDefault(); + + var replacement = ""; + + var selection = self.getSelection(); + + if (tool.classList.contains('command:bold')) { + replacement = '' + selection + ''; + } + + if (tool.classList.contains('command:italic')) { + replacement = '' + selection + ''; + } + + if (tool.classList.contains('command:underline')) { + replacement = '' + selection + ''; + } + + if (tool.classList.contains('command:strikethrough')) { + replacement = '' + selection + ''; + } + + self.content.setRangeText(replacement, self.content.selectionStart, self.content.selectionEnd); + }); + } + }; + + Textpad.prototype.getSelection = function () { + + return this.content.value.substring(this.content.selectionStart, this.content.selectionEnd); + }; + + var textpad = document.getElementsByClassName('textpad'); + + if(textpad.length > 0) { + + for(var i = 0; i < textpad.length; i++) { + + (function(i) { + + new Textpad(textpad[i]); + + })(i); + + } + } + +}()); diff --git a/Sources/HTMLKitComponents/Resources/symbols/chart.pie.svg b/Sources/HTMLKitComponents/Resources/symbols/chart.pie.svg new file mode 100644 index 00000000..a4327e41 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/chart.pie.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/clock.svg b/Sources/HTMLKitComponents/Resources/symbols/clock.svg new file mode 100644 index 00000000..76315edc --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/clock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Sources/HTMLKitComponents/Resources/symbols/envelope.svg b/Sources/HTMLKitComponents/Resources/symbols/envelope.svg new file mode 100644 index 00000000..3d787661 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/envelope.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/file.svg b/Sources/HTMLKitComponents/Resources/symbols/file.svg new file mode 100644 index 00000000..2c69fb28 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/file.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/folder.svg b/Sources/HTMLKitComponents/Resources/symbols/folder.svg index 86e9a90f..cc7b772c 100644 --- a/Sources/HTMLKitComponents/Resources/symbols/folder.svg +++ b/Sources/HTMLKitComponents/Resources/symbols/folder.svg @@ -1,11 +1,3 @@ - - - - - - - - - - + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/house.svg b/Sources/HTMLKitComponents/Resources/symbols/house.svg new file mode 100644 index 00000000..bc16411c --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/house.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/lock.svg b/Sources/HTMLKitComponents/Resources/symbols/lock.svg new file mode 100644 index 00000000..349375e4 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/lock.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/person.svg b/Sources/HTMLKitComponents/Resources/symbols/person.svg new file mode 100644 index 00000000..00d1c42e --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/person.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/photo.svg b/Sources/HTMLKitComponents/Resources/symbols/photo.svg new file mode 100644 index 00000000..f3ed750d --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/photo.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/text.aligncenter.svg b/Sources/HTMLKitComponents/Resources/symbols/text.aligncenter.svg new file mode 100644 index 00000000..8c99778f --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/text.aligncenter.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/text.alignjustify.svg b/Sources/HTMLKitComponents/Resources/symbols/text.alignjustify.svg new file mode 100644 index 00000000..92cbf6a4 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/text.alignjustify.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/text.alignleft.svg b/Sources/HTMLKitComponents/Resources/symbols/text.alignleft.svg new file mode 100644 index 00000000..5f481377 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/text.alignleft.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Resources/symbols/text.alignright.svg b/Sources/HTMLKitComponents/Resources/symbols/text.alignright.svg new file mode 100644 index 00000000..d5b08859 --- /dev/null +++ b/Sources/HTMLKitComponents/Resources/symbols/text.alignright.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/HTMLKitComponents/Tokens.swift b/Sources/HTMLKitComponents/Tokens.swift index 6cdfe061..664a7dcf 100644 --- a/Sources/HTMLKitComponents/Tokens.swift +++ b/Sources/HTMLKitComponents/Tokens.swift @@ -1,8 +1,9 @@ /* Abstract: - The file contains the class tokens for the css. + The file contains the class tokens for the components. */ +/// A collection of class tokens for the components. public enum Tokens { /// A direction for elements. @@ -439,6 +440,7 @@ public enum Tokens { case outline = "style:outline" } + /// A shape of the button. public enum BorderShape: String { case smallrounded = "shape:smallrounded" @@ -456,17 +458,29 @@ public enum Tokens { /// Sets the size to 75 %. case large = "size:large" - /// Sets the size to 50%. + /// Sets the size to 50 %. case medium = "size:medium" /// Sets the size to 25 %. case small = "size:small" } + /// A ratio for the grid. public enum ItemRatio: String { + /// Sets the ratio to 15%. + case sixth = "ratio:15" + + /// Sets the ratio to 20 %. + case fifth = "ratio:20" + + /// Sets the ratio to 25 %. case quarter = "ratio:25" + + /// Sets the ratio to 33 %. case third = "ratio:33" + + /// Sets the ratio to 50 %. case half = "ratio:50" } @@ -477,4 +491,20 @@ public enum Tokens { case accordion = "style:accordion" case tab = "style:tab" } + + /// A state for the view. + public enum ViewState: String { + + /// Sets the state to active. + case active = "state:active" + + /// Sets the state to disabled. + case disabled = "state:disabled" + + /// Sets the state to hidden. + case hidden = "state:hidden" + + /// Sets the state to visible. + case visible = "state:visible" + } } diff --git a/Sources/HTMLKitComponents/Validator.swift b/Sources/HTMLKitComponents/Validator.swift new file mode 100644 index 00000000..6b74e4b3 --- /dev/null +++ b/Sources/HTMLKitComponents/Validator.swift @@ -0,0 +1,41 @@ +/* + Abstract: + The file contains the validator for the form validation. + */ + +import Foundation + +/// The validator for the form validation. +public struct Validator: Encodable { + + public enum Rule: String { + + /// The field must have a value. + case value = "value" + + /// The field must have a valid email format. + case email = "email" + + /// The field must have a valid number format. + case number = "number" + + /// The field must have a valid date format. + case date = "date" + + /// The field must have a valid url format. + case url = "url" + } + + /// The name of the field. + public var field: String + + /// The rule of validation. + public var rule: String + + /// Initiates a validator. + public init(field: String, rule: Rule) { + + self.field = field + self.rule = rule.rawValue + } +} diff --git a/Sources/HTMLKitVapor/Environment.swift b/Sources/HTMLKitVapor/Environment.swift new file mode 100644 index 00000000..e37342d9 --- /dev/null +++ b/Sources/HTMLKitVapor/Environment.swift @@ -0,0 +1,25 @@ +/* + Abstract: + The file contains the configuration for the environment. + */ + +import Foundation +import HTMLKit + +/// The environment +final public class Environment { + + /// The storage manager + internal var manager: Manager + + /// Initiates a environment + public init() { + + self.manager = Manager() + } + + /// Adds an encodable object to the storage + public func add(object: T) where T: Encodable { + manager.upsert(object, for: \T.self) + } +} diff --git a/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift b/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift index 24d6f81b..78e83d00 100644 --- a/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift +++ b/Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift @@ -16,36 +16,57 @@ extension Application { /// The vapor provider public struct HtmlKit { - internal struct LingoStorageKey: StorageKey { + internal struct LocalizationStorageKey: StorageKey { - public typealias Value = LingoConfiguration + public typealias Value = Localization + } + + internal struct EnvironmentStorageKey: StorageKey { + + public typealias Value = Environment } /// The view localization - public var lingo: LingoConfiguration { + public var localization: Localization { + + if let configuration = self.application.storage[LocalizationStorageKey.self] { + return configuration + } + + let configuration = Localization() - if let configuration = self.application.storage[LingoStorageKey.self] { + self.application.storage[LocalizationStorageKey.self] = configuration + + return configuration + } + + /// The view environment + public var environment: Environment { + + if let configuration = self.application.storage[EnvironmentStorageKey.self] { return configuration } - let configuration = LingoConfiguration() + let configuration = Environment() - self.application.storage[LingoStorageKey.self] = configuration + self.application.storage[EnvironmentStorageKey.self] = configuration return configuration } /// The view renderer internal var renderer: ViewRenderer { - return .init(eventLoop: self.application.eventLoopGroup.next(), lingo: lingo) + return .init(eventLoop: application.eventLoopGroup.next(), + localization: localization, + environment: environment) } /// The application dependency - public let application: Application + internal let application: Application /// Creates the provider public init(application: Application) { - + self.application = application } } @@ -55,6 +76,8 @@ extension Request { /// Access to the view renderer public var htmlkit: ViewRenderer { - return .init(eventLoop: self.eventLoop, lingo: self.application.htmlkit.lingo) + return .init(eventLoop: self.eventLoop, + localization: self.application.htmlkit.localization, + environment: self.application.htmlkit.environment) } } diff --git a/Sources/HTMLKitVapor/LingoConfiguration.swift b/Sources/HTMLKitVapor/Localization.swift similarity index 78% rename from Sources/HTMLKitVapor/LingoConfiguration.swift rename to Sources/HTMLKitVapor/Localization.swift index 12b864b7..b88d7e3a 100644 --- a/Sources/HTMLKitVapor/LingoConfiguration.swift +++ b/Sources/HTMLKitVapor/Localization.swift @@ -1,13 +1,13 @@ /* Abstract: - The file contains the configuration for Lingo. + The file contains the configuration for the localization. */ import Foundation import Lingo /// The localization -public class LingoConfiguration { +public class Localization { /// A enumeration of possible locale identifier public enum Locale: String { @@ -23,10 +23,10 @@ public class LingoConfiguration { case indonesian = "id" } - /// The root path + /// The path of the source directory internal var defaultDirectory: String - /// The locale indentifier + /// The default locale indentifier internal var defaultLocale: String internal var lingo: Lingo? { @@ -34,15 +34,15 @@ public class LingoConfiguration { } /// Creates a configuration - internal init() { + public init() { self.defaultDirectory = "Resources/Localization" self.defaultLocale = "en" } /// Sets the root path - public func set(directory: URL) { - self.defaultDirectory = directory.path + public func set(source: URL) { + self.defaultDirectory = source.path } /// Sets the default locale indentifier diff --git a/Sources/HTMLKitVapor/ViewRenderer.swift b/Sources/HTMLKitVapor/ViewRenderer.swift index c583284d..90344549 100644 --- a/Sources/HTMLKitVapor/ViewRenderer.swift +++ b/Sources/HTMLKitVapor/ViewRenderer.swift @@ -17,10 +17,10 @@ public class ViewRenderer { internal var renderer: Renderer /// Creates the view renderer - public init(eventLoop: EventLoop, lingo: LingoConfiguration) { + public init(eventLoop: EventLoop, localization: Localization, environment: Environment) { self.eventLoop = eventLoop - self.renderer = Renderer(lingo: lingo.lingo) + self.renderer = Renderer(lingo: localization.lingo, manager: environment.manager) } /// Renders a layout and its context diff --git a/Tests/HTMLKitComponentsTests/ComponentTests.swift b/Tests/HTMLKitComponentsTests/ComponentTests.swift index 68578b5a..75e8469a 100644 --- a/Tests/HTMLKitComponentsTests/ComponentTests.swift +++ b/Tests/HTMLKitComponentsTests/ComponentTests.swift @@ -83,10 +83,10 @@ final class ComponentTests: XCTestCase { ) } - func testFormContainer() throws { + func testForm() throws { let view = TestView { - HTMLKitComponents.Form(method: .post) { + Form(method: .post) { } } @@ -205,6 +205,21 @@ final class ComponentTests: XCTestCase { ) } + func testSelectField() throws { + + let view = TestView { + SelectField(name: "name") { + } + } + + XCTAssertEqual(try renderer.render(view: view), + """ + + """ + ) + } + func testImage() throws { let view = TestView { @@ -252,13 +267,13 @@ final class ComponentTests: XCTestCase { let view = TestView { Link(destination: "uri") { - "link" + "Link" } } XCTAssertEqual(try renderer.render(view: view), """ - link + Link """ ) } @@ -324,16 +339,13 @@ final class ComponentTests: XCTestCase { let view = TestView { Text { + "Text" } - .fontSize(.large) - .fontTransformation(.uppercase) - .foregroundColor(.blue) - .bold() } XCTAssertEqual(try renderer.render(view: view), """ -

+

Text

""" ) } @@ -400,7 +412,123 @@ final class ComponentTests: XCTestCase { XCTAssertEqual(try renderer.render(view: view), """ -
+
\ +
\ +
\ +
+ """ + ) + } + + func testCarousel() throws { + + let view = TestView { + Carousel { + } indication: { + } + } + + XCTAssertEqual(try renderer.render(view: view), + """ + + """ + ) + } + + func testSlide() throws { + + let view = TestView { + Slide(source: "#") { + } + } + + XCTAssertEqual(try renderer.render(view: view), + """ +
\ +
\ + \ +
\ +
\ +
+ """ + ) + } + + func testIndicator() throws { + + let view = TestView { + Indicator(for: "example") + } + + XCTAssertEqual(try renderer.render(view: view), + """ + + """ + ) + } + + func testDropdown() throws { + + let view = TestView { + Dropdown { + } label: { + } + + } + + XCTAssertEqual(try renderer.render(view: view), + """ + + """ + ) + } + + func testModal() throws { + + let view = TestView { + Modal { + } + } + + XCTAssertEqual(try renderer.render(view: view), + """ + + """ + ) + } + + func testScrollView() throws { + + let view = TestView { + ScrollView(direction: .horizontal) { + } + } + + XCTAssertEqual(try renderer.render(view: view), + """ +
+ """ + ) + } + + func testSymbol() throws { + + let view = TestView { + Symbol(system: "folder") + } + + XCTAssertEqual(try renderer.render(view: view), + """ + \ + \ + """ ) } diff --git a/Tests/HTMLKitVaporTests/ProviderTests.swift b/Tests/HTMLKitVaporTests/ProviderTests.swift index 69974f31..82aa6c20 100644 --- a/Tests/HTMLKitVaporTests/ProviderTests.swift +++ b/Tests/HTMLKitVaporTests/ProviderTests.swift @@ -157,8 +157,8 @@ final class ProviderTests: XCTestCase { defer { app.shutdown() } - app.htmlkit.lingo.set(directory: currentDirectory) - app.htmlkit.lingo.set(locale: .french) + app.htmlkit.localization.set(source: currentDirectory) + app.htmlkit.localization.set(locale: .french) app.get("test") { request async throws -> Vapor.View in