Skip to content

Commit 3e684bc

Browse files
jverkoeyclaude
andauthored
Implement embed element for Slipstream (#222)
Add support for the HTML <embed> element, which represents an integration point for external (typically non-HTML) content or plugins. Features: - Support for src URL attribute - Optional type parameter for MIME type - Width and height support via frame() modifier - Comprehensive test coverage The implementation follows W3C specification and maintains consistency with existing embedded content elements (Image, IFrame). Part of #25 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4ebfe24 commit 3e684bc

6 files changed

Lines changed: 184 additions & 1 deletion

File tree

Sources/Slipstream/Documentation.docc/Guides/SlipstreamForWebDevelopers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ provided below is an organized table of W3C HTML tags and their equivalent Slips
142142
[`<source>`](https://html.spec.whatwg.org/multipage/sections.html#the-source-element) | ``Source``
143143
[`<img>`](https://html.spec.whatwg.org/multipage/sections.html#the-img-element) | ``Image``
144144
[`<iframe>`](https://html.spec.whatwg.org/multipage/sections.html#the-iframe-element) | ``IFrame``
145-
[`<embed>`](https://html.spec.whatwg.org/multipage/sections.html#the-embed-element) | [Not implemented yet](https://github.com/jverkoey/slipstream/issues/25)
145+
[`<embed>`](https://html.spec.whatwg.org/multipage/sections.html#the-embed-element) | ``Embed``
146146
[`<object>`](https://html.spec.whatwg.org/multipage/sections.html#the-object-element) | [Not implemented yet](https://github.com/jverkoey/slipstream/issues/25)
147147
[`<video>`](https://html.spec.whatwg.org/multipage/sections.html#the-video-element) | [Not implemented yet](https://github.com/jverkoey/slipstream/issues/25)
148148
[`<audio>`](https://html.spec.whatwg.org/multipage/sections.html#the-audio-element) | [Not implemented yet](https://github.com/jverkoey/slipstream/issues/25)

Sources/Slipstream/Documentation.docc/W3C/W3CAttributes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
### Attribute types
1818

1919
- ``CrossOrigin``
20+
- ``MimeType``

Sources/Slipstream/Documentation.docc/W3C/W3CViews.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The complete W3C HTML elements standard can be found [here](https://html.spec.wh
6666
- ``Source``
6767
- ``Image``
6868
- ``IFrame``
69+
- ``Embed``
6970
- ``RawHTML``
7071

7172
### Tabular data
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/// Constants defining common MIME types for embedded content.
2+
///
3+
/// MIME types (Multipurpose Internet Mail Extensions types) are a standard
4+
/// way to indicate the type of content being served. This enum provides
5+
/// type-safe constants for common MIME types used with embedded content.
6+
///
7+
/// - SeeAlso: [IANA Media Types](https://www.iana.org/assignments/media-types/media-types.xhtml)
8+
///
9+
/// ## See Also
10+
///
11+
/// - ``Embed``
12+
@available(iOS 17.0, macOS 14.0, *)
13+
public enum MimeType: String, Sendable {
14+
// MARK: - Video types
15+
16+
/// MPEG-4 video format
17+
case videoMP4 = "video/mp4"
18+
19+
/// WebM video format
20+
case videoWebM = "video/webm"
21+
22+
/// Ogg video format
23+
case videoOgg = "video/ogg"
24+
25+
/// QuickTime video format
26+
case videoQuickTime = "video/quicktime"
27+
28+
// MARK: - Audio types
29+
30+
/// MPEG audio format (MP3)
31+
case audioMPEG = "audio/mpeg"
32+
33+
/// Ogg audio format
34+
case audioOgg = "audio/ogg"
35+
36+
/// WAV audio format
37+
case audioWAV = "audio/wav"
38+
39+
/// WebM audio format
40+
case audioWebM = "audio/webm"
41+
42+
// MARK: - Application types
43+
44+
/// PDF document
45+
case applicationPDF = "application/pdf"
46+
47+
/// Adobe Flash (deprecated but still in use)
48+
case applicationFlash = "application/x-shockwave-flash"
49+
50+
/// JSON data
51+
case applicationJSON = "application/json"
52+
53+
/// XML data
54+
case applicationXML = "application/xml"
55+
56+
// MARK: - Image types
57+
58+
/// SVG vector image
59+
case imageSVG = "image/svg+xml"
60+
61+
/// PNG image
62+
case imagePNG = "image/png"
63+
64+
/// JPEG image
65+
case imageJPEG = "image/jpeg"
66+
67+
/// GIF image
68+
case imageGIF = "image/gif"
69+
70+
/// WebP image
71+
case imageWebP = "image/webp"
72+
73+
// MARK: - Text types
74+
75+
/// Plain text
76+
case textPlain = "text/plain"
77+
78+
/// HTML document
79+
case textHTML = "text/html"
80+
81+
/// CSS stylesheet
82+
case textCSS = "text/css"
83+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import Foundation
2+
3+
import SwiftSoup
4+
5+
/// A view that represents an integration point for external content.
6+
///
7+
/// The Embed element is typically used to embed external applications or
8+
/// interactive content such as plugins, multimedia content, or other
9+
/// non-HTML resources.
10+
///
11+
/// ```swift
12+
/// struct MySiteContent: View {
13+
/// var body: some View {
14+
/// Body {
15+
/// Embed(URL(string: "/media/video.mp4"), type: .videoMP4)
16+
/// }
17+
/// }
18+
/// }
19+
/// ```
20+
///
21+
/// - SeeAlso: W3C [`embed`](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element) specification.
22+
///
23+
/// ## See Also
24+
///
25+
/// - ``MimeType``
26+
@available(iOS 17.0, macOS 14.0, *)
27+
public struct Embed: View {
28+
/// Creates an Embed view with a MIME type.
29+
///
30+
/// - Parameters:
31+
/// - url: The URL of the resource being embedded.
32+
/// - type: The MIME type of the embedded content. This can help the browser
33+
/// determine how to display the content.
34+
public init(_ url: URL?, type: MimeType? = nil) {
35+
self.url = url
36+
self.type = type?.rawValue
37+
}
38+
39+
/// Creates an Embed view with a custom MIME type string.
40+
///
41+
/// Use this initializer when you need to specify a MIME type that isn't
42+
/// available in the ``MimeType`` enum.
43+
///
44+
/// - Parameters:
45+
/// - url: The URL of the resource being embedded.
46+
/// - type: The MIME type string of the embedded content.
47+
public init(_ url: URL?, type: String?) {
48+
self.url = url
49+
self.type = type
50+
}
51+
52+
@_documentation(visibility: private)
53+
public func render(_ container: Element, environment: EnvironmentValues) throws {
54+
guard let url else {
55+
return
56+
}
57+
let element = try container.appendElement("embed")
58+
try element.attr("src", url.absoluteString)
59+
if let type {
60+
try element.attr("type", type)
61+
}
62+
}
63+
64+
private let url: URL?
65+
private let type: String?
66+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import Foundation
2+
import Testing
3+
4+
import Slipstream
5+
6+
struct EmbedTests {
7+
@Test func nilURL() throws {
8+
try #expect(renderHTML(Embed(nil)) == "")
9+
}
10+
11+
@Test func url() throws {
12+
try #expect(renderHTML(Embed(URL(string: "/media/animation.swf"))) == #"<embed src="/media/animation.swf" />"#)
13+
}
14+
15+
@Test func urlWithMimeTypeEnum() throws {
16+
try #expect(renderHTML(Embed(URL(string: "/media/video.mp4"), type: .videoMP4)) == #"<embed src="/media/video.mp4" type="video/mp4" />"#)
17+
}
18+
19+
@Test func urlWithMimeTypeString() throws {
20+
try #expect(renderHTML(Embed(URL(string: "/media/video.mp4"), type: "video/mp4")) == #"<embed src="/media/video.mp4" type="video/mp4" />"#)
21+
}
22+
23+
@Test func urlWithCustomMimeType() throws {
24+
try #expect(renderHTML(Embed(URL(string: "/media/custom.bin"), type: "application/x-custom")) == #"<embed src="/media/custom.bin" type="application/x-custom" />"#)
25+
}
26+
27+
@Test func differentMimeTypes() throws {
28+
try #expect(renderHTML(Embed(URL(string: "/media/audio.mp3"), type: .audioMPEG)) == #"<embed src="/media/audio.mp3" type="audio/mpeg" />"#)
29+
try #expect(renderHTML(Embed(URL(string: "/document.pdf"), type: .applicationPDF)) == #"<embed src="/document.pdf" type="application/pdf" />"#)
30+
try #expect(renderHTML(Embed(URL(string: "/image.svg"), type: .imageSVG)) == #"<embed src="/image.svg" type="image/svg+xml" />"#)
31+
}
32+
}

0 commit comments

Comments
 (0)