Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ provided below is an organized table of W3C HTML tags and their equivalent Slips
[`<data>`](https://html.spec.whatwg.org/multipage/sections.html#the-data-element) | ``Data``
[`<time>`](https://html.spec.whatwg.org/multipage/sections.html#the-time-element) | ``Time``
[`<code>`](https://html.spec.whatwg.org/multipage/sections.html#the-code-element) | ``Code``
[`<var>`](https://html.spec.whatwg.org/multipage/sections.html#the-var-element) | [Not implemented yet](https://github.com/jverkoey/slipstream/issues/25)
[`<var>`](https://html.spec.whatwg.org/multipage/sections.html#the-var-element) | ``Variable``
[`<samp>`](https://html.spec.whatwg.org/multipage/sections.html#the-samp-element) | ``SampleOutput``
[`<kbd>`](https://html.spec.whatwg.org/multipage/sections.html#the-kbd-element) | ``Keyboard``
[`<sub>`](https://html.spec.whatwg.org/multipage/sections.html#the-sub-element) | ``Subscript``
Expand All @@ -133,7 +133,7 @@ provided below is an organized table of W3C HTML tags and their equivalent Slips
[`<bdo>`](https://html.spec.whatwg.org/multipage/sections.html#the-bdo-element) | ``BidirectionalOverride``
[`<span>`](https://html.spec.whatwg.org/multipage/sections.html#the-span-element) | ``Span``
[`<br>`](https://html.spec.whatwg.org/multipage/sections.html#the-br-element) | ``Linebreak``
[`<wbr>`](https://html.spec.whatwg.org/multipage/sections.html#the-wbr-element) | [Not implemented yet](https://github.com/jverkoey/slipstream/issues/25)
[`<wbr>`](https://html.spec.whatwg.org/multipage/sections.html#the-wbr-element) | ``WordBreakOpportunity``

### Edits

Expand Down Expand Up @@ -206,7 +206,7 @@ provided below is an organized table of W3C HTML tags and their equivalent Slips
[`<datalist>`](https://html.spec.whatwg.org/multipage/sections.html#the-datalist-element) | ``DataList``
[`<optgroup>`](https://html.spec.whatwg.org/multipage/sections.html#the-optgroup-element) | ``OptGroup``
[`<option>`](https://html.spec.whatwg.org/multipage/sections.html#the-option-element) | ``Option``
[`<textarea>`](https://html.spec.whatwg.org/multipage/sections.html#the-textarea-element) | [Not implemented yet](https://github.com/jverkoey/slipstream/issues/25)
[`<textarea>`](https://html.spec.whatwg.org/multipage/sections.html#the-textarea-element) | ``TextArea``
[`<output>`](https://html.spec.whatwg.org/multipage/sections.html#the-output-element) | ``Output``
[`<progress>`](https://html.spec.whatwg.org/multipage/sections.html#the-progress-element) | ``ProgressView``
[`<meter>`](https://html.spec.whatwg.org/multipage/sections.html#the-meter-element) | ``Meter``
Expand Down
98 changes: 98 additions & 0 deletions Sources/Slipstream/W3C/Elements/Forms/TextArea.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import SwiftSoup

/// A multi-line plain-text editing control.
///
/// The TextArea element represents a multi-line plain-text edit control for the
/// element's raw value. It's particularly useful for entering longer pieces of
/// text, such as comments or messages.
///
/// ```swift
/// struct MySiteContent: View {
/// var body: some View {
/// Body {
/// Form {
/// TextArea("Enter your comment...", name: "comment")
/// }
/// }
/// }
/// }
/// ```
///
/// - SeeAlso: W3C [`textarea`](https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element) specification.
@available(iOS 17.0, macOS 14.0, *)
public struct TextArea<Content>: View where Content: View {
/// Creates a TextArea with custom content.
///
/// - Parameters:
/// - name: The name of the form control, as used in form submission.
/// - rows: The number of visible text lines for the control.
/// - columns: The expected maximum number of characters per line.
/// - placeholder: A hint to the user of what can be entered in the control.
/// - content: A view builder that creates the textarea's content.
public init(
name: String? = nil,
rows: Int? = nil,
columns: Int? = nil,
placeholder: String? = nil,
@ViewBuilder content: @escaping @Sendable () -> Content
) {
self.name = name
self.rows = rows
self.columns = columns
self.placeholder = placeholder
self.content = content
}

/// Creates a TextArea with static text content.
///
/// - Parameters:
/// - text: The default text content to display in the textarea.
/// - name: The name of the form control, as used in form submission.
/// - rows: The number of visible text lines for the control.
/// - columns: The expected maximum number of characters per line.
/// - placeholder: A hint to the user of what can be entered in the control.
public init(
_ text: String = "",
name: String? = nil,
rows: Int? = nil,
columns: Int? = nil,
placeholder: String? = nil
) where Content == DOMString {
self.name = name
self.rows = rows
self.columns = columns
self.placeholder = placeholder
self.content = {
DOMString(text)
}
}

@_documentation(visibility: private)
public func render(_ container: Element, environment: EnvironmentValues) throws {
let element = try container.appendElement("textarea")

if let name {
try element.attr("name", name)
}

if let rows {
try element.attr("rows", String(rows))
}

if let columns {
try element.attr("cols", String(columns))
}

if let placeholder {
try element.attr("placeholder", placeholder)
}

try self.content().render(element, environment: environment)
}

private let name: String?
private let rows: Int?
private let columns: Int?
private let placeholder: String?
@ViewBuilder private let content: @Sendable () -> Content
}
43 changes: 43 additions & 0 deletions Sources/Slipstream/W3C/Elements/TextLevelSemantics/Variable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// A view that represents a variable in mathematical expressions or programming contexts.
///
/// The Variable element represents the name of a variable, parameter, or placeholder
/// in mathematical expressions or programming contexts.
///
/// ```swift
/// struct MySiteContent: View {
/// var body: some View {
/// Body {
/// Paragraph {
/// DOMString("The equation ")
/// Variable("x")
/// DOMString(" + ")
/// Variable("y")
/// DOMString(" = ")
/// Variable("z")
/// }
/// }
/// }
/// }
/// ```
///
/// - SeeAlso: W3C [`var`](https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-var-element) specification.
@available(iOS 17.0, macOS 14.0, *)
public struct Variable<Content>: W3CElement where Content: View {
@_documentation(visibility: private)
public let tagName: String = "var"

@_documentation(visibility: private)
@ViewBuilder public let content: @Sendable () -> Content

/// Creates a Variable view with custom content.
public init(@ViewBuilder content: @escaping @Sendable () -> Content) {
self.content = content
}

/// Creates a Variable view with static text.
public init(_ text: String) where Content == DOMString {
self.content = {
DOMString(text)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import SwiftSoup

/// A view that represents a word break opportunity.
///
/// The WordBreakOpportunity element represents a position within text where the browser
/// may optionally break a line, though its line-breaking rules would not otherwise
/// create a break at that location.
///
/// ```swift
/// struct MySiteContent: View {
/// var body: some View {
/// Body {
/// Paragraph {
/// DOMString("This")
/// WordBreakOpportunity()
/// DOMString("IsA")
/// WordBreakOpportunity()
/// DOMString("Very")
/// WordBreakOpportunity()
/// DOMString("Long")
/// WordBreakOpportunity()
/// DOMString("Word")
/// }
/// }
/// }
/// }
/// ```
///
/// - SeeAlso: W3C [`wbr`](https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-wbr-element) specification.
@available(iOS 17.0, macOS 14.0, *)
public struct WordBreakOpportunity: View {
/// Creates a WordBreakOpportunity view.
public init() {
}

@_documentation(visibility: private)
public func render(_ container: Element, environment: EnvironmentValues) throws {
_ = try container.appendElement("wbr")
}
}
49 changes: 49 additions & 0 deletions Tests/SlipstreamTests/W3C/TextAreaTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Testing

import Slipstream

struct TextAreaTests {
@Test func emptyTextArea() throws {
try #expect(renderHTML(TextArea()) == "<textarea></textarea>")
}

@Test func withText() throws {
try #expect(renderHTML(TextArea("Default text")) == "<textarea>Default text</textarea>")
}

@Test func withName() throws {
try #expect(renderHTML(TextArea(name: "comment")) == #"<textarea name="comment"></textarea>"#)
}

@Test func withRows() throws {
try #expect(renderHTML(TextArea(rows: 5)) == #"<textarea rows="5"></textarea>"#)
}

@Test func withColumns() throws {
try #expect(renderHTML(TextArea(columns: 40)) == #"<textarea cols="40"></textarea>"#)
}

@Test func withPlaceholder() throws {
try #expect(renderHTML(TextArea(placeholder: "Enter your comment...")) == #"<textarea placeholder="Enter your comment..."></textarea>"#)
}

@Test func withAllAttributes() throws {
try #expect(renderHTML(TextArea(
"Initial text",
name: "feedback",
rows: 10,
columns: 50,
placeholder: "Your feedback here..."
)) == #"<textarea name="feedback" rows="10" cols="50" placeholder="Your feedback here...">Initial text</textarea>"#)
}

@Test func withContent() throws {
try #expect(renderHTML(TextArea(name: "comment") {
DOMString("This is a comment")
}) == #"<textarea name="comment">This is a comment</textarea>"#)
}

@Test func globalAttribute() throws {
try #expect(renderHTML(TextArea("Text").language("en")) == #"<textarea lang="en">Text</textarea>"#)
}
}
23 changes: 23 additions & 0 deletions Tests/SlipstreamTests/W3C/VariableTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Testing

import Slipstream

struct VariableTests {
@Test func emptyVariable() throws {
try #expect(renderHTML(Variable {}) == "<var></var>")
}

@Test func withText() throws {
try #expect(renderHTML(Variable("x")) == "<var>x</var>")
}

@Test func withContent() throws {
try #expect(renderHTML(Variable {
DOMString("variable")
}) == "<var>variable</var>")
}

@Test func globalAttribute() throws {
try #expect(renderHTML(Variable("y").language("en")) == #"<var lang="en">y</var>"#)
}
}
13 changes: 13 additions & 0 deletions Tests/SlipstreamTests/W3C/WordBreakOpportunityTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Testing

import Slipstream

struct WordBreakOpportunityTests {
@Test func render() throws {
try #expect(renderHTML(WordBreakOpportunity()) == "<wbr />")
}

@Test func globalAttribute() throws {
try #expect(renderHTML(WordBreakOpportunity().language("en")) == #"<wbr lang="en" />"#)
}
}