@swiftbrowser/web 0.3.0 → 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.
- package/README.md +3 -1
- package/package.json +4 -2
- package/src/animation.ts +154 -3
- package/src/dragDrop.ts +549 -0
- package/src/hostContent.ts +37 -0
- package/src/interaction.ts +128 -22
- package/src/largeTitle.ts +95 -54
- package/src/layout/engine.ts +492 -91
- package/src/layout/index.ts +5 -2
- package/src/layout/measure.ts +2 -0
- package/src/layout/text.ts +6 -3
- package/src/layout/types.ts +16 -1
- package/src/layout/typography.ts +30 -10
- package/src/layoutDom.ts +66 -16
- package/src/pointerEffects.ts +182 -0
- package/src/protocol.ts +188 -9
- package/src/renderer.ts +316 -15
- package/src/scrollEdge.ts +87 -0
- package/src/scrollTo.ts +6 -0
- package/src/styles.css +430 -16
- package/src/typography.ts +5 -2
package/src/protocol.ts
CHANGED
|
@@ -719,7 +719,15 @@ export interface PickerProps {
|
|
|
719
719
|
* `GeometryReader`: fills its proposal and reports its size through a
|
|
720
720
|
* `geometry` event whenever it changes. Exactly one child.
|
|
721
721
|
*/
|
|
722
|
-
export
|
|
722
|
+
export interface GeometryProps {
|
|
723
|
+
/**
|
|
724
|
+
* Report the reader's frame on screen too (`x`, `y` and `safeArea` in its
|
|
725
|
+
* `geometry` events, kept current as its ancestors scroll), for
|
|
726
|
+
* `GeometryProxy.frame(in: .global)` and `safeAreaInsets`. Absent: the size
|
|
727
|
+
* only, as before.
|
|
728
|
+
*/
|
|
729
|
+
reportsFrame?: boolean;
|
|
730
|
+
}
|
|
723
731
|
|
|
724
732
|
// Phase 6 kinds (docs/phase6/layout-containers.md).
|
|
725
733
|
|
|
@@ -870,6 +878,19 @@ export interface TapGestureProps {
|
|
|
870
878
|
location?: boolean;
|
|
871
879
|
}
|
|
872
880
|
|
|
881
|
+
/**
|
|
882
|
+
* A box the page fills with its own DOM (src/hostContent.ts): `content` is
|
|
883
|
+
* the key the page registered its element under. `sizing: "fill"` takes the
|
|
884
|
+
* proposal, the content's own size on an unconstrained axis; `"content"`
|
|
885
|
+
* takes the content's size, its width capped by the proposal and its height
|
|
886
|
+
* measured at that width, like text. Only page-side producers (such as
|
|
887
|
+
* @swiftbrowser/react) emit it; the Swift core never does.
|
|
888
|
+
*/
|
|
889
|
+
export interface HostProps {
|
|
890
|
+
content: string;
|
|
891
|
+
sizing: 'fill' | 'content';
|
|
892
|
+
}
|
|
893
|
+
|
|
873
894
|
export interface ElementPropsByKind {
|
|
874
895
|
text: TextProps;
|
|
875
896
|
vstack: VStackProps;
|
|
@@ -905,6 +926,7 @@ export interface ElementPropsByKind {
|
|
|
905
926
|
toolbar: ToolbarProps;
|
|
906
927
|
searchfield: SearchFieldProps;
|
|
907
928
|
tapgesture: TapGestureProps;
|
|
929
|
+
host: HostProps;
|
|
908
930
|
}
|
|
909
931
|
|
|
910
932
|
export type ElementKind = keyof ElementPropsByKind;
|
|
@@ -1289,6 +1311,11 @@ export interface GeometryEvent {
|
|
|
1289
1311
|
id: ElementId;
|
|
1290
1312
|
width: number;
|
|
1291
1313
|
height: number;
|
|
1314
|
+
/** With `reportsFrame`: the box's origin in the root's coordinates, scrolling included (whole px). */
|
|
1315
|
+
x?: number;
|
|
1316
|
+
y?: number;
|
|
1317
|
+
/** With `reportsFrame`: how far the device's safe area reaches into the box on each edge. */
|
|
1318
|
+
safeArea?: { top: number; leading: number; bottom: number; trailing: number };
|
|
1292
1319
|
}
|
|
1293
1320
|
|
|
1294
1321
|
/** Host measurements of one subview of a `layout` element (points, 2 decimals; `"infinity"` for an unbounded axis). */
|
|
@@ -1333,7 +1360,8 @@ export type RendererEvent =
|
|
|
1333
1360
|
// A `video` element's playback (src/video.ts)
|
|
1334
1361
|
| VideoEvent
|
|
1335
1362
|
| LazyEvent
|
|
1336
|
-
| MenuEvent
|
|
1363
|
+
| MenuEvent
|
|
1364
|
+
| HoverEvent;
|
|
1337
1365
|
|
|
1338
1366
|
/**
|
|
1339
1367
|
* Phase 13: the page's answer to the `request` op with this `id`: `value`
|
|
@@ -1451,7 +1479,10 @@ export function isOp(value: unknown): value is Op {
|
|
|
1451
1479
|
// ---------------------------------------------------------------------------
|
|
1452
1480
|
|
|
1453
1481
|
/** `NavigationSplitView` in the regular size class: children are `[sidebar, detail]`, each a `navstack`. */
|
|
1454
|
-
export
|
|
1482
|
+
export interface NavSplitProps {
|
|
1483
|
+
/** iPadOS: false hides the sidebar (`NavigationSplitViewVisibility.detailOnly`); absent or true shows it. */
|
|
1484
|
+
sidebarVisible?: boolean;
|
|
1485
|
+
}
|
|
1455
1486
|
|
|
1456
1487
|
export type ViewThatFitsAxes = 'horizontal' | 'vertical' | 'both';
|
|
1457
1488
|
/**
|
|
@@ -1466,6 +1497,17 @@ export interface ViewThatFitsProps {
|
|
|
1466
1497
|
export interface ElementPropsByKind {
|
|
1467
1498
|
navsplit: NavSplitProps;
|
|
1468
1499
|
viewthatfits: ViewThatFitsProps;
|
|
1500
|
+
inspector: InspectorProps;
|
|
1501
|
+
inspectorpanel: Record<string, never>;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* `.inspector(isPresented:)` in the regular size class: the children are the
|
|
1506
|
+
* content, then an `inspectorpanel` (the trailing column's content).
|
|
1507
|
+
*/
|
|
1508
|
+
export interface InspectorProps {
|
|
1509
|
+
/** Whether the panel is shown; false slides it off the trailing edge. */
|
|
1510
|
+
presented: boolean;
|
|
1469
1511
|
}
|
|
1470
1512
|
|
|
1471
1513
|
export interface Style {
|
|
@@ -1688,14 +1730,40 @@ export interface ContextMenuProps {
|
|
|
1688
1730
|
}
|
|
1689
1731
|
|
|
1690
1732
|
/**
|
|
1691
|
-
* A `List` row that can be selected (`selectable`, `selected`)
|
|
1692
|
-
* (`deletable`, from `ForEach.onDelete`)
|
|
1693
|
-
* `
|
|
1733
|
+
* A `List` row that can be selected (`selectable`, `selected`), deleted
|
|
1734
|
+
* (`deletable`, from `ForEach.onDelete`) or moved (`movable`, from
|
|
1735
|
+
* `ForEach.onMove`; absent means false). A tap on a selectable row sends
|
|
1736
|
+
* `tap` with the row id; swipe-to-delete or the edit-mode minus sends
|
|
1737
|
+
* `delete`. A movable row shows a grip at its trailing edge while the list
|
|
1738
|
+
* is editing, and a long press lifts it otherwise; dragging it among its
|
|
1739
|
+
* neighbours and letting go sends `move` (src/dragDrop.ts).
|
|
1694
1740
|
*/
|
|
1695
1741
|
export interface ListRowProps {
|
|
1696
1742
|
selectable?: boolean;
|
|
1697
1743
|
selected?: boolean;
|
|
1698
1744
|
deletable?: boolean;
|
|
1745
|
+
movable?: boolean;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* `.draggable` / `.onDrag`: exactly one child, the child's size. A long
|
|
1750
|
+
* press (touch) or a 10 px drag (mouse) lifts a copy of the content, which
|
|
1751
|
+
* follows the pointer; `dragstart` goes out as it lifts and `dragend` once
|
|
1752
|
+
* it is dropped or let go (src/dragDrop.ts).
|
|
1753
|
+
*/
|
|
1754
|
+
export type DraggableProps = Record<string, never>;
|
|
1755
|
+
|
|
1756
|
+
/**
|
|
1757
|
+
* `.dropDestination` / `.onDrop`: exactly one child, the child's size. A
|
|
1758
|
+
* lifted `draggable` over it sends `drop` with the phase `entered` /
|
|
1759
|
+
* `exited`, and `performed` with the source when released over it. Text,
|
|
1760
|
+
* links and files dragged in from outside the page (the browser's own drag
|
|
1761
|
+
* and drop) are offered when their MIME type matches one of `accepts`
|
|
1762
|
+
* (`text/plain`, `image/*`, or a star over a star for anything); they
|
|
1763
|
+
* arrive as `items`.
|
|
1764
|
+
*/
|
|
1765
|
+
export interface DropDestinationProps {
|
|
1766
|
+
accepts?: string[];
|
|
1699
1767
|
}
|
|
1700
1768
|
|
|
1701
1769
|
export interface ElementPropsByKind {
|
|
@@ -1703,12 +1771,14 @@ export interface ElementPropsByKind {
|
|
|
1703
1771
|
swipeactions: SwipeActionsProps;
|
|
1704
1772
|
contextmenu: ContextMenuProps;
|
|
1705
1773
|
listrow: ListRowProps;
|
|
1774
|
+
draggable: DraggableProps;
|
|
1775
|
+
dropdestination: DropDestinationProps;
|
|
1706
1776
|
}
|
|
1707
1777
|
|
|
1708
|
-
export const INTERACTION_KINDS: readonly ElementKind[] = ['gesture', 'swipeactions', 'contextmenu', 'listrow'];
|
|
1778
|
+
export const INTERACTION_KINDS: readonly ElementKind[] = ['gesture', 'swipeactions', 'contextmenu', 'listrow', 'draggable', 'dropdestination'];
|
|
1709
1779
|
|
|
1710
1780
|
export interface ListProps {
|
|
1711
|
-
/** `EditButton` / `editMode`:
|
|
1781
|
+
/** `EditButton` / `editMode`: a selectable or deletable `listrow` gains a 44 pt leading control and its content shifts right 40; a movable one a trailing grip. */
|
|
1712
1782
|
editing?: boolean;
|
|
1713
1783
|
/** `.refreshable`: a pull past 60 pt at the top sends `refresh`. */
|
|
1714
1784
|
refreshable?: boolean;
|
|
@@ -1772,9 +1842,52 @@ export interface RefreshEvent {
|
|
|
1772
1842
|
type: 'refresh';
|
|
1773
1843
|
id: ElementId;
|
|
1774
1844
|
}
|
|
1845
|
+
/**
|
|
1846
|
+
* A movable `listrow` was dragged to a new place: `to` is the index among
|
|
1847
|
+
* its container's children (the `list`'s, or its `section`'s) before which
|
|
1848
|
+
* it goes, counted before it leaves (0 ... the child count), as
|
|
1849
|
+
* `move(fromOffsets:toOffset:)` takes its destination.
|
|
1850
|
+
*/
|
|
1851
|
+
export interface MoveEvent {
|
|
1852
|
+
type: 'move';
|
|
1853
|
+
id: ElementId;
|
|
1854
|
+
to: number;
|
|
1855
|
+
}
|
|
1856
|
+
/** A `draggable` was lifted: its payload is made now and kept until `dragend`. */
|
|
1857
|
+
export interface DragStartEvent {
|
|
1858
|
+
type: 'dragstart';
|
|
1859
|
+
id: ElementId;
|
|
1860
|
+
}
|
|
1861
|
+
/** The lifted `draggable` was dropped or let go. */
|
|
1862
|
+
export interface DragEndEvent {
|
|
1863
|
+
type: 'dragend';
|
|
1864
|
+
id: ElementId;
|
|
1865
|
+
}
|
|
1866
|
+
/** One thing dragged in from outside the page: text (or a link, `text/uri-list`), or a file's bytes in base64. */
|
|
1867
|
+
export interface DropItem {
|
|
1868
|
+
type: string;
|
|
1869
|
+
text?: string;
|
|
1870
|
+
name?: string;
|
|
1871
|
+
data?: string;
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* A drag over a `dropdestination`: it `entered`, `exited`, or `performed`
|
|
1875
|
+
* (was released over it). `source` is the lifted `draggable`'s id for a drag
|
|
1876
|
+
* that started in the app; `items` what came from outside the page. The
|
|
1877
|
+
* point is in the destination's own coordinates (0, 0 while entering and leaving).
|
|
1878
|
+
*/
|
|
1879
|
+
export interface DropEvent {
|
|
1880
|
+
type: 'drop';
|
|
1881
|
+
id: ElementId;
|
|
1882
|
+
phase: 'entered' | 'exited' | 'performed';
|
|
1883
|
+
source?: ElementId;
|
|
1884
|
+
items?: DropItem[];
|
|
1885
|
+
x: number;
|
|
1886
|
+
y: number;
|
|
1887
|
+
}
|
|
1775
1888
|
|
|
1776
1889
|
/** The Phase 8 events (merged into `RendererEvent` above). */
|
|
1777
|
-
export type InteractionEvent = DragEvent | LongPressEvent | DeleteEvent | RefreshEvent;
|
|
1890
|
+
export type InteractionEvent = DragEvent | LongPressEvent | DeleteEvent | RefreshEvent | MoveEvent | DragStartEvent | DragEndEvent | DropEvent;
|
|
1778
1891
|
// Phase 8: alerts, controls, effects
|
|
1779
1892
|
// (docs/ops-protocol.md, "Phase 8: interaction"; gestures, swipe actions,
|
|
1780
1893
|
// context menus, list rows and refresh live in their own block)
|
|
@@ -1970,11 +2083,24 @@ export interface ListProps {
|
|
|
1970
2083
|
background?: 'hidden';
|
|
1971
2084
|
/** `.scrollDismissesKeyboard(.immediately | .interactively)`: scrolling blurs the text input being edited inside. */
|
|
1972
2085
|
dismissesKeyboard?: boolean;
|
|
2086
|
+
/**
|
|
2087
|
+
* `.scrollEdgeEffectStyle(_:for:)` / `.scrollEdgeEffectHidden(_:for:)`: the
|
|
2088
|
+
* scroll edge effect where the list runs under its screen's bar (`top`) or
|
|
2089
|
+
* a bar covering the bottom (`bottom`); an absent edge is automatic (soft).
|
|
2090
|
+
*/
|
|
2091
|
+
edgeEffect?: EdgeEffectProps;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
export interface EdgeEffectProps {
|
|
2095
|
+
top?: 'hard' | 'soft' | 'hidden';
|
|
2096
|
+
bottom?: 'hard' | 'soft' | 'hidden';
|
|
1973
2097
|
}
|
|
1974
2098
|
|
|
1975
2099
|
export interface ScrollViewProps {
|
|
1976
2100
|
/** As for `list`. */
|
|
1977
2101
|
dismissesKeyboard?: boolean;
|
|
2102
|
+
/** As for `list`. */
|
|
2103
|
+
edgeEffect?: EdgeEffectProps;
|
|
1978
2104
|
}
|
|
1979
2105
|
|
|
1980
2106
|
/**
|
|
@@ -1992,6 +2118,59 @@ export interface ElementPropsByKind {
|
|
|
1992
2118
|
}
|
|
1993
2119
|
|
|
1994
2120
|
/** `ScrollViewProxy.scrollTo(_:anchor:)`: where the target should end up (null: as little scrolling as shows it). */
|
|
2121
|
+
/**
|
|
2122
|
+
* SF Symbols' effects (`.symbolEffect`), drawn with CSS on the styled box:
|
|
2123
|
+
* `bounce`, `pulse`, `variableColor` (as a pulse), `scale` (up while
|
|
2124
|
+
* active; `down` shrinks), `rotate`, `wiggle`, `breathe`, `appear` /
|
|
2125
|
+
* `disappear` (shown / hidden while active). Without `trigger` the effect
|
|
2126
|
+
* runs while the style carries it (`repeat` times, or forever); with one, it
|
|
2127
|
+
* plays once (`repeat` times) whenever `trigger` changes, like
|
|
2128
|
+
* `.symbolEffect(_:value:)`.
|
|
2129
|
+
*/
|
|
2130
|
+
export interface SymbolEffectStyle {
|
|
2131
|
+
effect: 'bounce' | 'pulse' | 'variableColor' | 'scale' | 'rotate' | 'wiggle' | 'breathe' | 'appear' | 'disappear';
|
|
2132
|
+
direction?: 'up' | 'down';
|
|
2133
|
+
/** Seconds per run at speed 1 is the effect's own; `speed` divides it. */
|
|
2134
|
+
speed?: number;
|
|
2135
|
+
repeat?: number | 'forever';
|
|
2136
|
+
trigger?: number;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
export interface Style {
|
|
2140
|
+
/** The page sends `hover` events as a mouse enters and leaves the box (`.onHover`). */
|
|
2141
|
+
hover?: boolean;
|
|
2142
|
+
/** `.help(_:)`: a tooltip on the box. */
|
|
2143
|
+
help?: string;
|
|
2144
|
+
/** `.pointerStyle(_:)`: the CSS cursor over the box (`pointer`, `text`, `grab`…). */
|
|
2145
|
+
pointerStyle?: string;
|
|
2146
|
+
symbolEffect?: SymbolEffectStyle;
|
|
2147
|
+
/**
|
|
2148
|
+
* `.matchedGeometryEffect(id:in:)`: when an animated pass removes a box
|
|
2149
|
+
* with this id and inserts another with it, the inserted one starts from
|
|
2150
|
+
* the removed one's frame and the removed one's exit travels to the new
|
|
2151
|
+
* frame. The namespace is part of the id.
|
|
2152
|
+
*/
|
|
2153
|
+
matchedGeometry?: string;
|
|
2154
|
+
/**
|
|
2155
|
+
* `.contentTransition(_:)`: how a text under the box changes in a pass that
|
|
2156
|
+
* animates. `numericText` rolls the old text out upward and the new one in
|
|
2157
|
+
* from below, or the other way when the new text reads as a smaller number
|
|
2158
|
+
* (`countsDown` fixes the direction); `opacity` and `interpolate` crossfade
|
|
2159
|
+
* them.
|
|
2160
|
+
*/
|
|
2161
|
+
contentTransition?: { kind: 'numericText' | 'opacity' | 'interpolate'; countsDown?: boolean };
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
/**
|
|
2165
|
+
* A mouse (or pen) entered (`value: true`) or left a box whose style has
|
|
2166
|
+
* `hover`. Touch never hovers.
|
|
2167
|
+
*/
|
|
2168
|
+
export interface HoverEvent {
|
|
2169
|
+
type: 'hover';
|
|
2170
|
+
id: ElementId;
|
|
2171
|
+
value: boolean;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
1995
2174
|
export interface ScrollToCommand {
|
|
1996
2175
|
anchor: UnitPoint | null;
|
|
1997
2176
|
/** Increases with every request (across readers); the page carries out each serial once. */
|