@swiftbrowser/react 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +435 -2
  2. package/dist/binding.d.ts +9 -0
  3. package/dist/binding.d.ts.map +1 -0
  4. package/dist/bridge.d.ts +17 -0
  5. package/dist/bridge.d.ts.map +1 -0
  6. package/dist/components/animators.d.ts +40 -0
  7. package/dist/components/animators.d.ts.map +1 -0
  8. package/dist/components/canvas.d.ts +138 -0
  9. package/dist/components/canvas.d.ts.map +1 -0
  10. package/dist/components/charts.d.ts +100 -0
  11. package/dist/components/charts.d.ts.map +1 -0
  12. package/dist/components/containers.d.ts +78 -0
  13. package/dist/components/containers.d.ts.map +1 -0
  14. package/dist/components/controls.d.ts +106 -0
  15. package/dist/components/controls.d.ts.map +1 -0
  16. package/dist/components/geometry.d.ts +131 -0
  17. package/dist/components/geometry.d.ts.map +1 -0
  18. package/dist/components/host.d.ts +24 -0
  19. package/dist/components/host.d.ts.map +1 -0
  20. package/dist/components/images.d.ts +18 -0
  21. package/dist/components/images.d.ts.map +1 -0
  22. package/dist/components/lists.d.ts +26 -0
  23. package/dist/components/lists.d.ts.map +1 -0
  24. package/dist/components/media.d.ts +105 -0
  25. package/dist/components/media.d.ts.map +1 -0
  26. package/dist/components/menus.d.ts +20 -0
  27. package/dist/components/menus.d.ts.map +1 -0
  28. package/dist/components/navigation.d.ts +22 -0
  29. package/dist/components/navigation.d.ts.map +1 -0
  30. package/dist/components/row.d.ts +34 -0
  31. package/dist/components/row.d.ts.map +1 -0
  32. package/dist/components/shapes.d.ts +107 -0
  33. package/dist/components/shapes.d.ts.map +1 -0
  34. package/dist/components/split.d.ts +25 -0
  35. package/dist/components/split.d.ts.map +1 -0
  36. package/dist/components/stacks.d.ts +138 -0
  37. package/dist/components/stacks.d.ts.map +1 -0
  38. package/dist/components/system.d.ts +25 -0
  39. package/dist/components/system.d.ts.map +1 -0
  40. package/dist/components/table.d.ts +36 -0
  41. package/dist/components/table.d.ts.map +1 -0
  42. package/dist/components/tabs.d.ts +20 -0
  43. package/dist/components/tabs.d.ts.map +1 -0
  44. package/dist/components/text.d.ts +63 -0
  45. package/dist/components/text.d.ts.map +1 -0
  46. package/dist/coordinateSpace.d.ts +20 -0
  47. package/dist/coordinateSpace.d.ts.map +1 -0
  48. package/dist/dragDrop.d.ts +16 -0
  49. package/dist/dragDrop.d.ts.map +1 -0
  50. package/dist/environment.d.ts +54 -0
  51. package/dist/environment.d.ts.map +1 -0
  52. package/dist/host/instance.d.ts +67 -0
  53. package/dist/host/instance.d.ts.map +1 -0
  54. package/dist/host/reconciler.d.ts +9 -0
  55. package/dist/host/reconciler.d.ts.map +1 -0
  56. package/dist/host/root.d.ts +29 -0
  57. package/dist/host/root.d.ts.map +1 -0
  58. package/dist/host/sb.d.ts +5 -0
  59. package/dist/host/sb.d.ts.map +1 -0
  60. package/dist/host/tree.d.ts +35 -0
  61. package/dist/host/tree.d.ts.map +1 -0
  62. package/dist/index.d.ts +41 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +4038 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/inspector.d.ts +11 -0
  67. package/dist/inspector.d.ts.map +1 -0
  68. package/dist/island.d.ts +25 -0
  69. package/dist/island.d.ts.map +1 -0
  70. package/dist/lazy.d.ts +19 -0
  71. package/dist/lazy.d.ts.map +1 -0
  72. package/dist/lifecycle.d.ts +11 -0
  73. package/dist/lifecycle.d.ts.map +1 -0
  74. package/dist/modifiers.d.ts +330 -0
  75. package/dist/modifiers.d.ts.map +1 -0
  76. package/dist/mount.d.ts +41 -0
  77. package/dist/mount.d.ts.map +1 -0
  78. package/dist/navigation-context.d.ts +15 -0
  79. package/dist/navigation-context.d.ts.map +1 -0
  80. package/dist/path.d.ts +48 -0
  81. package/dist/path.d.ts.map +1 -0
  82. package/dist/pointer.d.ts +65 -0
  83. package/dist/pointer.d.ts.map +1 -0
  84. package/dist/presentation.d.ts +38 -0
  85. package/dist/presentation.d.ts.map +1 -0
  86. package/dist/scrollReader.d.ts +27 -0
  87. package/dist/scrollReader.d.ts.map +1 -0
  88. package/dist/values.d.ts +118 -0
  89. package/dist/values.d.ts.map +1 -0
  90. package/package.json +47 -4
package/README.md CHANGED
@@ -1,3 +1,436 @@
1
- # Temporary Holding Version
1
+ # @swiftbrowser/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ SwiftUI's components for React. A pure React/TypeScript web app writes its
4
+ screens with `List`, `NavigationStack`, `Toggle`, `Picker` and the rest, and
5
+ gets the same iOS UI a SwiftUI app gets in SwiftBrowser: the components emit
6
+ the [render ops](../../docs/ops-protocol.md) the Swift shim emits, and the
7
+ [web renderer](../../Web/README.md) draws them with its iOS layout engine,
8
+ typography, animations, navigation bars, tab bars, sheets and alerts. No
9
+ Swift, no WebAssembly: the package is React components and a renderer host.
10
+
11
+ It works in both directions with ordinary React, and the two nest: a
12
+ SwiftUI tree can live inside a `react-dom` page, and ordinary React DOM can
13
+ live inside a SwiftUI element (see [Mixing with ordinary React](#mixing-with-ordinary-react)).
14
+
15
+ The package is experimental.
16
+
17
+ ```sh
18
+ npm install @swiftbrowser/react @swiftbrowser/web react react-dom
19
+ ```
20
+
21
+ It is built for a bundler (Vite, or any that compiles TypeScript in
22
+ dependencies): the renderer it drives comes from `@swiftbrowser/web`'s
23
+ TypeScript sources, as does the stylesheet the page imports.
24
+
25
+ ## Getting started
26
+
27
+ ### A whole page
28
+
29
+ The simplest app is a page whose root is a SwiftUI tree. The page provides
30
+ the renderer's stylesheet and a root element, and `mount` draws the tree in
31
+ it. In the device frame (the markup of `index.html` here, with the iPhone
32
+ bezel, status bar and the theme controls) the root is the `#screen` element;
33
+ on a phone the renderer can also draw full screen.
34
+
35
+ ```tsx
36
+ import '@swiftbrowser/web/src/styles.css';
37
+ import { useState } from 'react';
38
+ import { mount, Label, List, NavigationLink, NavigationStack, Section, Text, Toggle } from '@swiftbrowser/react';
39
+
40
+ function Library() {
41
+ const [downloads, setDownloads] = useState(true);
42
+ return (
43
+ <List navigationTitle="Library">
44
+ <Section header="Reading">
45
+ <NavigationLink destination={<Text>Detail</Text>}>
46
+ <Text>The Swift Programming Language</Text>
47
+ </NavigationLink>
48
+ </Section>
49
+ <Section header="Settings">
50
+ <Toggle isOn={[downloads, setDownloads]}>
51
+ <Label title="Automatic Downloads" systemImage="arrow.down.circle" />
52
+ </Toggle>
53
+ </Section>
54
+ </List>
55
+ );
56
+ }
57
+
58
+ mount(document.getElementById('screen')!, <NavigationStack><Library /></NavigationStack>);
59
+ ```
60
+
61
+ `mount` returns the root and the renderer; `setEnvironment({ colorScheme,
62
+ dynamicTypeSize })` on it switches dark mode and the Dynamic Type size, as
63
+ the demo page's controls do.
64
+
65
+ ### Inside an existing React app
66
+
67
+ In a `react-dom` app, `SwiftUIView` is a normal component that draws its
68
+ children with the renderer in a box of the page's own layout, with the
69
+ device's design tokens and none of its frame:
70
+
71
+ ```tsx
72
+ import '@swiftbrowser/web/src/styles.css';
73
+ import { SwiftUIView, List, Section, Toggle } from '@swiftbrowser/react';
74
+
75
+ function Settings({ dark, setDark }) {
76
+ return (
77
+ <SwiftUIView fit="content" colorScheme={dark ? 'dark' : 'light'} className="settings">
78
+ <List>
79
+ <Section header="Appearance">
80
+ <Toggle title="Dark Mode" isOn={[dark, setDark]} />
81
+ </Section>
82
+ </List>
83
+ </SwiftUIView>
84
+ );
85
+ }
86
+ ```
87
+
88
+ `fit="content"` makes the island as tall as its content, like a block in
89
+ the page's flow; `fit="fill"` (the default) draws in the box the page sizes
90
+ with CSS, which is what navigation stacks and tab views want, since they fill
91
+ their screen. `colorScheme` and `dynamicTypeSize` are props. The island is
92
+ a React root of its own: the page's React state reaches it through props as
93
+ usual, and its contexts through `contexts={[…]}` (see
94
+ [Contexts across roots](#contexts-across-roots)).
95
+
96
+ ### Try the demos
97
+
98
+ ```sh
99
+ npm install
100
+ cd packages/react
101
+ npm run dev # http://localhost:5174/ — Examples/Fidelity, in React
102
+ # http://localhost:5174/island.html — islands in a react-dom page
103
+ # http://localhost:5174/wide.html — a regular-width app: split view, table, inspector
104
+ npm run test:unit # the React Fidelity tabs emit the same tree as the Swift app
105
+ ```
106
+
107
+ `?screen=<name>` on the first page opens one of the Fidelity screens alone
108
+ (`list`, `detail`, `form`, `controls`, `typography`, `shapes`, `layout`,
109
+ `tabs`, `sheet`, `alert`, `search`, `post`, `settings`, `reorder`), like the
110
+ Swift app's `-screen` argument; `extras`, `more` and `web` show the
111
+ components beyond it.
112
+
113
+ ## Writing views
114
+
115
+ ### Components
116
+
117
+ The components are named as in SwiftUI and take the same arguments as
118
+ props: `<VStack alignment="leading" spacing={8}>`, `<Text font="headline">`,
119
+ `<Image systemName="heart.fill" />`, `<Button title="Save" action={save} />`,
120
+ `<Picker label="Plan" selection={[plan, setPlan]} options={plans} />`,
121
+ `<Section header="Reading" footer="…">`. Children are the content closure.
122
+ `ForEach` takes `data` and a render function, with `id`, `onDelete` and
123
+ `onMove` as in SwiftUI; `Group` applies its modifiers to each child.
124
+
125
+ Text is plain strings and numbers inside `<Text>`; nested `<Text>`s and
126
+ inline `<Image>`s make a rich text (`Text + Text`), and `italic`,
127
+ `underline`, `strikethrough` and `tracking` are its own props.
128
+
129
+ ### Modifiers as props
130
+
131
+ Every component takes SwiftUI's modifiers as props, and applies them **in
132
+ the order they are written, innermost first**, so a chain reads the same:
133
+
134
+ ```tsx
135
+ <Image
136
+ systemName="swift"
137
+ font={system(17, 'semibold')}
138
+ foregroundStyle="white"
139
+ frame={{ width: 32, height: 32 }}
140
+ background={{ fill: 'orange', in: <RoundedRectangle cornerRadius={8} /> }}
141
+ />
142
+ ```
143
+
144
+ is `Image(systemName: "swift").font(.system(size: 17, weight: .semibold))
145
+ .foregroundStyle(.white).frame(width: 32, height: 32).background(.orange, in:
146
+ RoundedRectangle(cornerRadius: 8))`. Each modifier wraps the view in the same
147
+ `styled` element the Swift shim emits, so the renderer lays it out
148
+ identically. A modifier that must apply twice goes in `modifiers={[…]}`, an
149
+ ordered list. The environment modifiers (`buttonStyle`, `controlSize`,
150
+ `pickerStyle`, `labelsHidden`) are React contexts the views below read, and
151
+ `navigationTitle`, `toolbar`, `searchable`, `sheet`, `alert`,
152
+ `presentationDetents` and `preferredColorScheme` attach to the enclosing
153
+ screen, sheet or page, as the SwiftUI preferences do. Effects (`blur`,
154
+ `grayscale`, `saturation`, `brightness`, `contrast`, `hueRotation`,
155
+ `colorInvert`, `colorMultiply`, `blendMode`, `mask`, `rotation3DEffect`),
156
+ layout hints (`aspectRatio`, `scaledToFit`, `position`, `zIndex`,
157
+ `alignmentGuide`, `ignoresSafeArea`, `allowsHitTesting`, `lineLimit` ranges)
158
+ and the lifecycle (`onAppear`, `task` with an `AbortSignal` for
159
+ cancellation, `onChangeOf`) are modifiers too, as are `draggable` and
160
+ `dropDestination`.
161
+
162
+ Values are plain data: colors by name (`"red"`, `"secondary"`,
163
+ `"secondarySystemBackground"`) or through `Color` (`Color.opacity('black',
164
+ 0.2)`, `Color.gradient('teal')`, `Color.hex('#336699')`); fonts by text
165
+ style (`"title2"`) or `{ textStyle: 'title2', weight: 'bold' }` and
166
+ `system(size, weight, design)`; frames as `{ width, height, maxWidth:
167
+ Infinity, alignment }`; padding as `true`, a number, `['vertical', 6]` or
168
+ insets; gradients through `LinearGradient`, `RadialGradient` and
169
+ `AngularGradient`.
170
+
171
+ ### Bindings
172
+
173
+ A binding is a `useState` pair, so `isOn={[downloads, setDownloads]}` reads
174
+ like `isOn: $downloads`. Everything that takes one (`Toggle`, `Picker`,
175
+ `Slider`, `Stepper`, `TextField`, `TextEditor`, `DatePicker`, `TabView`'s
176
+ `selection`, `List`'s `selection` and `editing`, `NavigationStack`'s `path`,
177
+ the `sheet` and `alert` modifiers' `isPresented`) also takes a plain value
178
+ with an `onChange` handler. The setter can be anything with that signature.
179
+
180
+ When the renderer sends an event (a tap, a switch, a selection), the
181
+ handler's state updates render synchronously and the renderer gets the next
182
+ pass inside the event, the way the Swift core answers one.
183
+
184
+ ### Navigation and presentation
185
+
186
+ `NavigationStack` holds the pushed values (or takes a `path` binding).
187
+ `NavigationLink` pushes a `value`, which the enclosing screen's
188
+ `navigationDestination` builds a screen for, or a `destination` view
189
+ directly. `TabView` holds `Tab`s. `.sheet` and `.alert` are modifiers whose
190
+ content and actions render as root-level elements while presented;
191
+ `presentationDetents` inside a sheet sets its detents. `useDismiss()` inside
192
+ a pushed screen or a sheet pops or dismisses it.
193
+
194
+ ### Lists
195
+
196
+ `List` and `Form` hold `Section`s and rows. `selection={[…]}` makes rows with
197
+ a `tag` (or a `NavigationLink` value) selectable, one value or a `Set`;
198
+ `editing` is the edit mode, which `EditButton` flips. `ForEach`'s `onDelete`
199
+ makes its rows deletable (a swipe, or edit mode's minus) and `onMove` makes
200
+ them reorder with a grip, with `moveItems` to apply the move to an array.
201
+ `swipeActions` and `contextMenu` are modifiers on a row.
202
+
203
+ ### Size classes
204
+
205
+ `useHorizontalSizeClass()` is `compact` below 700 points of width (a phone)
206
+ and `regular` from it (a tablet, a desktop window); `mount` and
207
+ `SwiftUIView` follow the root's width as it changes. The views that adapt do
208
+ it as on iOS: `NavigationSplitView` is two columns when regular (the
209
+ sidebar's toggle button hides and shows the sidebar) and one stack when
210
+ compact, which pushes the detail while the sidebar's `List(selection:)` has
211
+ a selection and clears it on back; `.inspector` is a trailing panel when
212
+ regular and a sheet when compact; `Table` shows every column under sortable
213
+ titles when regular and the first column when compact.
214
+
215
+ ```tsx
216
+ <NavigationSplitView
217
+ sidebar={<List selection={[team, setTeam]}>{/* rows with a tag */}</List>}
218
+ detail={<Table data={people} columns={columns} sortOrder={[order, setOrder]} />}
219
+ />
220
+ ```
221
+
222
+ ### Scrolling
223
+
224
+ `List`, `LazyVStack` and `LazyHStack` build a long `ForEach` a batch of
225
+ rows at a time as they scroll into view. `ScrollViewReader` hands a proxy
226
+ whose `scrollTo(id, anchor)` scrolls to the view marked `scrollID={id}`,
227
+ building lazy rows up to it first. `refreshable` (on a `List` or
228
+ `ScrollView`, or above one) runs an async action on a pull, with the
229
+ spinner while it runs. `scrollTargetBehavior="paging"` or `"viewAligned"` on
230
+ a `ScrollView` snaps, with `scrollTargetLayout` on the stack inside.
231
+ Name a coordinate space with `coordinateSpace="page"` on the `ScrollView`
232
+ (or any view), and a `GeometryReader` inside reads its frame there with
233
+ `proxy.frame({ named: 'page' })`, updated as the page scrolls.
234
+
235
+ ### Drawing and media
236
+
237
+ `Shape` takes a `path` function of the rectangle it is laid out in and
238
+ draws a `Path` (`move`, `addLine`, `addCurve`, `addArc`, `addRoundedRect`…);
239
+ `UnevenRoundedRectangle` and `ContainerRelativeShape` are built on it.
240
+ `Canvas` hands its renderer a `GraphicsContext` (fill, stroke, text, images,
241
+ transforms, opacity, clips, filters, gradients in its own coordinates), and
242
+ `TimelineView` re-renders on a schedule, so a clock is a `TimelineView`
243
+ around a `Canvas`. Views passed in the `symbols` prop (`{ star: <Image
244
+ systemName="star.fill" /> }`) come back from `resolveSymbol` and draw with
245
+ `drawSymbol` at a point, under the context's transform and opacity; they are
246
+ real views laid over the canvas, so they draw above its other commands. `useAVPlayer(url)` is an `AVPlayer` (play, pause, seek,
247
+ mute, volume, rate) that `VideoPlayer` shows in the page's `<video>`; the
248
+ position is read or observed without re-rendering.
249
+
250
+ ### Mouse, keyboard and effects
251
+
252
+ A web app may have a mouse and a keyboard, so the modifiers SwiftUI keeps
253
+ for iPad and Mac work here: `onHover` (a mouse entering and leaving the
254
+ view's frame), `help` (a tooltip), `pointerStyle` (`link`, `text`,
255
+ `grabIdle`… or any CSS cursor), `keyboardShortcut` on a `Button` (`⌘` is
256
+ Ctrl off Apple platforms) and `onKeyPress` (keys pressed while no text field
257
+ has the focus). `symbolEffect` draws SF Symbols' effects (`bounce`, `pulse`,
258
+ `variableColor`, `rotate`, `wiggle`, `breathe`, `scale`, `appear`), running
259
+ while active or once per change of a value. `matchedGeometryEffect` with a
260
+ namespace from `useNamespace()` moves a view into the frame of the one it
261
+ replaces in an animated change. `contentTransition="numericText"` rolls a
262
+ text's old value out and the new one in when it changes in an animated pass,
263
+ upward or downward as the number grows or shrinks (`{ numericText: {
264
+ countsDown } }` fixes the direction); the whole text rolls, not each digit.
265
+ `opacity` and `interpolate` crossfade. `sensoryFeedback` vibrates where the
266
+ device can.
267
+
268
+ ### Animation
269
+
270
+ `withAnimation(animation, () => …)` animates the pass a state change makes,
271
+ and the `animation` modifier a view's changes when a value changes.
272
+ `PhaseAnimator` moves its content through phases, each change animated;
273
+ `KeyframeAnimator` plays tracks of keyframes (`linear`, `cubic`, `spring`,
274
+ `move`) and renders its content with the values each frame.
275
+
276
+ ### Charts
277
+
278
+ `Chart` takes `BarMark`, `LineMark`, `AreaMark`, `PointMark` and `RuleMark`
279
+ with their values in `x` and `y`: categories, numbers or dates. The
280
+ component builds the scales and ticks the Swift Charts module builds
281
+ (bands, nice numeric domains that include zero, calendar strides), groups
282
+ lines and areas by series, colors `foregroundStyleBy` series from the chart
283
+ palette with a legend, and places `annotation`s.
284
+
285
+ ## Mixing with ordinary React
286
+
287
+ Both directions work, and they nest: a `react-dom` page can hold a SwiftUI
288
+ island, and the island can hold ordinary React DOM inside one of its rows.
289
+ The island page of the demo (`island.html`) does exactly that.
290
+
291
+ ### A SwiftUI tree inside a react-dom app
292
+
293
+ `SwiftUIView`, shown in [Getting started](#inside-an-existing-react-app):
294
+ a component whose children are a SwiftUI tree, drawn in a box of the page.
295
+
296
+ ### React DOM inside a SwiftUI element
297
+
298
+ `Host` puts ordinary React DOM, with its own components, stylesheets and
299
+ event handlers, inside a SwiftUI tree. Its children render with `react-dom`
300
+ in a `host` element the layout engine sizes, so the DOM sits inside list
301
+ rows, stacks and screens like any other view:
302
+
303
+ ```tsx
304
+ function Composer() {
305
+ const [notes, setNotes] = useState('');
306
+ return (
307
+ <List>
308
+ <Section header="Appearance">
309
+ <LabeledContent label="Notes" value={`${notes.length} characters`} />
310
+ </Section>
311
+ <Section header="Composer" footer="A textarea and a counter, rendered by react-dom inside the list row.">
312
+ <Host className="composer">
313
+ <textarea value={notes} placeholder="Write something…" onChange={(e) => setNotes(e.target.value)} />
314
+ <div className="count">{280 - notes.length}</div>
315
+ </Host>
316
+ </Section>
317
+ <Section header="Map">
318
+ <Host sizing="fill" frame={{ height: 160 }}>
319
+ <MapView />
320
+ </Host>
321
+ </Section>
322
+ </List>
323
+ );
324
+ }
325
+ ```
326
+
327
+ Typing in the textarea updates `notes` in the React component above, the
328
+ SwiftUI row shows the count, and the row grows with the text.
329
+
330
+ - **Sizing.** `sizing="content"` (the default) measures the DOM like text:
331
+ as wide as the content wants, up to the width the parent proposes, and as
332
+ tall as it is at that width. Whenever the content changes size (more
333
+ lines, an image that loaded, a state change), the tree is laid out again
334
+ around it. `sizing="fill"` takes the proposal, so a `frame` (or the
335
+ parent's size) decides the box and the content fills it; `height: 100%`
336
+ in the content reaches the box.
337
+ - **Styling.** The content inherits the device's font and text color, and
338
+ the iOS tokens are CSS custom properties it can use:
339
+ `var(--sb-color-secondary)`, `var(--sb-color-accent)`,
340
+ `var(--sb-color-separator)`, the Dynamic Type sizes, and so on; they
341
+ follow dark mode. `className` and `style` land on the wrapper the
342
+ children render in.
343
+ - **Events.** The DOM handles its own events. Pointer downs inside a host
344
+ never start the renderer's gestures (no swipe, no drag), so text
345
+ selection, scrolling inside the host and native controls work; the row
346
+ around it still swipes when the pan starts outside the host.
347
+ - **Hooks.** The content can use this package's hooks: `useDismiss()` from a
348
+ sheet or a pushed screen, `useNavigation()` to push, `useColorScheme()`.
349
+
350
+ A host is a React root of its own, so it needs the `react-dom` peer
351
+ dependency, and React context does not cross into it by itself; see below.
352
+
353
+ ### Contexts across roots
354
+
355
+ `SwiftUIView` and `Host` each start a React root, and React context stops at
356
+ a root boundary. This package's own contexts (the environment, navigation,
357
+ dismiss, the view environment) are bridged automatically. For the page's
358
+ own contexts, name them on the component and they are read on the outer
359
+ side and provided again inside:
360
+
361
+ ```tsx
362
+ <SwiftUIView contexts={[ThemeContext, RouterContext]}>…</SwiftUIView>
363
+ <Host contexts={[ThemeContext]}>…</Host>
364
+ ```
365
+
366
+ The list must be the same on every render of that component (each entry is
367
+ read with a hook). Props, callbacks and external stores need no bridging.
368
+
369
+ ## How it works
370
+
371
+ - `src/host/`: a [react-reconciler](https://www.npmjs.com/package/react-reconciler)
372
+ host whose instances are protocol elements. After each React commit,
373
+ `HostTree.flush` resolves two virtual kinds (a *preference* merges props
374
+ into the enclosing `navscreen` or `sheet`; a *hoist* moves children to the
375
+ enclosing screen or to the root, as `.toolbar`, `.sheet` and `.alert` do),
376
+ diffs the result against the last pass and emits one render pass: creates,
377
+ updates, inserts (parents before children), removes, `commit`. Events from
378
+ the renderer reach the element's handlers and flush synchronously.
379
+ - `createSwiftUIRoot(sink)` renders a tree into any ops sink; `mount(screen,
380
+ element)` pairs a root with a `Renderer` from `@swiftbrowser/web/src/renderer`.
381
+ `withAnimation(() => setCount(1))` stamps the pass's `commit` with the
382
+ animation, as `withAnimation { }` does.
383
+ - `src/modifiers.tsx` applies the modifier props; `src/components/` holds the
384
+ components; `src/components/host.tsx` and `src/island.tsx` the two mixing
385
+ components, over the renderer's `host` element kind and its island layout
386
+ mode ([docs/ops-protocol.md](../../docs/ops-protocol.md#host-elements-the-pages-own-dom)).
387
+ - `test/fidelity.test.tsx` renders `Examples/Fidelity`'s tab view through the
388
+ host and compares the element tree with `Examples/Fidelity/__snapshots__/fidelity.jsonl`,
389
+ the ops the Swift app emits for the same screens; `test/extras.test.tsx`
390
+ covers the components beyond the Swift app.
391
+
392
+ ## What is covered
393
+
394
+ - **Layout**: stacks, `LazyVStack`, `LazyHStack`, `Spacer`, `Divider`,
395
+ `ScrollView`, `ScrollViewReader`, `Group`, `ForEach` (with `onDelete` and
396
+ `onMove`), `Grid`, `LazyVGrid`, `ViewThatFits`, `GeometryReader` (its size,
397
+ its frame on screen or in a `coordinateSpace` named on an ancestor, and its
398
+ safe-area insets), custom `Layout` (with `layoutValue`s, priorities, view
399
+ spacing, explicit alignment guides and a cache), `safeAreaInset`.
400
+ - **Content**: `Text` (with `Text + Text` runs, inline images, italic,
401
+ underline, strikethrough, tracking), `Image` (symbols, catalog images
402
+ with the `assets` option, remote images and `AsyncImage`), `Label` and its
403
+ styles, `LabeledContent`, `Link`, `Color` as a view, shapes (built-in,
404
+ custom paths, `UnevenRoundedRectangle`, `ContainerRelativeShape`) with
405
+ fills, strokes and gradients, `Canvas`, `TimelineView`, `VideoPlayer`,
406
+ `Gauge`, `ProgressView`, Swift Charts.
407
+ - **Controls**: `Button` (styles, roles, sizes), `Toggle`, `Picker`,
408
+ `Slider`, `Stepper`, `TextField` and `SecureField` (focus bindings, submit
409
+ labels), `TextEditor`, `DatePicker`, `ColorPicker`, `ShareLink`, `Menu`,
410
+ `EditButton`.
411
+ - **Containers**: `List` (selection, edit mode, swipe actions, deletion,
412
+ reordering, pull to refresh), `Form`, `Section`, `Table`, `GroupBox`,
413
+ `DisclosureGroup`, `ControlGroup`, `ContentUnavailableView`.
414
+ - **Navigation and presentation**: `NavigationStack` (value and destination
415
+ links, a `path` binding, nested stacks flattened), `NavigationSplitView`,
416
+ `TabView`/`Tab`, toolbars, search fields, sheets with detents, popovers
417
+ (as sheets), alerts and dialogs, inspectors.
418
+ - **Interaction**: `onTapGesture`, `dragGesture`, `onLongPressGesture`,
419
+ `contextMenu`, `draggable` and `dropDestination` (in the app, and text or
420
+ files from outside the page), `onHover`, `help`, `pointerStyle`,
421
+ `keyboardShortcut`, `onKeyPress`, `sensoryFeedback`.
422
+ - **Animation and effects**: `withAnimation`, `animation`, transitions,
423
+ `PhaseAnimator`, `KeyframeAnimator`, `matchedGeometryEffect`,
424
+ `symbolEffect`, `contentTransition`, `redacted` placeholders, `textCase`.
425
+ - **Mixing**: `Host` and `SwiftUIView`.
426
+
427
+ ## Not yet
428
+
429
+ - `contentTransition="numericText"` rolls the whole text rather than only
430
+ the digits that changed, and `Canvas` symbols always draw above the
431
+ canvas's other commands.
432
+ - The Swift-only bridges (SwiftData, URLSession, notifications, Core ML,
433
+ StoreKit) have no React counterpart, by design: a web app uses the
434
+ platform's own.
435
+ - The ops protocol is the contract with the renderer and is versioned with it:
436
+ this package imports the renderer's sources from `@swiftbrowser/web/src/*`.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A `Binding<T>`: a `useState` pair, so `isOn={[downloads, setDownloads]}`
3
+ * reads like `isOn: $downloads`. A plain value with an `onChange` handler is
4
+ * accepted everywhere a binding is.
5
+ */
6
+ export type Binding<T> = readonly [T, (value: T) => void];
7
+ export type BindingValue<T> = Binding<T> | T;
8
+ export declare function readBinding<T>(value: BindingValue<T>, onChange?: (value: T) => void): [T, (next: T) => void];
9
+ //# sourceMappingURL=binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC;AAE1D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAE7C,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAM5G"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * React context does not cross a root boundary, and a `Host` (a react-dom
3
+ * root inside the SwiftUI tree) or a `SwiftUIView` (a SwiftUI root inside a
4
+ * react-dom tree) is one. The bridge reads the contexts on the outer side and
5
+ * provides them again on the inner side. This package's own contexts always
6
+ * travel; a component names any others it needs.
7
+ */
8
+ import { type Context, type ReactNode } from 'react';
9
+ /**
10
+ * Returns a function that wraps `children` in providers of the current
11
+ * values of this package's contexts and of `contexts`. The list of contexts
12
+ * must not change between renders (each is read with a hook).
13
+ */
14
+ export declare function useContextBridge(contexts?: readonly Context<unknown>[], options?: {
15
+ environment?: boolean;
16
+ }): (children: ReactNode) => ReactNode;
17
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA6B,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAWhF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,GAAE,SAAS,OAAO,CAAC,OAAO,CAAC,EAAO,EAC1C,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,CAAC,QAAQ,EAAE,SAAS,KAAK,SAAS,CASpC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `PhaseAnimator` and `KeyframeAnimator`. A phase animator moves its content
3
+ * through phases, each change in one animated pass that the renderer tweens
4
+ * (as the shim does); a keyframe animator computes its values frame by frame
5
+ * along tracks of keyframes and renders them, as SwiftUI evaluates its
6
+ * content per frame.
7
+ */
8
+ import type { Animation } from '@swiftbrowser/web/src/protocol';
9
+ import { type ReactNode } from 'react';
10
+ export interface PhaseAnimatorProps<Phase> {
11
+ phases: readonly Phase[];
12
+ /** Each change runs through the phases once, back to the first; without one the phases cycle while shown. */
13
+ trigger?: unknown;
14
+ /** The animation into a phase (`default` by default). */
15
+ animation?: (phase: Phase) => Animation;
16
+ children: (phase: Phase) => ReactNode;
17
+ }
18
+ export declare function PhaseAnimator<Phase>({ phases, trigger, animation, children }: PhaseAnimatorProps<Phase>): ReactNode;
19
+ export type KeyframeCurve = 'linear' | 'cubic' | 'spring' | 'move';
20
+ /** One keyframe of a track: the value it reaches, in how long (seconds), along which curve. */
21
+ export interface Keyframe {
22
+ to: number;
23
+ duration: number;
24
+ curve?: KeyframeCurve;
25
+ }
26
+ export type KeyframeTracks<Value extends Record<string, number>> = {
27
+ [K in keyof Value]?: readonly Keyframe[];
28
+ };
29
+ export interface KeyframeAnimatorProps<Value extends Record<string, number>> {
30
+ initialValue: Value;
31
+ keyframes: KeyframeTracks<Value>;
32
+ /** Each change plays the keyframes once; without one they repeat while shown (`repeating`). */
33
+ trigger?: unknown;
34
+ repeating?: boolean;
35
+ children: (value: Value) => ReactNode;
36
+ }
37
+ /** The tracks' values `elapsed` seconds in (each track from the initial value through its keyframes). */
38
+ export declare function keyframeValues<Value extends Record<string, number>>(initial: Value, tracks: KeyframeTracks<Value>, elapsed: number): Value;
39
+ export declare function KeyframeAnimator<Value extends Record<string, number>>({ initialValue, keyframes, trigger, repeating, children, }: KeyframeAnimatorProps<Value>): ReactNode;
40
+ //# sourceMappingURL=animators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animators.d.ts","sourceRoot":"","sources":["../../src/components/animators.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAOpE,MAAM,WAAW,kBAAkB,CAAC,KAAK;IACvC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IACzB,6GAA6G;IAC7G,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC;IACxC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC;CACvC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,kBAAkB,CAAC,KAAK,CAAC,GAAG,SAAS,CAuCnH;AAMD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnE,+FAA+F;AAC/F,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,EAAE,SAAS,QAAQ,EAAE;CAAE,CAAC;AAEhH,MAAM,WAAW,qBAAqB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IACzE,YAAY,EAAE,KAAK,CAAC;IACpB,SAAS,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACjC,+FAA+F;IAC/F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC;CACvC;AAkBD,yGAAyG;AACzG,wBAAgB,cAAc,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAsB1I;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACrE,YAAY,EACZ,SAAS,EACT,OAAO,EACP,SAAS,EACT,QAAQ,GACT,EAAE,qBAAqB,CAAC,KAAK,CAAC,GAAG,SAAS,CA+B1C"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * `Canvas`: a `GeometryReader` whose content, at the reported size, is a
3
+ * `canvas` element. The renderer function draws into a `GraphicsContext`
4
+ * that records commands, as Sources/SwiftUI/API/Canvas.swift does; the page
5
+ * draws them as SVG.
6
+ */
7
+ import type { CanvasCommand, CanvasFilter } from '@swiftbrowser/web/src/protocol';
8
+ import { type ReactNode } from 'react';
9
+ import { type ViewModifiers } from '../modifiers';
10
+ import type { Path, Point } from '../path';
11
+ import { type ColorValue, type FontValue, type StyleValue, type UnitPointValue } from '../values';
12
+ type Matrix = [number, number, number, number, number, number];
13
+ /** A shading in the drawing's coordinates: a style, or a gradient between two points. */
14
+ export type Shading = StyleValue | {
15
+ linearGradient: {
16
+ colors?: ColorValue[];
17
+ stops?: {
18
+ color: ColorValue;
19
+ location: number;
20
+ }[];
21
+ start: Point;
22
+ end: Point;
23
+ };
24
+ } | {
25
+ radialGradient: {
26
+ colors?: ColorValue[];
27
+ stops?: {
28
+ color: ColorValue;
29
+ location: number;
30
+ }[];
31
+ center: Point;
32
+ startRadius: number;
33
+ endRadius: number;
34
+ };
35
+ };
36
+ export interface StrokeOptions {
37
+ lineWidth?: number;
38
+ lineCap?: 'butt' | 'round' | 'square';
39
+ lineJoin?: 'miter' | 'round' | 'bevel';
40
+ miterLimit?: number;
41
+ dash?: number[];
42
+ dashPhase?: number;
43
+ }
44
+ /** What a canvas image is: a symbol, a catalog image, or a remote image of a known size. */
45
+ export type CanvasImage = {
46
+ systemName: string;
47
+ } | {
48
+ name: string;
49
+ } | {
50
+ url: string;
51
+ width: number;
52
+ height: number;
53
+ };
54
+ /** A view passed in `Canvas`'s `symbols`, resolved by its id. */
55
+ export interface ResolvedSymbol {
56
+ symbol: string;
57
+ }
58
+ /** Where a symbol was drawn: the point (the context's transform applied) and the look at that moment. */
59
+ export interface SymbolDraw {
60
+ id: string;
61
+ x: number;
62
+ y: number;
63
+ rotation: number;
64
+ scale: {
65
+ x: number;
66
+ y: number;
67
+ };
68
+ opacity: number;
69
+ }
70
+ /** `GraphicsContext`: what `Canvas`'s renderer draws with. Methods return the context for chaining. */
71
+ export declare class GraphicsContext {
72
+ readonly size: {
73
+ width: number;
74
+ height: number;
75
+ };
76
+ readonly commands: CanvasCommand[];
77
+ readonly symbolDraws: SymbolDraw[];
78
+ private symbolIds;
79
+ /** @internal The ids `symbols` gave the canvas. */
80
+ setSymbols(ids: Iterable<string>): void;
81
+ /** `resolveSymbol(id:)`: the view the canvas was given under `id`, or null. */
82
+ resolveSymbol(id: string): ResolvedSymbol | null;
83
+ /**
84
+ * `draw(symbol, at:)`: the symbol's view centered on the point. Symbols are
85
+ * views laid over the drawing, so they draw above its other commands.
86
+ */
87
+ drawSymbol(symbol: ResolvedSymbol, at: Point): this;
88
+ private state;
89
+ constructor(size: {
90
+ width: number;
91
+ height: number;
92
+ });
93
+ get opacity(): number;
94
+ set opacity(value: number);
95
+ get blendMode(): string | undefined;
96
+ set blendMode(value: string | undefined);
97
+ translateBy(x: number, y: number): this;
98
+ scaleBy(x: number, y?: number): this;
99
+ /** Degrees, clockwise on screen. */
100
+ rotate(degrees: number): this;
101
+ concatenate(matrix: Matrix): this;
102
+ clip(to: Path): this;
103
+ addFilter(filter: CanvasFilter): this;
104
+ /** `drawLayer { }`: draws with a copy of the state, restored after. */
105
+ drawLayer(body: (context: GraphicsContext) => void): this;
106
+ fill(path: Path, with_: Shading, options?: {
107
+ eoFill?: boolean;
108
+ }): this;
109
+ stroke(path: Path, with_: Shading, options?: StrokeOptions | number): this;
110
+ /** `draw(Text(...), at:anchor:)` */
111
+ drawText(text: string, at: Point, options?: {
112
+ anchor?: UnitPointValue;
113
+ font?: FontValue;
114
+ color?: StyleValue;
115
+ }): this;
116
+ /** `draw(Image(...), at:anchor:)` or `draw(_:in:)` with `size`. */
117
+ drawImage(image: CanvasImage, at: Point, options?: {
118
+ anchor?: UnitPointValue;
119
+ size?: {
120
+ width: number;
121
+ height: number;
122
+ };
123
+ color?: StyleValue;
124
+ }): this;
125
+ private push;
126
+ }
127
+ export interface CanvasProps extends ViewModifiers {
128
+ /** `Canvas { context, size in }` */
129
+ renderer: (context: GraphicsContext, size: {
130
+ width: number;
131
+ height: number;
132
+ }) => void;
133
+ /** `Canvas(renderer:symbols:)`: views the renderer draws by id (`resolveSymbol`, `drawSymbol`). */
134
+ symbols?: Record<string, ReactNode>;
135
+ }
136
+ export declare function Canvas(props: CanvasProps): ReactNode;
137
+ export {};
138
+ //# sourceMappingURL=canvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/components/canvas.tsx"],"names":[],"mappings":"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"}