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 @@ -197,6 +197,10 @@ provided below is an organized table of W3C HTML tags and their equivalent Slips
[`<input type="range">`](https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)) | ``Slider``
[`<input type="file">`](https://html.spec.whatwg.org/multipage/input.html#file-upload-state-(type=file)) | ``FileInput``
[`<input type="hidden">`](https://html.spec.whatwg.org/multipage/input.html#hidden-state-(type=hidden)) | ``HiddenField``
[`<input type="submit">`](https://html.spec.whatwg.org/multipage/input.html#submit-button-state-(type=submit)) | ``SubmitButton``
[`<input type="image">`](https://html.spec.whatwg.org/multipage/input.html#image-button-state-(type=image)) | ``ImageButton``
[`<input type="reset">`](https://html.spec.whatwg.org/multipage/input.html#reset-button-state-(type=reset)) | ``ResetButton``
[`<input type="button">`](https://html.spec.whatwg.org/multipage/input.html#button-state-(type=button)) | ``InputButton``
[`<button>`](https://html.spec.whatwg.org/multipage/sections.html#the-button-element) | ``Button``
[`<select>`](https://html.spec.whatwg.org/multipage/sections.html#the-select-element) | ``Picker``
[`<datalist>`](https://html.spec.whatwg.org/multipage/sections.html#the-datalist-element) | ``DataList``
Expand Down
66 changes: 66 additions & 0 deletions Sources/Slipstream/W3C/Elements/Forms/ImageButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import SwiftSoup

/// A graphical submit button.
///
/// An image button is a submit button that uses an image instead of text.
/// When clicked, it submits the form and also sends the x and y coordinates
/// of where the user clicked within the image.
///
/// ```swift
/// Form(action: "/submit", method: .post) {
/// TextField("Enter your name", name: "username")
/// ImageButton(src: "/images/submit.png", alt: "Submit")
/// }
/// ```
///
/// - SeeAlso: W3C [input type="image"](https://html.spec.whatwg.org/multipage/input.html#image-button-state-(type=image)) specification.
@available(iOS 17.0, macOS 14.0, *)
public struct ImageButton: View {
/// Creates an image button.
///
/// - Parameters:
/// - src: The URL of the image to display.
/// - alt: Alternative text for the image, used for accessibility.
/// - name: The name of the form control, as used in form submission.
/// - width: The width of the image in pixels.
/// - height: The height of the image in pixels.
/// - autoFocus: If true, indicates that the view should be focused as soon as
/// the page is loaded, allowing the user to interact with it without having to
/// manually focus the main view.
public init(src: String, alt: String, name: String? = nil, width: Int? = nil, height: Int? = nil, autoFocus: Bool = false) {
self.src = src
self.alt = alt
self.name = name
self.width = width
self.height = height
self.autoFocus = autoFocus
}

@_documentation(visibility: private)
public func render(_ container: Element, environment: EnvironmentValues) throws {
let element = try container.appendElement("input")
try element.attr("type", "image")
try element.attr("src", src)
try element.attr("alt", alt)

if let name {
try element.attr("name", name)
}
if let width {
try element.attr("width", String(width))
}
if let height {
try element.attr("height", String(height))
}
if autoFocus {
try element.attr("autofocus", "")
}
}

private let src: String
private let alt: String
private let name: String?
private let width: Int?
private let height: Int?
private let autoFocus: Bool
}
55 changes: 55 additions & 0 deletions Sources/Slipstream/W3C/Elements/Forms/InputButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import SwiftSoup

/// A button with no default behavior.
///
/// An input button is a button that has no default behavior and is typically
/// used with JavaScript to implement custom functionality.
///
/// ```swift
/// Form {
/// TextField("Enter your name", name: "username")
/// InputButton("Click me", action: "handleClick()")
/// }
/// ```
///
/// - SeeAlso: W3C [input type="button"](https://html.spec.whatwg.org/multipage/input.html#button-state-(type=button)) specification.
@available(iOS 17.0, macOS 14.0, *)
public struct InputButton: View {
/// Creates an input button with a label.
///
/// - Parameters:
/// - text: The text label to display on the button.
/// - action: JavaScript code to execute when the button is clicked.
/// - name: The name of the form control, as used in form submission.
/// - autoFocus: If true, indicates that the view should be focused as soon as
/// the page is loaded, allowing the user to interact with it without having to
/// manually focus the main view.
public init(_ text: String, action: String? = nil, name: String? = nil, autoFocus: Bool = false) {
self.text = text
self.action = action
self.name = name
self.autoFocus = autoFocus
}

@_documentation(visibility: private)
public func render(_ container: Element, environment: EnvironmentValues) throws {
let element = try container.appendElement("input")
try element.attr("type", "button")
try element.attr("value", text)

if let action {
try element.attr("onclick", action)
}
if let name {
try element.attr("name", name)
}
if autoFocus {
try element.attr("autofocus", "")
}
}

private let text: String
private let action: String?
private let name: String?
private let autoFocus: Bool
}
48 changes: 48 additions & 0 deletions Sources/Slipstream/W3C/Elements/Forms/ResetButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import SwiftSoup

/// A button that resets its form.
///
/// A reset button restores all form controls in the form to their default values.
///
/// ```swift
/// Form(action: "/submit", method: .post) {
/// TextField("Enter your name", name: "username")
/// ResetButton("Clear form")
/// }
/// ```
///
/// - SeeAlso: W3C [input type="reset"](https://html.spec.whatwg.org/multipage/input.html#reset-button-state-(type=reset)) specification.
@available(iOS 17.0, macOS 14.0, *)
public struct ResetButton: View {
/// Creates a reset button with a label.
///
/// - Parameters:
/// - text: The text label to display on the button.
/// - name: The name of the form control, as used in form submission.
/// - autoFocus: If true, indicates that the view should be focused as soon as
/// the page is loaded, allowing the user to interact with it without having to
/// manually focus the main view.
public init(_ text: String, name: String? = nil, autoFocus: Bool = false) {
self.text = text
self.name = name
self.autoFocus = autoFocus
}

@_documentation(visibility: private)
public func render(_ container: Element, environment: EnvironmentValues) throws {
let element = try container.appendElement("input")
try element.attr("type", "reset")
try element.attr("value", text)

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

private let text: String
private let name: String?
private let autoFocus: Bool
}
48 changes: 48 additions & 0 deletions Sources/Slipstream/W3C/Elements/Forms/SubmitButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import SwiftSoup

/// A button that submits its form.
///
/// A submit button is a button that submits the form data to the server when clicked.
///
/// ```swift
/// Form(action: "/submit", method: .post) {
/// TextField("Enter your name", name: "username")
/// SubmitButton("Sign up")
/// }
/// ```
///
/// - SeeAlso: W3C [input type="submit"](https://html.spec.whatwg.org/multipage/input.html#submit-button-state-(type=submit)) specification.
@available(iOS 17.0, macOS 14.0, *)
public struct SubmitButton: View {
/// Creates a submit button with a label.
///
/// - Parameters:
/// - text: The text label to display on the button.
/// - name: The name of the form control, as used in form submission.
/// - autoFocus: If true, indicates that the view should be focused as soon as
/// the page is loaded, allowing the user to interact with it without having to
/// manually focus the main view.
public init(_ text: String, name: String? = nil, autoFocus: Bool = false) {
self.text = text
self.name = name
self.autoFocus = autoFocus
}

@_documentation(visibility: private)
public func render(_ container: Element, environment: EnvironmentValues) throws {
let element = try container.appendElement("input")
try element.attr("type", "submit")
try element.attr("value", text)

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

private let text: String
private let name: String?
private let autoFocus: Bool
}
29 changes: 29 additions & 0 deletions Tests/SlipstreamTests/W3C/Forms/ImageButtonTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Testing

import Slipstream

struct ImageButtonTests {
@Test func basic() throws {
try #expect(renderHTML(ImageButton(src: "/submit.png", alt: "Submit")) == #"<input type="image" src="/submit.png" alt="Submit" />"#)
}

@Test func withName() throws {
try #expect(renderHTML(ImageButton(src: "/submit.png", alt: "Submit", name: "submit-btn")) == #"<input type="image" src="/submit.png" alt="Submit" name="submit-btn" />"#)
}

@Test func withDimensions() throws {
try #expect(renderHTML(ImageButton(src: "/submit.png", alt: "Submit", width: 100, height: 50)) == #"<input type="image" src="/submit.png" alt="Submit" width="100" height="50" />"#)
}

@Test func withId() throws {
try #expect(renderHTML(ImageButton(src: "/submit.png", alt: "Submit").id("submit-button")) == #"<input type="image" src="/submit.png" alt="Submit" id="submit-button" />"#)
}

@Test func autoFocus() throws {
try #expect(renderHTML(ImageButton(src: "/submit.png", alt: "Submit", autoFocus: true)) == #"<input type="image" src="/submit.png" alt="Submit" autofocus />"#)
}

@Test func allAttributes() throws {
try #expect(renderHTML(ImageButton(src: "/register.png", alt: "Register", name: "register", width: 120, height: 60, autoFocus: true).id("register-btn")) == #"<input type="image" src="/register.png" alt="Register" name="register" width="120" height="60" autofocus id="register-btn" />"#)
}
}
29 changes: 29 additions & 0 deletions Tests/SlipstreamTests/W3C/Forms/InputButtonTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Testing

import Slipstream

struct InputButtonTests {
@Test func basic() throws {
try #expect(renderHTML(InputButton("Click me")) == #"<input type="button" value="Click me" />"#)
}

@Test func withAction() throws {
try #expect(renderHTML(InputButton("Click me", action: "handleClick()")) == #"<input type="button" value="Click me" onclick="handleClick()" />"#)
}

@Test func withName() throws {
try #expect(renderHTML(InputButton("Click me", name: "action-btn")) == #"<input type="button" value="Click me" name="action-btn" />"#)
}

@Test func withId() throws {
try #expect(renderHTML(InputButton("Click me").id("action-button")) == #"<input type="button" value="Click me" id="action-button" />"#)
}

@Test func autoFocus() throws {
try #expect(renderHTML(InputButton("Click me", autoFocus: true)) == #"<input type="button" value="Click me" autofocus />"#)
}

@Test func allAttributes() throws {
try #expect(renderHTML(InputButton("Do something", action: "doSomething()", name: "action", autoFocus: true).id("action-btn")) == #"<input type="button" value="Do something" onclick="doSomething()" name="action" autofocus id="action-btn" />"#)
}
}
25 changes: 25 additions & 0 deletions Tests/SlipstreamTests/W3C/Forms/ResetButtonTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Testing

import Slipstream

struct ResetButtonTests {
@Test func basic() throws {
try #expect(renderHTML(ResetButton("Reset")) == #"<input type="reset" value="Reset" />"#)
}

@Test func withName() throws {
try #expect(renderHTML(ResetButton("Reset", name: "reset-btn")) == #"<input type="reset" value="Reset" name="reset-btn" />"#)
}

@Test func withId() throws {
try #expect(renderHTML(ResetButton("Reset").id("reset-button")) == #"<input type="reset" value="Reset" id="reset-button" />"#)
}

@Test func autoFocus() throws {
try #expect(renderHTML(ResetButton("Reset", autoFocus: true)) == #"<input type="reset" value="Reset" autofocus />"#)
}

@Test func allAttributes() throws {
try #expect(renderHTML(ResetButton("Clear form", name: "clear", autoFocus: true).id("clear-btn")) == #"<input type="reset" value="Clear form" name="clear" autofocus id="clear-btn" />"#)
}
}
25 changes: 25 additions & 0 deletions Tests/SlipstreamTests/W3C/Forms/SubmitButtonTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Testing

import Slipstream

struct SubmitButtonTests {
@Test func basic() throws {
try #expect(renderHTML(SubmitButton("Submit")) == #"<input type="submit" value="Submit" />"#)
}

@Test func withName() throws {
try #expect(renderHTML(SubmitButton("Submit", name: "submit-btn")) == #"<input type="submit" value="Submit" name="submit-btn" />"#)
}

@Test func withId() throws {
try #expect(renderHTML(SubmitButton("Submit").id("submit-button")) == #"<input type="submit" value="Submit" id="submit-button" />"#)
}

@Test func autoFocus() throws {
try #expect(renderHTML(SubmitButton("Submit", autoFocus: true)) == #"<input type="submit" value="Submit" autofocus />"#)
}

@Test func allAttributes() throws {
try #expect(renderHTML(SubmitButton("Register", name: "register", autoFocus: true).id("register-btn")) == #"<input type="submit" value="Register" name="register" autofocus id="register-btn" />"#)
}
}