Support <meta> and <title> in DOMFiberRenderer

This commit is contained in:
Carson Katri 2022-07-06 10:02:31 -04:00
parent 4e8b84e4a1
commit 1c3603ae40
8 changed files with 88 additions and 4 deletions

View File

@ -15,6 +15,11 @@
// Created by Carson Katri on 7/16/20.
//
@_spi(TokamakCore)
public struct _WindowGroupTitle: _PrimitiveView {
public let title: Text?
}
public struct WindowGroup<Content>: Scene, TitledScene where Content: View {
public let id: String
public let title: Text?
@ -77,6 +82,10 @@ public struct WindowGroup<Content>: Scene, TitledScene where Content: View {
// }
public func _visitChildren<V>(_ visitor: V) where V: SceneVisitor {
visitor.visit(content)
print("Visiting scene")
visitor.visit(Group {
_WindowGroupTitle(title: self.title)
content
})
}
}

View File

@ -423,7 +423,8 @@ public extension FiberReconciler {
environment: .init(rootEnvironment),
traits: .init(),
preferenceStore: preferences
)
),
preferenceStore: preferences ?? .init()
)
if let preferenceStore = outputs.preferenceStore {
preferences = preferenceStore

View File

@ -69,6 +69,10 @@ public final class FiberReconciler<Renderer: FiberRenderer> {
.environmentValues(environment)
}
}
static func _makeView(_ inputs: ViewInputs<Self>) -> ViewOutputs {
.init(inputs: inputs, preferenceStore: inputs.preferenceStore ?? .init())
}
}
/// The `Layout` container for the root of a `View` hierarchy.
@ -275,6 +279,10 @@ public final class FiberReconciler<Renderer: FiberRenderer> {
for action in afterReconcileActions {
action()
}
if let preferences = current.preferences {
renderer.preferencesChanged(preferences)
}
}
}

View File

@ -88,6 +88,9 @@ public protocol FiberRenderer {
/// (in this case just `DuelOfTheStates` as both properties were on it),
/// and reconcile after all changes have been collected.
func schedule(_ action: @escaping () -> ())
/// Called by the reconciler when the preferences of the topmost `Fiber` changed.
func preferencesChanged(_ preferenceStore: _PreferenceStore)
}
public extension FiberRenderer {
@ -107,6 +110,8 @@ public extension FiberRenderer {
}
}
func preferencesChanged(_ preferenceStore: _PreferenceStore) {}
@discardableResult
@_disfavoredOverload
func render<V: View>(_ view: V) -> FiberReconciler<Self> {

View File

@ -118,6 +118,19 @@ public final class _PreferenceStore: CustomDebugStringConvertible {
_PreferenceValue(storage: previousValues[ObjectIdentifier(key)] ?? .init(key))
}
/// Returns the new value for `Key`, or `nil` if the value did not change.
public func newValue<Key>(forKey key: Key.Type = Key.self) -> Key.Value?
where Key: PreferenceKey, Key.Value: Equatable
{
let value = value(forKey: key).value
let previousValue = previousValue(forKey: key).value
if value != previousValue {
return value
} else {
return nil
}
}
public func insert<Key>(_ value: Key.Value, forKey key: Key.Type = Key.self)
where Key: PreferenceKey
{

View File

@ -323,6 +323,38 @@ public struct DOMFiberRenderer: FiberRenderer {
}
}
final class Head {
let head = document.head.object!
var metaTags = [JSObject]()
var title: JSObject?
}
private let head = Head()
public func preferencesChanged(_ preferenceStore: _PreferenceStore) {
if let newMetaTags = preferenceStore.newValue(forKey: HTMLMetaPreferenceKey.self) {
for oldTag in head.metaTags {
_ = head.head.removeChild!(oldTag)
}
head.metaTags = newMetaTags.map {
let template = document.createElement!("template").object!
template.innerHTML = .string($0.outerHTML())
let meta = template.content.firstChild.object!
_ = head.head.appendChild!(meta)
return meta
}
}
if let newTitle = preferenceStore.newValue(forKey: HTMLTitlePreferenceKey.self) {
if let title = head.title {
title.innerHTML = .string(newTitle)
} else {
let node = document.createElement!("title").object!
_ = head.head.appendChild!(node)
node.innerHTML = .string(newTitle)
head.title = node
}
}
}
private let scheduler = JSScheduler()
public func schedule(_ action: @escaping () -> ()) {
scheduler.schedule(options: nil, action)

View File

@ -15,10 +15,26 @@
// Created by Carson Katri on 7/19/20.
//
import TokamakCore
@_spi(TokamakCore) import TokamakCore
extension WindowGroup: SceneDeferredToRenderer {
public var deferredBody: AnyView {
AnyView(content)
}
}
extension _WindowGroupTitle: HTMLConvertible {
public var tag: String { "div" }
public func attributes(useDynamicLayout: Bool) -> [HTMLAttribute: String] {
guard !useDynamicLayout else { return [:] }
return ["style": "position: absolute; width: 0; height: 0; top: 0; left: 0;"]
}
public func primitiveVisitor<V>(useDynamicLayout: Bool) -> ((V) -> ())? where V: ViewVisitor {
{
if let title = self.title {
$0.visit(HTMLTitle(_TextProxy(title).rawText))
}
}
}
}

View File

@ -58,7 +58,7 @@ struct HTMLBody: AnyHTML {
]
}
extension HTMLMeta.MetaTag {
public extension HTMLMeta.MetaTag {
func outerHTML() -> String {
switch self {
case let .charset(charset):