@solidrt/components 0.0.51 → 0.0.52
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/AGENTS.md +34 -7
- package/README.md +47 -10
- package/demos/README.md +24 -0
- package/demos/assets/icon.png +0 -0
- package/demos/assets/icon.svg +23 -0
- package/demos/package.json +9 -0
- package/demos/src/gallery.tsx +866 -0
- package/demos/tsconfig.json +15 -0
- package/docs/button.md +1 -1
- package/docs/focus-nav.md +1 -1
- package/docs/icon.md +1 -1
- package/docs/item.md +1 -1
- package/docs/policy.md +1 -1
- package/docs/scroll-view.md +22 -1
- package/docs/split-view.md +1 -1
- package/docs/theme.md +14 -2
- package/docs/types.md +4 -0
- package/package.json +4 -3
- package/src/badge.tsx +21 -14
- package/src/button.tsx +13 -7
- package/src/card.tsx +10 -3
- package/src/checkbox.tsx +8 -2
- package/src/context-menu.tsx +9 -6
- package/src/divider.tsx +8 -3
- package/src/editor-field.tsx +18 -11
- package/src/field.tsx +4 -2
- package/src/icon.tsx +8 -4
- package/src/image.tsx +10 -3
- package/src/index.ts +10 -1
- package/src/item.tsx +12 -7
- package/src/nav-shell.tsx +6 -16
- package/src/policy.ts +9 -4
- package/src/pressable.tsx +11 -2
- package/src/progress-bar.tsx +4 -3
- package/src/qrcode.tsx +7 -5
- package/src/radio.tsx +12 -4
- package/src/rich-text-editor.tsx +5 -3
- package/src/scroll-view.tsx +76 -7
- package/src/segmented-control.tsx +14 -5
- package/src/select.tsx +23 -12
- package/src/slider.tsx +31 -5
- package/src/spinner.tsx +5 -2
- package/src/split-view.tsx +3 -4
- package/src/switch.tsx +8 -2
- package/src/text-input.tsx +4 -2
- package/src/text.tsx +22 -2
- package/src/theme.ts +72 -20
- package/src/tooltip.tsx +16 -5
- package/src/types.ts +119 -1
- package/src/view.tsx +11 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
"jsx": "preserve",
|
|
6
|
+
"jsxImportSource": "@solidrt/core",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"allowImportingTsExtensions": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"types": ["@solidrt/flux-types"]
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|
package/docs/button.md
CHANGED
|
@@ -18,4 +18,4 @@ An `onPress` returning a promise makes the button an async action: while it is u
|
|
|
18
18
|
<Button onPress={async () => { await save() }}>Save</Button>
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
A focused Button (see `createFocusNav`) draws a
|
|
21
|
+
A focused Button (see `createFocusNav`) draws a ring in the theme `ring` color under the `focusRing` policy (text-colored by default so it stays visible on primary-filled buttons) and activates on Enter, Space, or a remote's center key. `focusable` (default true) opts out of focus-navigation candidacy; disabled buttons are never candidates.
|
package/docs/focus-nav.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# createFocusNav
|
|
2
2
|
|
|
3
|
-
Focus navigation for pointer-free control (TV remote, keyboard, gamepad), moving real focus across the elements declaring `focusable`. Two movement types over the same candidates: spatial (arrow keys, dpad) picks the nearest candidate in the pressed direction by on-screen boxes, and sequential (Tab / Shift+Tab) walks visual reading order - rows top to bottom, left to right - wrapping at the ends. Enter / remote center / gamepad south activates the focused control. Nothing is focused until the first navigation press; pointer input works unchanged throughout.
|
|
3
|
+
Focus navigation for pointer-free control (TV remote, keyboard, gamepad), moving real focus across the elements declaring `focusable`. Two movement types over the same candidates: spatial (arrow keys, dpad) picks the nearest candidate in the pressed direction by on-screen boxes, and sequential (Tab / Shift+Tab) walks visual reading order - rows top to bottom, left to right - wrapping at the ends. Enter / remote center / gamepad south activates the focused control. Nothing is focused until the first navigation press; pointer input works unchanged throughout. Every interactive control (Button, Item, TextInput, RichTextEditor, Checkbox, Radio, Switch, Select and its options, SegmentedControl segments, Slider) is a candidate unless disabled, and draws the theme's `ring` color at `borderWidth.focus` while focused under the `focusRing` policy; the Slider steps its value with the arrow keys.
|
|
4
4
|
|
|
5
5
|
```jsx
|
|
6
6
|
import { createFocusNav } from "@solidrt/components"
|
package/docs/icon.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Icon
|
|
2
2
|
|
|
3
|
-
A thin themed wrapper over the core `parseSvg` primitive. `src` is a whole SVG document as a string; the component parses it once (memoized), maps the draws to `<d-path>` in a square `
|
|
3
|
+
A thin themed wrapper over the core `parseSvg` primitive. `src` is a whole SVG document as a string; the component parses it once (memoized), maps the draws to `<d-path>` in a square `designSize`-fitted box (`size`, default 24) and, for monochrome icons that stroke/fill with `currentColor`, recolors it via `color` (default the theme text color). It carries no icon set and no name registry, so any `currentColor` SVG works (Lucide, Feather, Heroicons, ...) and only the icons you import are bundled. Multi-color documents keep their own fills. For a non-square box, use `parseSvg` directly.
|
|
4
4
|
|
|
5
5
|
Icons are just SVG strings: import them as assets (`import House from "lucide-static/icons/house.svg"`, resolved to a string), pull them from a string export, or inline a literal.
|
|
6
6
|
|
package/docs/item.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Item
|
|
2
2
|
|
|
3
|
-
A list row: `startContent` (icon, avatar, checkbox), a `label` with an optional `description` under it, and `endContent` (badge, timestamp, action) pushed to the end. String/number label and description render as themed body and muted
|
|
3
|
+
A list row: `startContent` (icon, avatar, checkbox), a `label` with an optional `description` under it, and `endContent` (badge, timestamp, action) pushed to the end. String/number label and description render as themed body and muted body text; anything else as-is. The dense-data workhorse: rows compose with `<For>` inside a plain column view or `ScrollView` - there is no List wrapper, because a column IS the list. Paddings and gaps are density-scaled, so a `<Density>` region compacts rows wholesale.
|
|
4
4
|
|
|
5
5
|
```jsx
|
|
6
6
|
import { Item, Badge, Icon } from "@solidrt/components"
|
package/docs/policy.md
CHANGED
|
@@ -10,7 +10,7 @@ The fields:
|
|
|
10
10
|
- `focusRing` (`boolean`) - whether focused controls draw a visible focus indicator (true when a keyboard or gamepad/remote is present).
|
|
11
11
|
- `textScale` (`number`) - multiplier on type-scale font sizes; defaults to the OS text-scale preference.
|
|
12
12
|
- `textWeightDelta` (`number`) - weight compensation (steps of 100) for light-on-dark text on low-DPI displays.
|
|
13
|
-
- `navigation` (`"bottomTabs" | "rail" | "sidebar"`) - recommended nav layout, derived from the window
|
|
13
|
+
- `navigation` (`"bottomTabs" | "rail" | "sidebar"`) - recommended nav layout, derived from the pane count: `sidebar` beside a two-pane layout, `bottomTabs` under a single pane (a side strip spends the width a narrow window is short of). `rail` is never derived; set it for a content-dense two-pane app. `NavShell` follows it.
|
|
14
14
|
- `layout` (`"singlePane" | "twoPane"`) - recommended pane count, derived from the window size class. `SplitView` follows it.
|
|
15
15
|
|
|
16
16
|
```jsx
|
package/docs/scroll-view.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ScrollView
|
|
2
2
|
|
|
3
|
-
A scrollable region; vertical by default, `horizontal` to flip. Both the wheel and dragging scroll the content: the drag activates after a small movement threshold along the scroll axis, also when it starts on a pressable (the press is cancelled and its feedback retracts), and keeps scrolling when the pointer leaves the box. No momentum/fling yet.
|
|
3
|
+
A scrollable region; vertical by default, `horizontal` to flip. Both the wheel and dragging scroll the content: the drag activates after a small movement threshold along the scroll axis, also when it starts on a pressable (the press is cancelled and its feedback retracts), and keeps scrolling when the pointer leaves the box. Scrolling glides: the offset springs to each new target (250 ms, critically damped), so a wheel notch never jumps and a burst of notches reads as one motion; a dragging finger is tracked exactly, without the spring. No momentum/fling yet.
|
|
4
4
|
|
|
5
5
|
```jsx
|
|
6
6
|
import { ScrollView, Text } from "@solidrt/components"
|
|
@@ -11,4 +11,25 @@ import { For } from "@solidrt/core"
|
|
|
11
11
|
</ScrollView>
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
`scrollRef` hands out the scroll handle from `createScroll`: `offset()` and `range()` (the largest reachable offset, refreshed each layout) are reactive; `scrollTo({ x, y, behavior })` and `scrollBy({ x, y, behavior })` clamp to the range, an omitted axis stays put, and `behavior: "instant"` writes without the spring (the web's word; `"auto"` and `"smooth"` are the default motion). Scroll policies are written against it in the app. A transcript that opens at its newest message and then follows growth, without yanking a reader who has scrolled back:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
let [scroll, setScroll] = createSignal<Scroll>()
|
|
18
|
+
createEffect(
|
|
19
|
+
() => scroll()?.range(),
|
|
20
|
+
(r, prev) =>
|
|
21
|
+
untrack(() => {
|
|
22
|
+
let s = scroll()
|
|
23
|
+
if (!s || !r) return
|
|
24
|
+
if (!prev || prev.y === 0) s.scrollTo({ y: Infinity, behavior: "instant" })
|
|
25
|
+
else if (s.offset().y >= prev.y - 1) s.scrollTo({ y: Infinity })
|
|
26
|
+
}),
|
|
27
|
+
)
|
|
28
|
+
<ScrollView scrollRef={setScroll}>...</ScrollView>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The range changes whenever the content or the viewport changes size. The first fill (nothing was scrollable before it, whether it mounted with the view or arrived a second later) lands instantly, as a chat opens at its end; after that the view follows the end only if it was at the previous end, and the spring makes that follow a glide. The handle arrives once the component has settled, after an effect's first compute, so hold it in a signal (a setter can be passed as the ref) rather than a plain variable, which the effect would find unset and never track. The offset is read untracked: the policy reacts to the range, not to every scroll.
|
|
32
|
+
|
|
33
|
+
A `scrollX`/`scrollY` entry in `transition` replaces the default spring: `transition={{ scrollY: { duration: 400, bounce: 0.2 } }}` (keep it a spring rather than a tween, because the wheel retargets mid-flight). The other entries animate the box itself and its background/border as on any component.
|
|
34
|
+
|
|
14
35
|
The underlying geometry primitive `createScroll` is available from `@solidrt/core` for building custom scrollers.
|
package/docs/split-view.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SplitView
|
|
2
2
|
|
|
3
|
-
A list-detail container driven by `policy.layout`: `twoPane` shows the `list` pane (width `listWidth`, default
|
|
3
|
+
A list-detail container driven by `policy.layout`: `twoPane` shows the `list` pane (width `listWidth`, default `theme.size.splitViewList`) beside the `detail` pane, `singlePane` shows one at a time per `showDetail`. Keep pane state (selection, scroll) in the app, not in the panes: crossing a breakpoint re-arranges and can remount them. It draws no chrome and adds no padding; a back affordance in the single-pane detail is the app's to render (fork on `policy.layout`).
|
|
4
4
|
|
|
5
5
|
```jsx
|
|
6
6
|
import { SplitView } from "@solidrt/components"
|
package/docs/theme.md
CHANGED
|
@@ -29,11 +29,23 @@ let def = {
|
|
|
29
29
|
setTheme(defineTheme(def, "dark"))
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
The type scale derives from `text.base` (the body size, default 14) and `text.ratio` (default 1.26): caption, label
|
|
32
|
+
The type scale derives from `text.base` (the body size, default 14) and `text.ratio` (default 1.26): caption sits one step under body, label is body at an emphasized weight, title and heading sit one and two steps above, rounded to whole pixels, with per-role `text.roles` overrides for sizes, line heights, and weights. De-emphasis is a color (`textMuted`), not a size: caption is for small glanceable text (badges, tab labels, timestamps) and stays in the full text color.
|
|
33
33
|
|
|
34
34
|
## Tokens
|
|
35
35
|
|
|
36
|
-
The color tokens are `background` (window fill), `surface` (control/card fill), `surfaceAlt` (subtle raised/track fill), `text`, `textMuted`, `border`, `primary`/`onPrimary`, `secondary`/`onSecondary` (lower-emphasis accent), `danger` (validation/destructive), `scrim` (modal dim), and the feedback pair `overlayHover`/`overlayPressed`: translucent tints components draw OVER a control's own fill, so one token pair gives hover/pressed feedback on every fill color, including caller-set ones. Non-color tokens are `spacing`, `radius`, `borderWidth
|
|
36
|
+
The color tokens are `background` (window fill), `surface` (control/card fill), `surfaceAlt` (subtle raised/track fill), `text`, `textMuted`, `border`, `primary`/`onPrimary`, `secondary`/`onSecondary` (lower-emphasis accent), `danger` (validation/destructive), `scrim` (modal dim), `ring` (the focus ring; defaults to `text` so it stays visible on primary fills), and the feedback pair `overlayHover`/`overlayPressed`: translucent tints components draw OVER a control's own fill, so one token pair gives hover/pressed feedback on every fill color, including caller-set ones. Non-color tokens are `spacing`, `radius`, `borderWidth` (`sm` for borders, `focus` for the ring), `size` (app-wide default extents: `navRail` 72, `navSidebar` 220, `splitViewList` 320, `menuMinWidth` 120, `slider` 200; each overridable per instance through its layout or prop), and `text` (the type scale: `caption`/`label`/`body`/`title`/`heading` roles, each `{ size, lineHeight, weight }`, plus `fontFamily` and `monoFamily` for code).
|
|
37
|
+
|
|
38
|
+
## Spacing
|
|
39
|
+
|
|
40
|
+
Spacing is one base unit: `spacing` in a theme definition is a number (default 4) and the steps are multiples of it (`sm` 1x, `md` 2x, `lg` 4x, `xl` 5x). Components read them through `space()`, which applies the density policy on top, so a theme sets the rhythm and density tightens it. Pass an object (`spacing: { sm, md, lg, xl }`, any subset) to pin individual steps.
|
|
41
|
+
|
|
42
|
+
## Radius
|
|
43
|
+
|
|
44
|
+
Corner radius is set once: `radius` in a theme definition is a single number, the control radius (default 8), and the scale derives from it: `md` is the base (Button, TextInput, RichTextEditor, Select, SegmentedControl, QrCode), `sm` half of it (Checkbox, Item, NavShell items, Select and ContextMenu popups, Tooltip), `lg` one and a half (Card), and `full` the pill (Badge). Set `radius: 0` for a square theme, `radius: 12` for a soft one; buttons and inputs always match. Shapes derived from a control's own height (Switch, Slider, ProgressBar, Radio) are not on the scale. Pass an object (`radius: { sm, md, lg, full }`, any subset) to pin individual steps instead.
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
setTheme({ radius: 4 }) // sm 2, md 4, lg 6
|
|
48
|
+
```
|
|
37
49
|
|
|
38
50
|
## Per-component overrides
|
|
39
51
|
|
package/docs/types.md
CHANGED
|
@@ -3,3 +3,7 @@
|
|
|
3
3
|
Most components group their props into two objects, split by one rule: `layout` properties feed the layout engine (flexbox/grid, sizing, padding, margin, position - the core `LayoutProps` set) and changing them triggers a relayout; `style` properties are paint-only and never affect layout: `color`, `backgroundColor`, `borderColor`, `borderWidth`, `borderRadius`, `opacity`, and the transform (`x`, `y`, `scale`, `rotate`, `rotateX`/`rotateY` with `perspective`, `originX`/`originY`, `clipRadius`). Event handlers (`onPointerDown`, `onKeyDown`, ...) are top-level props, never inside `layout` or `style`.
|
|
4
4
|
|
|
5
5
|
`StyleProps` is that paint set. `TextLayoutProps` extends `LayoutProps` with the font fields (`fontFamily`, `fontSize`, `lineHeight`, `fontStyle`, `fontWeight`, `textAlign`, `maxLines`) because text shaping affects measurement; note `lineHeight` is a multiplier of `fontSize` (the theme uses 1.3-1.6), not a pixel value. `Option` (`{ value, label }`) is the shared shape of the single-choice controls (`Select`, `SegmentedControl`): shared shapes go through this module so components never import a sibling.
|
|
6
|
+
|
|
7
|
+
`TransitionProps` (`transition`, `onTransitionEnd`) is the third top-level group, in the component's own vocabulary rather than core's: a declaration names the view-level properties (`opacity`, `x`, `y`, `scale*`, `rotate*`, `origin*`, `perspective`, `clipRadius`) and the style ones (`backgroundColor`, `borderColor`, `borderWidth`, `borderRadius`), plus `all`, a shorthand string, and `stagger` - `<Button transition={{ backgroundColor: { duration: 300 }, opacity: "200ms ease-out" }}>`. Core's paint names (`color`, `radius`, `strokeWidth`) are rejected by the types: a component is a root view plus the rects it draws for `style`, and `splitTransition` hands each entry to the node that owns it (the background rect gets `backgroundColor`/`borderRadius`, the stroke rect `borderColor`/`borderWidth`/`borderRadius`, the root view the rest). `onTransitionEnd` reports the component name (`backgroundColor`, not `color`). `Text` adds `color` (its text node), `ScrollView` adds `scrollX`/`scrollY` (its viewport).
|
|
8
|
+
|
|
9
|
+
Controls whose paint is their own - `Switch` knob, `Slider` thumb, `Checkbox` mark, `Radio` dot, `ProgressBar` fill, `Spinner`, `Icon`, `QrCode`, and the chrome of `NavShell`, `ContextMenu`, `Field` - animate the view-level entries only for now; their internal parts are not reachable through `transition` yet (okf/backlog/component-transitions-internal-paint.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"funding": "https://github.com/sponsors/wellawaretech",
|
|
6
6
|
"author": "Antoine van Wel",
|
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
"src/",
|
|
15
15
|
"docs/",
|
|
16
16
|
"examples/",
|
|
17
|
+
"demos/",
|
|
17
18
|
"AGENTS.md"
|
|
18
19
|
],
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"qrcode-generator": "^2.0.4"
|
|
21
22
|
},
|
|
22
23
|
"peerDependencies": {
|
|
23
|
-
"@solidjs/signals": "2.0.0-rc.
|
|
24
|
-
"@solidrt/core": "0.0.
|
|
24
|
+
"@solidjs/signals": "2.0.0-rc.1",
|
|
25
|
+
"@solidrt/core": "0.0.52"
|
|
25
26
|
}
|
|
26
27
|
}
|
package/src/badge.tsx
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { Show, children } from "@solidrt/core"
|
|
2
2
|
import type { LayoutProps } from "@solidrt/core"
|
|
3
3
|
import { theme } from "./theme"
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
4
|
+
import { space } from "./spacing"
|
|
5
|
+
import { typeStyle, typeWeight, lightOnDark } from "./typography"
|
|
6
|
+
import { policy } from "./policy"
|
|
7
|
+
import type { StyleProps, TransitionProps } from "./types"
|
|
8
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
6
9
|
|
|
7
10
|
export type BadgeVariant = "primary" | "neutral" | "danger"
|
|
8
11
|
|
|
9
|
-
export interface BadgeProps {
|
|
12
|
+
export interface BadgeProps extends TransitionProps {
|
|
10
13
|
// A string/number renders as the themed pill label; anything else is rendered
|
|
11
14
|
// as-is (an icon, a dot, ...).
|
|
12
15
|
children?: any
|
|
@@ -16,10 +19,6 @@ export interface BadgeProps {
|
|
|
16
19
|
style?: StyleProps
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
// A rounded radius large enough to fully pill any typical badge height; the
|
|
20
|
-
// renderer clamps it to half the box, so both ends stay round.
|
|
21
|
-
const RADIUS = 999
|
|
22
|
-
|
|
23
22
|
// A small rounded pill for counts, labels, and status. Accent fill with
|
|
24
23
|
// onPrimary text by default; override the fill via style.backgroundColor and the
|
|
25
24
|
// label color via style.color.
|
|
@@ -39,22 +38,26 @@ export function Badge(props: BadgeProps) {
|
|
|
39
38
|
let styled = () => ({ ...theme.components.badge, ...props.style })
|
|
40
39
|
let bg = () => styled().backgroundColor ?? colors().bg
|
|
41
40
|
let fg = () => styled().color ?? colors().fg
|
|
42
|
-
let radius = () => styled().borderRadius ??
|
|
41
|
+
let radius = () => styled().borderRadius ?? theme.radius.full
|
|
43
42
|
// Resolved once via children(): the typeof probe and the mount sites must
|
|
44
43
|
// share one build - reading the raw getter again would orphan native nodes.
|
|
45
44
|
let resolved = children(() => props.children)
|
|
46
45
|
let isText = () => typeof resolved() === "string" || typeof resolved() === "number"
|
|
47
46
|
let labelOnDark = () => lightOnDark(fg(), bg())
|
|
48
47
|
|
|
48
|
+
let split = () => splitTransition(props.transition)
|
|
49
|
+
|
|
49
50
|
return (
|
|
50
51
|
<view
|
|
52
|
+
transition={split().root}
|
|
53
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
51
54
|
flexDirection="row"
|
|
52
55
|
alignItems="center"
|
|
53
56
|
justifyContent="center"
|
|
54
|
-
paddingLeft={
|
|
55
|
-
paddingRight={
|
|
56
|
-
paddingTop={2}
|
|
57
|
-
paddingBottom={2}
|
|
57
|
+
paddingLeft={space("md")}
|
|
58
|
+
paddingRight={space("md")}
|
|
59
|
+
paddingTop={Math.round(space("sm") / 2)}
|
|
60
|
+
paddingBottom={Math.round(space("sm") / 2)}
|
|
58
61
|
{...props.layout}
|
|
59
62
|
x={styled().x}
|
|
60
63
|
y={styled().y}
|
|
@@ -62,9 +65,13 @@ export function Badge(props: BadgeProps) {
|
|
|
62
65
|
rotate={styled().rotate}
|
|
63
66
|
opacity={styled().opacity}
|
|
64
67
|
>
|
|
65
|
-
<d-rect color={bg()} radius={radius()} />
|
|
68
|
+
<d-rect transition={split().background} onTransitionEnd={transitionEndFor("background", props.onTransitionEnd)} color={bg()} radius={radius()} />
|
|
66
69
|
<Show when={isText()} fallback={resolved()}>
|
|
67
|
-
<text
|
|
70
|
+
<text
|
|
71
|
+
color={fg()}
|
|
72
|
+
{...typeStyle("caption", labelOnDark())}
|
|
73
|
+
fontWeight={typeWeight(600, theme.text.caption.size * policy.textScale, labelOnDark())}
|
|
74
|
+
>
|
|
68
75
|
{resolved()}
|
|
69
76
|
</text>
|
|
70
77
|
</Show>
|
package/src/button.tsx
CHANGED
|
@@ -6,12 +6,13 @@ import { space } from "./spacing"
|
|
|
6
6
|
import { typeStyle, lightOnDark } from "./typography"
|
|
7
7
|
import { Spinner } from "./spinner"
|
|
8
8
|
import type { LayoutProps } from "@solidrt/core"
|
|
9
|
-
import type { StyleProps } from "./types"
|
|
9
|
+
import type { StyleProps, TransitionProps } from "./types"
|
|
10
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
10
11
|
|
|
11
12
|
export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger"
|
|
12
13
|
export type ButtonSize = "sm" | "md" | "lg"
|
|
13
14
|
|
|
14
|
-
export interface ButtonProps {
|
|
15
|
+
export interface ButtonProps extends TransitionProps {
|
|
15
16
|
// A string/number is rendered as the themed label; anything else is rendered
|
|
16
17
|
// as-is, so a button can hold custom content (an icon, a row, ...).
|
|
17
18
|
children?: any
|
|
@@ -47,9 +48,8 @@ const SIZE_WIDTH: Record<ButtonSize, number> = { sm: 88, md: 120, lg: 160 }
|
|
|
47
48
|
// box via style and the padding/sizing via layout; because hover is an
|
|
48
49
|
// overlay, it composes over a caller-set backgroundColor too. When disabled,
|
|
49
50
|
// it takes no pointer events at all. Focus (spatial nav) draws a ring under
|
|
50
|
-
// the focusRing policy
|
|
51
|
-
//
|
|
52
|
-
// createPress).
|
|
51
|
+
// the focusRing policy in the theme's ring color; Enter/Space/remote-select
|
|
52
|
+
// activates (handled by createPress).
|
|
53
53
|
export function Button(props: ButtonProps) {
|
|
54
54
|
// Fill and label color per variant, read reactively from the theme. No
|
|
55
55
|
// variant draws a border.
|
|
@@ -95,7 +95,7 @@ export function Button(props: ButtonProps) {
|
|
|
95
95
|
let press = createPress(props)
|
|
96
96
|
let style = () => ({
|
|
97
97
|
...styled(),
|
|
98
|
-
...(press.focused() && policy.focusRing ? { borderWidth:
|
|
98
|
+
...(press.focused() && policy.focusRing ? { borderWidth: theme.borderWidth.focus, borderColor: theme.color.ring } : {}),
|
|
99
99
|
backgroundColor: bg(),
|
|
100
100
|
borderRadius: radius(),
|
|
101
101
|
// Always a number: a scale that flips from a number back to undefined
|
|
@@ -104,8 +104,12 @@ export function Button(props: ButtonProps) {
|
|
|
104
104
|
scale: (styled().scale ?? 1) * (press.pressed() && policy.motion !== "none" ? 0.97 : 1),
|
|
105
105
|
})
|
|
106
106
|
|
|
107
|
+
let split = () => splitTransition(props.transition)
|
|
108
|
+
|
|
107
109
|
return (
|
|
108
110
|
<view
|
|
111
|
+
transition={split().root}
|
|
112
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
109
113
|
ref={(n: { id: number }) => {
|
|
110
114
|
press.ref(n)
|
|
111
115
|
props.ref?.(n)
|
|
@@ -130,7 +134,7 @@ export function Button(props: ButtonProps) {
|
|
|
130
134
|
focusable={(props.focusable ?? true) && props.disabled !== true}
|
|
131
135
|
pointerEvents={props.disabled ? "none" : undefined}
|
|
132
136
|
>
|
|
133
|
-
<d-rect color={style().backgroundColor ?? "transparent"} radius={style().borderRadius} />
|
|
137
|
+
<d-rect transition={split().background} onTransitionEnd={transitionEndFor("background", props.onTransitionEnd)} color={style().backgroundColor ?? "transparent"} radius={style().borderRadius} />
|
|
134
138
|
<d-rect color={overlay(press.state())} radius={style().borderRadius} />
|
|
135
139
|
<Show when={isText()} fallback={resolved()}>
|
|
136
140
|
<text color={press.pending() ? "transparent" : label()} {...typeStyle("body", labelOnDark())}>
|
|
@@ -145,6 +149,8 @@ export function Button(props: ButtonProps) {
|
|
|
145
149
|
<Show when={(style().borderWidth ?? 0) > 0}>
|
|
146
150
|
<d-rect
|
|
147
151
|
drawStyle="stroke"
|
|
152
|
+
transition={split().border}
|
|
153
|
+
onTransitionEnd={transitionEndFor("border", props.onTransitionEnd)}
|
|
148
154
|
color={style().borderColor ?? "transparent"}
|
|
149
155
|
strokeWidth={style().borderWidth}
|
|
150
156
|
radius={style().borderRadius}
|
package/src/card.tsx
CHANGED
|
@@ -3,9 +3,10 @@ import type { LayoutProps } from "@solidrt/core"
|
|
|
3
3
|
import { theme } from "./theme"
|
|
4
4
|
import { typeStyle } from "./typography"
|
|
5
5
|
import { space } from "./spacing"
|
|
6
|
-
import type { StyleProps } from "./types"
|
|
6
|
+
import type { StyleProps, TransitionProps } from "./types"
|
|
7
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
7
8
|
|
|
8
|
-
export interface CardProps {
|
|
9
|
+
export interface CardProps extends TransitionProps {
|
|
9
10
|
children?: any
|
|
10
11
|
// Optional heading rendered above the content.
|
|
11
12
|
title?: string
|
|
@@ -25,8 +26,12 @@ export function Card(props: CardProps) {
|
|
|
25
26
|
let radius = () => styled().borderRadius ?? theme.radius.lg
|
|
26
27
|
let hasBorder = () => styled().borderWidth != null || styled().borderColor != null
|
|
27
28
|
|
|
29
|
+
let split = () => splitTransition(props.transition)
|
|
30
|
+
|
|
28
31
|
return (
|
|
29
32
|
<view
|
|
33
|
+
transition={split().root}
|
|
34
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
30
35
|
ref={props.ref}
|
|
31
36
|
repaintBoundary
|
|
32
37
|
flexDirection="column"
|
|
@@ -39,7 +44,7 @@ export function Card(props: CardProps) {
|
|
|
39
44
|
rotate={styled().rotate}
|
|
40
45
|
opacity={styled().opacity}
|
|
41
46
|
>
|
|
42
|
-
<d-rect color={bg()} radius={radius()} />
|
|
47
|
+
<d-rect transition={split().background} onTransitionEnd={transitionEndFor("background", props.onTransitionEnd)} color={bg()} radius={radius()} />
|
|
43
48
|
<Show when={props.title != null}>
|
|
44
49
|
<text color={theme.color.text} {...typeStyle("title")}>
|
|
45
50
|
{props.title}
|
|
@@ -49,6 +54,8 @@ export function Card(props: CardProps) {
|
|
|
49
54
|
<Show when={hasBorder()}>
|
|
50
55
|
<d-rect
|
|
51
56
|
drawStyle="stroke"
|
|
57
|
+
transition={split().border}
|
|
58
|
+
onTransitionEnd={transitionEndFor("border", props.onTransitionEnd)}
|
|
52
59
|
color={styled().borderColor ?? theme.color.border}
|
|
53
60
|
strokeWidth={styled().borderWidth ?? theme.borderWidth.sm}
|
|
54
61
|
radius={radius()}
|
package/src/checkbox.tsx
CHANGED
|
@@ -2,11 +2,13 @@ import { createSignal, Show } from "@solidrt/core"
|
|
|
2
2
|
import type { LayoutProps } from "@solidrt/core"
|
|
3
3
|
import { createPress } from "./press"
|
|
4
4
|
import { theme } from "./theme"
|
|
5
|
+
import { policy } from "./policy"
|
|
5
6
|
import { densityScale } from "./density"
|
|
6
7
|
import { Icon } from "./icon"
|
|
7
|
-
import type { StyleProps } from "./types"
|
|
8
|
+
import type { StyleProps, TransitionProps } from "./types"
|
|
9
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
8
10
|
|
|
9
|
-
export interface CheckboxProps {
|
|
11
|
+
export interface CheckboxProps extends TransitionProps {
|
|
10
12
|
// Controlled checked state. If omitted, the checkbox is uncontrolled.
|
|
11
13
|
checked?: boolean
|
|
12
14
|
defaultChecked?: boolean
|
|
@@ -46,10 +48,13 @@ export function Checkbox(props: CheckboxProps) {
|
|
|
46
48
|
borderRadius: theme.radius.sm,
|
|
47
49
|
...theme.components.checkbox,
|
|
48
50
|
...props.style,
|
|
51
|
+
...(press.focused() && policy.focusRing ? { borderWidth: theme.borderWidth.focus, borderColor: theme.color.ring } : {}),
|
|
49
52
|
})
|
|
50
53
|
|
|
51
54
|
return (
|
|
52
55
|
<view
|
|
56
|
+
transition={splitTransition(props.transition).root}
|
|
57
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
53
58
|
ref={press.ref}
|
|
54
59
|
repaintBoundary
|
|
55
60
|
width={size()}
|
|
@@ -62,6 +67,7 @@ export function Checkbox(props: CheckboxProps) {
|
|
|
62
67
|
rotate={style().rotate}
|
|
63
68
|
opacity={style().opacity}
|
|
64
69
|
{...press.handlers}
|
|
70
|
+
focusable={!props.disabled}
|
|
65
71
|
pointerEvents={props.disabled ? "none" : undefined}
|
|
66
72
|
>
|
|
67
73
|
<d-rect color={style().backgroundColor ?? "transparent"} radius={style().borderRadius} />
|
package/src/context-menu.tsx
CHANGED
|
@@ -5,6 +5,8 @@ import { theme } from "./theme"
|
|
|
5
5
|
import { policy } from "./policy"
|
|
6
6
|
import { space } from "./spacing"
|
|
7
7
|
import { typeStyle } from "./typography"
|
|
8
|
+
import type { TransitionProps } from "./types"
|
|
9
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
8
10
|
|
|
9
11
|
export interface ContextMenuItem {
|
|
10
12
|
label: string
|
|
@@ -12,7 +14,7 @@ export interface ContextMenuItem {
|
|
|
12
14
|
disabled?: boolean
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export interface ContextMenuProps {
|
|
17
|
+
export interface ContextMenuProps extends TransitionProps {
|
|
16
18
|
items: ContextMenuItem[]
|
|
17
19
|
// The content the menu attaches to.
|
|
18
20
|
children?: any
|
|
@@ -23,8 +25,7 @@ const LONG_PRESS_MS = 500
|
|
|
23
25
|
// Finger travel (window px) that cancels a pending long-press.
|
|
24
26
|
const MOVE_SLOP = 8
|
|
25
27
|
// Minimum distance kept between the menu and the window edges.
|
|
26
|
-
|
|
27
|
-
const MIN_WIDTH = 120
|
|
28
|
+
let margin = () => theme.spacing.sm
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Secondary actions on the wrapped content. The opening gesture follows the
|
|
@@ -113,9 +114,9 @@ export function ContextMenu(props: ContextMenuProps) {
|
|
|
113
114
|
let b = menu && getBoundingBox(menu)
|
|
114
115
|
if (!b) return
|
|
115
116
|
let p = point()
|
|
116
|
-
let x = Math.round(Math.min(Math.max(p.x,
|
|
117
|
+
let x = Math.round(Math.min(Math.max(p.x, margin()), env.windowSize.width - b.width - margin()))
|
|
117
118
|
let y = Math.round(
|
|
118
|
-
Math.max(p.y + b.height > env.windowSize.height -
|
|
119
|
+
Math.max(p.y + b.height > env.windowSize.height - margin() ? p.y - b.height : p.y, margin()),
|
|
119
120
|
)
|
|
120
121
|
let cur = pos()
|
|
121
122
|
if (!cur || cur.x !== x || cur.y !== y) setPos({ x, y })
|
|
@@ -130,7 +131,7 @@ export function ContextMenu(props: ContextMenuProps) {
|
|
|
130
131
|
left={0}
|
|
131
132
|
x={pos()?.x ?? -10000}
|
|
132
133
|
y={pos()?.y ?? 0}
|
|
133
|
-
minWidth={
|
|
134
|
+
minWidth={theme.size.menuMinWidth}
|
|
134
135
|
flexDirection="column"
|
|
135
136
|
paddingTop={theme.spacing.sm}
|
|
136
137
|
paddingBottom={theme.spacing.sm}
|
|
@@ -177,6 +178,8 @@ export function ContextMenu(props: ContextMenuProps) {
|
|
|
177
178
|
|
|
178
179
|
return (
|
|
179
180
|
<view
|
|
181
|
+
transition={splitTransition(props.transition).root}
|
|
182
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
180
183
|
onPointerDown={handleDown}
|
|
181
184
|
onPointerMove={handleMove}
|
|
182
185
|
onPointerUp={cancelHold}
|
package/src/divider.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { LayoutProps } from "@solidrt/core"
|
|
2
2
|
import { theme } from "./theme"
|
|
3
|
-
import type { StyleProps } from "./types"
|
|
3
|
+
import type { StyleProps, TransitionProps } from "./types"
|
|
4
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
4
5
|
|
|
5
|
-
export interface DividerProps {
|
|
6
|
+
export interface DividerProps extends TransitionProps {
|
|
6
7
|
// Line direction. Horizontal (default) is a full-width rule; vertical is a
|
|
7
8
|
// full-height rule for use inside a row.
|
|
8
9
|
orientation?: "horizontal" | "vertical"
|
|
@@ -22,14 +23,18 @@ export function Divider(props: DividerProps) {
|
|
|
22
23
|
let styled = () => ({ ...theme.components.divider, ...props.style })
|
|
23
24
|
let color = () => styled().backgroundColor ?? theme.color.border
|
|
24
25
|
|
|
26
|
+
let split = () => splitTransition(props.transition)
|
|
27
|
+
|
|
25
28
|
return (
|
|
26
29
|
<view
|
|
30
|
+
transition={split().root}
|
|
31
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
27
32
|
width={vertical() ? thickness() : "auto"}
|
|
28
33
|
height={vertical() ? "auto" : thickness()}
|
|
29
34
|
alignSelf="stretch"
|
|
30
35
|
{...props.layout}
|
|
31
36
|
>
|
|
32
|
-
<d-rect color={color()} />
|
|
37
|
+
<d-rect transition={split().background} onTransitionEnd={transitionEndFor("background", props.onTransitionEnd)} color={color()} />
|
|
33
38
|
</view>
|
|
34
39
|
)
|
|
35
40
|
}
|
package/src/editor-field.tsx
CHANGED
|
@@ -22,7 +22,8 @@ import type { Color, Gradient, KeyEvent, LayoutProps, PointerEvent, TextInputHin
|
|
|
22
22
|
import type { Element } from "solid-js"
|
|
23
23
|
import type { MeasureTextOptions, TextRunRange } from "flux:rendertree"
|
|
24
24
|
import { registerNavAction } from "./focus-nav"
|
|
25
|
-
import type { StyleProps } from "./types"
|
|
25
|
+
import type { StyleProps, TransitionProps } from "./types"
|
|
26
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
26
27
|
import { theme } from "./theme"
|
|
27
28
|
import { policy } from "./policy"
|
|
28
29
|
import { space } from "./spacing"
|
|
@@ -44,7 +45,7 @@ export type LineRender = {
|
|
|
44
45
|
color: () => Color | Gradient
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
export interface EditorFieldProps {
|
|
48
|
+
export interface EditorFieldProps extends TransitionProps {
|
|
48
49
|
/**
|
|
49
50
|
* Creates the buffer the field edits, given the grapheme `step` from the
|
|
50
51
|
* field's geometry (createTextEditorLayout.step); called once.
|
|
@@ -234,14 +235,14 @@ export function EditorField(props: EditorFieldProps) {
|
|
|
234
235
|
})
|
|
235
236
|
|
|
236
237
|
// Style overrides fall back to theme defaults. The border doubles as the
|
|
237
|
-
// focus ring
|
|
238
|
-
// visible indicator.
|
|
238
|
+
// focus ring (ring color at the focus width) while focused, when the
|
|
239
|
+
// focus-ring policy asks for a visible indicator.
|
|
239
240
|
let textColor = () => props.style?.color ?? theme.color.text
|
|
240
241
|
let surfaceColor = () => props.style?.backgroundColor ?? theme.color.surface
|
|
241
|
-
let
|
|
242
|
-
|
|
243
|
-
let borderWidth = () => props.style?.borderWidth ?? theme.borderWidth.sm
|
|
244
|
-
let borderRadius = () => props.style?.borderRadius ?? theme.radius.
|
|
242
|
+
let ring = () => focused() && policy.focusRing
|
|
243
|
+
let borderColor = () => props.style?.borderColor ?? (ring() ? theme.color.ring : theme.color.border)
|
|
244
|
+
let borderWidth = () => props.style?.borderWidth ?? (ring() ? theme.borderWidth.focus : theme.borderWidth.sm)
|
|
245
|
+
let borderRadius = () => props.style?.borderRadius ?? theme.radius.md
|
|
245
246
|
|
|
246
247
|
let showPlaceholder = () => !focused() && value().length === 0 && (props.placeholder ?? "").length > 0
|
|
247
248
|
let showCaret = () => focused() && caretOn() && !showPlaceholder()
|
|
@@ -290,8 +291,12 @@ export function EditorField(props: EditorFieldProps) {
|
|
|
290
291
|
return Math.max(rowHeight(), Math.min(content, max))
|
|
291
292
|
}
|
|
292
293
|
|
|
294
|
+
let split = () => splitTransition(props.transition)
|
|
295
|
+
|
|
293
296
|
return (
|
|
294
297
|
<view
|
|
298
|
+
transition={split().root}
|
|
299
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
295
300
|
ref={(n: { id: number }) => {
|
|
296
301
|
node = n
|
|
297
302
|
unregisterNav?.()
|
|
@@ -304,8 +309,8 @@ export function EditorField(props: EditorFieldProps) {
|
|
|
304
309
|
alignItems="center"
|
|
305
310
|
paddingLeft={space("md")}
|
|
306
311
|
paddingRight={space("md")}
|
|
307
|
-
paddingTop={space("
|
|
308
|
-
paddingBottom={space("
|
|
312
|
+
paddingTop={space("md")}
|
|
313
|
+
paddingBottom={space("md")}
|
|
309
314
|
{...props.layout}
|
|
310
315
|
x={props.style?.x}
|
|
311
316
|
y={props.style?.y}
|
|
@@ -318,9 +323,11 @@ export function EditorField(props: EditorFieldProps) {
|
|
|
318
323
|
onKeyDown={handleKeyDown}
|
|
319
324
|
onTextInput={handleTextInput}
|
|
320
325
|
>
|
|
321
|
-
<d-rect color={surfaceColor()} radius={borderRadius()} />
|
|
326
|
+
<d-rect transition={split().background} onTransitionEnd={transitionEndFor("background", props.onTransitionEnd)} color={surfaceColor()} radius={borderRadius()} />
|
|
322
327
|
<d-rect
|
|
323
328
|
drawStyle="stroke"
|
|
329
|
+
transition={split().border}
|
|
330
|
+
onTransitionEnd={transitionEndFor("border", props.onTransitionEnd)}
|
|
324
331
|
color={borderColor()}
|
|
325
332
|
strokeWidth={borderWidth()}
|
|
326
333
|
radius={borderRadius()}
|
package/src/field.tsx
CHANGED
|
@@ -3,8 +3,10 @@ import type { LayoutProps } from "@solidrt/core"
|
|
|
3
3
|
import { theme } from "./theme"
|
|
4
4
|
import { space } from "./spacing"
|
|
5
5
|
import { typeStyle } from "./typography"
|
|
6
|
+
import type { TransitionProps } from "./types"
|
|
7
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
6
8
|
|
|
7
|
-
export interface FieldProps {
|
|
9
|
+
export interface FieldProps extends TransitionProps {
|
|
8
10
|
// Label above the control.
|
|
9
11
|
label?: string
|
|
10
12
|
// Help text under the control; replaced by `error` while one is set.
|
|
@@ -25,7 +27,7 @@ export interface FieldProps {
|
|
|
25
27
|
// `description` if that matters.
|
|
26
28
|
export function Field(props: FieldProps) {
|
|
27
29
|
return (
|
|
28
|
-
<view flexDirection="column" gap={space("sm")} {...props.layout}>
|
|
30
|
+
<view flexDirection="column" gap={space("sm")} transition={splitTransition(props.transition).root} onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)} {...props.layout}>
|
|
29
31
|
<Show when={props.label != null}>
|
|
30
32
|
<text color={theme.color.text} {...typeStyle("label")}>
|
|
31
33
|
{props.label}
|
package/src/icon.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createMemo, parseSvg, For } from "@solidrt/core"
|
|
2
2
|
import type { LayoutProps } from "@solidrt/core"
|
|
3
3
|
import { theme } from "./theme"
|
|
4
|
+
import type { TransitionProps } from "./types"
|
|
5
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
4
6
|
|
|
5
|
-
export interface IconProps {
|
|
7
|
+
export interface IconProps extends TransitionProps {
|
|
6
8
|
// An SVG document as a string: an imported `.svg` asset, a `lucide-static`
|
|
7
9
|
// string export, or an inline template literal. Monochrome icons that stroke/
|
|
8
10
|
// fill with `currentColor` (Lucide, Feather, Heroicons, ...) get recolored by
|
|
@@ -18,9 +20,9 @@ export interface IconProps {
|
|
|
18
20
|
const SIZE = 24
|
|
19
21
|
|
|
20
22
|
// A themed wrapper over parseSvg: the document parsed once per src/color (a
|
|
21
|
-
// memo), its draws mapped to <d-path> in a square
|
|
23
|
+
// memo), its draws mapped to <d-path> in a square designSize-fitted box colored
|
|
22
24
|
// from the theme by default. That is the only value it adds, so reach for
|
|
23
|
-
// parseSvg plus your own <view
|
|
25
|
+
// parseSvg plus your own <view designSize> when you need a non-square box or
|
|
24
26
|
// want no theme coupling. The plain repaintBoundary keeps the static subtree
|
|
25
27
|
// from re-recording alongside animating siblings; pointerEvents="all" makes
|
|
26
28
|
// the box one hit unit (and skips per-path outline tests) - an icon's shapes
|
|
@@ -31,11 +33,13 @@ export function Icon(props: IconProps) {
|
|
|
31
33
|
|
|
32
34
|
return (
|
|
33
35
|
<view
|
|
36
|
+
transition={splitTransition(props.transition).root}
|
|
37
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
34
38
|
repaintBoundary
|
|
35
39
|
pointerEvents="all"
|
|
36
40
|
width={size()}
|
|
37
41
|
height={size()}
|
|
38
|
-
|
|
42
|
+
designSize={[doc().width, doc().height]}
|
|
39
43
|
{...props.layout}
|
|
40
44
|
>
|
|
41
45
|
<For each={doc().draws}>{(draw) => <d-path {...draw} />}</For>
|