@solidrt/components 0.0.49 → 0.0.51
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 +66 -85
- package/README.md +387 -314
- package/docs/badge.md +10 -0
- package/docs/button.md +21 -0
- package/docs/card.md +11 -0
- package/docs/checkbox.md +9 -0
- package/docs/context-menu.md +17 -0
- package/docs/density.md +13 -0
- package/docs/divider.md +10 -0
- package/docs/field.md +11 -0
- package/docs/focus-nav.md +18 -0
- package/docs/icon.md +13 -0
- package/docs/image.md +21 -0
- package/docs/index.md +13 -0
- package/docs/item.md +25 -0
- package/docs/modal.md +15 -0
- package/docs/nav-shell.md +18 -0
- package/docs/policy.md +21 -0
- package/docs/portal.md +15 -0
- package/docs/pressable.md +17 -0
- package/docs/progress-bar.md +10 -0
- package/docs/qrcode.md +10 -0
- package/docs/radio.md +13 -0
- package/docs/rich-text-document.md +5 -0
- package/docs/rich-text-editor.md +24 -0
- package/docs/safe-area.md +12 -0
- package/docs/scroll-view.md +14 -0
- package/docs/segmented-control.md +13 -0
- package/docs/select.md +15 -0
- package/docs/slider.md +9 -0
- package/docs/spacing.md +7 -0
- package/docs/spinner.md +10 -0
- package/docs/split-view.md +14 -0
- package/docs/switch.md +13 -0
- package/docs/text-input.md +23 -0
- package/docs/text.md +15 -0
- package/docs/theme.md +56 -0
- package/docs/tooltip.md +11 -0
- package/docs/types.md +5 -0
- package/docs/typography.md +5 -0
- package/docs/view.md +14 -0
- package/docs/window.md +15 -0
- package/package.json +6 -3
- package/src/badge.tsx +10 -8
- package/src/button.tsx +40 -28
- package/src/card.tsx +12 -10
- package/src/checkbox.tsx +21 -9
- package/src/context-menu.tsx +5 -3
- package/src/density.tsx +39 -0
- package/src/divider.tsx +3 -1
- package/src/editor-field.tsx +361 -0
- package/src/field.tsx +45 -0
- package/src/index.ts +9 -1
- package/src/item.tsx +116 -0
- package/src/nav-shell.tsx +1 -1
- package/src/policy.ts +0 -8
- package/src/press.ts +37 -8
- package/src/pressable.tsx +4 -1
- package/src/progress-bar.tsx +4 -2
- package/src/radio.tsx +14 -12
- package/src/rich-text-document.ts +247 -0
- package/src/rich-text-editor.tsx +149 -0
- package/src/segmented-control.tsx +19 -14
- package/src/select.tsx +37 -19
- package/src/slider.tsx +1 -1
- package/src/spacing.ts +1 -1
- package/src/spinner.tsx +6 -4
- package/src/switch.tsx +2 -1
- package/src/text-input.tsx +50 -262
- package/src/theme.ts +163 -76
- package/src/tooltip.tsx +7 -4
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Typography helpers
|
|
2
|
+
|
|
3
|
+
`typeStyle(variant)` resolves a theme type-scale role (`caption`/`label`/`body`/`title`/`heading`) to font props ready to spread onto a `<text>` or `d-text`: `fontSize` carries `policy.textScale`, and `fontWeight` carries the low-DPI weight compensation. Reactive when called inside a tracked scope, like any theme/policy read. `Text` applies it for you; reach for the helpers when building custom text out of core primitives.
|
|
4
|
+
|
|
5
|
+
The compensation exists because the renderer rasterizes glyphs unhinted and composites in nonlinear sRGB, which thins light-on-dark text on low-DPI displays as glyphs shrink. `typeWeight(weight, size, onDark?)` adds `policy.textWeightDelta` (0 on high-DPI displays) plus one extra step below 16px; dark-on-light text passes through untouched. `lightOnDark(text, fill)` computes the polarity for a known pair of colors (Button uses it for its fills); omitted, the theme's own palette polarity is used.
|
package/docs/view.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# View
|
|
2
|
+
|
|
3
|
+
A general-purpose box. Spreads `layout` onto the underlying view, applies the transform from `style`, and draws a background and/or border when those style props are set. Takes all pointer event props.
|
|
4
|
+
|
|
5
|
+
```jsx
|
|
6
|
+
import { View } from "@solidrt/components"
|
|
7
|
+
|
|
8
|
+
<View
|
|
9
|
+
layout={{ padding: 16, flexDirection: "column", gap: 8 }}
|
|
10
|
+
style={{ backgroundColor: "#222", borderRadius: 8 }}
|
|
11
|
+
>
|
|
12
|
+
{/* ... */}
|
|
13
|
+
</View>
|
|
14
|
+
```
|
package/docs/window.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Window
|
|
2
|
+
|
|
3
|
+
The root surface of an app: renders a core `<window>`, so `render()` accepts it. Applies `layout` and `style.backgroundColor` only (a window cannot be transformed or bordered), plus `title` and `fullscreen`.
|
|
4
|
+
|
|
5
|
+
```jsx
|
|
6
|
+
import { Window } from "@solidrt/components"
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
return (
|
|
10
|
+
<Window title="My App" style={{ backgroundColor: "#111" }}>
|
|
11
|
+
{/* ... */}
|
|
12
|
+
</Window>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
```
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"funding": "https://github.com/sponsors/wellawaretech",
|
|
5
6
|
"author": "Antoine van Wel",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"main": "src/index.ts",
|
|
8
9
|
"exports": {
|
|
9
|
-
".": "./src/index.ts"
|
|
10
|
+
".": "./src/index.ts",
|
|
11
|
+
"./theme": "./src/theme.ts"
|
|
10
12
|
},
|
|
11
13
|
"files": [
|
|
12
14
|
"src/",
|
|
15
|
+
"docs/",
|
|
13
16
|
"examples/",
|
|
14
17
|
"AGENTS.md"
|
|
15
18
|
],
|
|
@@ -18,6 +21,6 @@
|
|
|
18
21
|
},
|
|
19
22
|
"peerDependencies": {
|
|
20
23
|
"@solidjs/signals": "2.0.0-rc.0",
|
|
21
|
-
"@solidrt/core": "0.0.
|
|
24
|
+
"@solidrt/core": "0.0.51"
|
|
22
25
|
}
|
|
23
26
|
}
|
package/src/badge.tsx
CHANGED
|
@@ -35,9 +35,11 @@ export function Badge(props: BadgeProps) {
|
|
|
35
35
|
return { bg: c.primary, fg: c.onPrimary }
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
let
|
|
40
|
-
let
|
|
38
|
+
// Theme-level per-component overrides merged under the instance style.
|
|
39
|
+
let styled = () => ({ ...theme.components.badge, ...props.style })
|
|
40
|
+
let bg = () => styled().backgroundColor ?? colors().bg
|
|
41
|
+
let fg = () => styled().color ?? colors().fg
|
|
42
|
+
let radius = () => styled().borderRadius ?? RADIUS
|
|
41
43
|
// Resolved once via children(): the typeof probe and the mount sites must
|
|
42
44
|
// share one build - reading the raw getter again would orphan native nodes.
|
|
43
45
|
let resolved = children(() => props.children)
|
|
@@ -54,11 +56,11 @@ export function Badge(props: BadgeProps) {
|
|
|
54
56
|
paddingTop={2}
|
|
55
57
|
paddingBottom={2}
|
|
56
58
|
{...props.layout}
|
|
57
|
-
x={
|
|
58
|
-
y={
|
|
59
|
-
scale={
|
|
60
|
-
rotate={
|
|
61
|
-
opacity={
|
|
59
|
+
x={styled().x}
|
|
60
|
+
y={styled().y}
|
|
61
|
+
scale={styled().scale}
|
|
62
|
+
rotate={styled().rotate}
|
|
63
|
+
opacity={styled().opacity}
|
|
62
64
|
>
|
|
63
65
|
<d-rect color={bg()} radius={radius()} />
|
|
64
66
|
<Show when={isText()} fallback={resolved()}>
|
package/src/button.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { theme } from "./theme"
|
|
|
4
4
|
import { policy } from "./policy"
|
|
5
5
|
import { space } from "./spacing"
|
|
6
6
|
import { typeStyle, lightOnDark } from "./typography"
|
|
7
|
+
import { Spinner } from "./spinner"
|
|
7
8
|
import type { LayoutProps } from "@solidrt/core"
|
|
8
9
|
import type { StyleProps } from "./types"
|
|
9
10
|
|
|
@@ -22,7 +23,10 @@ export interface ButtonProps {
|
|
|
22
23
|
// stretches to the container's width (the default). Padding is the same at
|
|
23
24
|
// every size.
|
|
24
25
|
size?: ButtonSize
|
|
25
|
-
|
|
26
|
+
// A returned promise makes this an async action: the button shows a
|
|
27
|
+
// centered spinner in place of the label (geometry unchanged) and ignores
|
|
28
|
+
// presses until it settles. Non-thenable returns are ignored.
|
|
29
|
+
onPress?: () => unknown
|
|
26
30
|
disabled?: boolean
|
|
27
31
|
// Focus-navigation candidacy (spatial nav, TV remotes); on by default.
|
|
28
32
|
// Disabled buttons are never candidates.
|
|
@@ -37,44 +41,45 @@ export interface ButtonProps {
|
|
|
37
41
|
const SIZE_WIDTH: Record<ButtonSize, number> = { sm: 88, md: 120, lg: 160 }
|
|
38
42
|
|
|
39
43
|
// A themed press target: a padded, centered, accent-colored box with a label.
|
|
40
|
-
// Press feedback is a slight scale, hover feedback
|
|
41
|
-
// interaction policies only), both
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// Focus (spatial nav) draws a ring under
|
|
46
|
-
// rather than primary so it stays visible
|
|
47
|
-
// Space/remote-select activates (handled by
|
|
44
|
+
// Press feedback is a slight scale, hover feedback the theme's overlayHover
|
|
45
|
+
// tint drawn over the fill (non-touch interaction policies only), both
|
|
46
|
+
// reactive reads of the press state so no nodes are recreated. Override the
|
|
47
|
+
// box via style and the padding/sizing via layout; because hover is an
|
|
48
|
+
// overlay, it composes over a caller-set backgroundColor too. When disabled,
|
|
49
|
+
// it takes no pointer events at all. Focus (spatial nav) draws a ring under
|
|
50
|
+
// the focusRing policy, text-colored rather than primary so it stays visible
|
|
51
|
+
// on primary-filled buttons; Enter/Space/remote-select activates (handled by
|
|
52
|
+
// createPress).
|
|
48
53
|
export function Button(props: ButtonProps) {
|
|
49
|
-
// Fill
|
|
50
|
-
//
|
|
54
|
+
// Fill and label color per variant, read reactively from the theme. No
|
|
55
|
+
// variant draws a border.
|
|
51
56
|
let colors = () => {
|
|
52
57
|
let c = theme.color
|
|
53
58
|
switch (props.variant ?? "primary") {
|
|
54
59
|
case "secondary":
|
|
55
|
-
return { fill: c.secondary,
|
|
60
|
+
return { fill: c.secondary, label: c.onSecondary }
|
|
56
61
|
case "ghost":
|
|
57
|
-
return { fill: "transparent",
|
|
62
|
+
return { fill: "transparent", label: c.text }
|
|
58
63
|
case "danger":
|
|
59
|
-
return { fill: c.danger,
|
|
64
|
+
return { fill: c.danger, label: c.onPrimary }
|
|
60
65
|
default:
|
|
61
|
-
return { fill: c.primary,
|
|
66
|
+
return { fill: c.primary, label: c.onPrimary }
|
|
62
67
|
}
|
|
63
68
|
}
|
|
69
|
+
// Theme-level per-component overrides merged under the instance style.
|
|
70
|
+
let styled = (): StyleProps => ({ ...theme.components.button, ...props.style })
|
|
64
71
|
let idleFill = () =>
|
|
65
72
|
props.disabled
|
|
66
73
|
? props.variant === "ghost"
|
|
67
74
|
? "transparent"
|
|
68
75
|
: theme.color.surface
|
|
69
76
|
: colors().fill
|
|
70
|
-
let bg = (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
: colors().fill)
|
|
77
|
-
let radius = () => props.style?.borderRadius ?? theme.radius.md
|
|
77
|
+
let bg = () => styled().backgroundColor ?? idleFill()
|
|
78
|
+
// The hover feedback: the theme's overlay tint drawn over the fill, so it
|
|
79
|
+
// composes with any backgroundColor (variant, theme override, or caller).
|
|
80
|
+
let overlay = (s: PressState) =>
|
|
81
|
+
s.hovered && !props.disabled && policy.interaction !== "touch" ? theme.color.overlayHover : "transparent"
|
|
82
|
+
let radius = () => styled().borderRadius ?? theme.radius.md
|
|
78
83
|
let label = () => (props.disabled ? theme.color.textMuted : colors().label)
|
|
79
84
|
// Resolved once via children(): reading the raw children getter builds a new
|
|
80
85
|
// subtree per read, so the typeof probe and the two mount sites below must
|
|
@@ -84,19 +89,19 @@ export function Button(props: ButtonProps) {
|
|
|
84
89
|
// The label's polarity against the idle fill: onPrimary on a saturated fill
|
|
85
90
|
// is light-on-dark even in a light theme, so it needs the low-DPI weight
|
|
86
91
|
// compensation there too.
|
|
87
|
-
let labelOnDark = () => lightOnDark(label(),
|
|
92
|
+
let labelOnDark = () => lightOnDark(label(), bg())
|
|
88
93
|
|
|
89
94
|
// props (not a literal) so a swapped-in onPress is read at event time.
|
|
90
95
|
let press = createPress(props)
|
|
91
96
|
let style = () => ({
|
|
92
|
-
...
|
|
97
|
+
...styled(),
|
|
93
98
|
...(press.focused() && policy.focusRing ? { borderWidth: 2, borderColor: theme.color.text } : {}),
|
|
94
|
-
backgroundColor: bg(
|
|
99
|
+
backgroundColor: bg(),
|
|
95
100
|
borderRadius: radius(),
|
|
96
101
|
// Always a number: a scale that flips from a number back to undefined
|
|
97
102
|
// hits the transform decoder, which rejects null. Multiply so a
|
|
98
103
|
// caller-set scale is preserved under the press feedback.
|
|
99
|
-
scale: (
|
|
104
|
+
scale: (styled().scale ?? 1) * (press.pressed() && policy.motion !== "none" ? 0.97 : 1),
|
|
100
105
|
})
|
|
101
106
|
|
|
102
107
|
return (
|
|
@@ -109,6 +114,7 @@ export function Button(props: ButtonProps) {
|
|
|
109
114
|
flexDirection="row"
|
|
110
115
|
alignItems="center"
|
|
111
116
|
justifyContent="center"
|
|
117
|
+
position="relative"
|
|
112
118
|
paddingTop={space("md")}
|
|
113
119
|
paddingBottom={space("md")}
|
|
114
120
|
paddingLeft={space("lg")}
|
|
@@ -125,11 +131,17 @@ export function Button(props: ButtonProps) {
|
|
|
125
131
|
pointerEvents={props.disabled ? "none" : undefined}
|
|
126
132
|
>
|
|
127
133
|
<d-rect color={style().backgroundColor ?? "transparent"} radius={style().borderRadius} />
|
|
134
|
+
<d-rect color={overlay(press.state())} radius={style().borderRadius} />
|
|
128
135
|
<Show when={isText()} fallback={resolved()}>
|
|
129
|
-
<text color={label()} {...typeStyle("body", labelOnDark())}>
|
|
136
|
+
<text color={press.pending() ? "transparent" : label()} {...typeStyle("body", labelOnDark())}>
|
|
130
137
|
{resolved()}
|
|
131
138
|
</text>
|
|
132
139
|
</Show>
|
|
140
|
+
<Show when={press.pending()}>
|
|
141
|
+
<view position="absolute" top={0} bottom={0} left={0} right={0} alignItems="center" justifyContent="center">
|
|
142
|
+
<Spinner size={16} thickness={2} style={{ color: label() }} />
|
|
143
|
+
</view>
|
|
144
|
+
</Show>
|
|
133
145
|
<Show when={(style().borderWidth ?? 0) > 0}>
|
|
134
146
|
<d-rect
|
|
135
147
|
drawStyle="stroke"
|
package/src/card.tsx
CHANGED
|
@@ -19,9 +19,11 @@ export interface CardProps {
|
|
|
19
19
|
// style.borderWidth or style.borderColor to draw an outline. Override any paint
|
|
20
20
|
// via style, spacing/sizing via layout.
|
|
21
21
|
export function Card(props: CardProps) {
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
let
|
|
22
|
+
// Theme-level per-component overrides merged under the instance style.
|
|
23
|
+
let styled = () => ({ ...theme.components.card, ...props.style })
|
|
24
|
+
let bg = () => styled().backgroundColor ?? theme.color.surface
|
|
25
|
+
let radius = () => styled().borderRadius ?? theme.radius.lg
|
|
26
|
+
let hasBorder = () => styled().borderWidth != null || styled().borderColor != null
|
|
25
27
|
|
|
26
28
|
return (
|
|
27
29
|
<view
|
|
@@ -31,11 +33,11 @@ export function Card(props: CardProps) {
|
|
|
31
33
|
gap={space("lg")}
|
|
32
34
|
padding={space("xl")}
|
|
33
35
|
{...props.layout}
|
|
34
|
-
x={
|
|
35
|
-
y={
|
|
36
|
-
scale={
|
|
37
|
-
rotate={
|
|
38
|
-
opacity={
|
|
36
|
+
x={styled().x}
|
|
37
|
+
y={styled().y}
|
|
38
|
+
scale={styled().scale}
|
|
39
|
+
rotate={styled().rotate}
|
|
40
|
+
opacity={styled().opacity}
|
|
39
41
|
>
|
|
40
42
|
<d-rect color={bg()} radius={radius()} />
|
|
41
43
|
<Show when={props.title != null}>
|
|
@@ -47,8 +49,8 @@ export function Card(props: CardProps) {
|
|
|
47
49
|
<Show when={hasBorder()}>
|
|
48
50
|
<d-rect
|
|
49
51
|
drawStyle="stroke"
|
|
50
|
-
color={
|
|
51
|
-
strokeWidth={
|
|
52
|
+
color={styled().borderColor ?? theme.color.border}
|
|
53
|
+
strokeWidth={styled().borderWidth ?? theme.borderWidth.sm}
|
|
52
54
|
radius={radius()}
|
|
53
55
|
/>
|
|
54
56
|
</Show>
|
package/src/checkbox.tsx
CHANGED
|
@@ -2,7 +2,8 @@ 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 { densityScale } from "./
|
|
5
|
+
import { densityScale } from "./density"
|
|
6
|
+
import { Icon } from "./icon"
|
|
6
7
|
import type { StyleProps } from "./types"
|
|
7
8
|
|
|
8
9
|
export interface CheckboxProps {
|
|
@@ -43,6 +44,7 @@ export function Checkbox(props: CheckboxProps) {
|
|
|
43
44
|
borderColor: theme.color.border,
|
|
44
45
|
borderWidth: theme.borderWidth.sm,
|
|
45
46
|
borderRadius: theme.radius.sm,
|
|
47
|
+
...theme.components.checkbox,
|
|
46
48
|
...props.style,
|
|
47
49
|
})
|
|
48
50
|
|
|
@@ -52,6 +54,7 @@ export function Checkbox(props: CheckboxProps) {
|
|
|
52
54
|
repaintBoundary
|
|
53
55
|
width={size()}
|
|
54
56
|
height={size()}
|
|
57
|
+
position="relative"
|
|
55
58
|
{...props.layout}
|
|
56
59
|
x={style().x}
|
|
57
60
|
y={style().y}
|
|
@@ -63,14 +66,23 @@ export function Checkbox(props: CheckboxProps) {
|
|
|
63
66
|
>
|
|
64
67
|
<d-rect color={style().backgroundColor ?? "transparent"} radius={style().borderRadius} />
|
|
65
68
|
<Show when={checked()}>
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
<Show
|
|
70
|
+
when={theme.icons.check}
|
|
71
|
+
fallback={
|
|
72
|
+
<d-path
|
|
73
|
+
d={check()}
|
|
74
|
+
drawStyle="stroke"
|
|
75
|
+
color={theme.color.onPrimary}
|
|
76
|
+
strokeWidth={2}
|
|
77
|
+
strokeCap="round"
|
|
78
|
+
strokeJoin="round"
|
|
79
|
+
/>
|
|
80
|
+
}
|
|
81
|
+
>
|
|
82
|
+
<view position="absolute" top={0} bottom={0} left={0} right={0} alignItems="center" justifyContent="center">
|
|
83
|
+
<Icon src={theme.icons.check!} size={Math.round(size() * 0.75)} color={theme.color.onPrimary} />
|
|
84
|
+
</view>
|
|
85
|
+
</Show>
|
|
74
86
|
</Show>
|
|
75
87
|
<Show when={(style().borderWidth ?? 0) > 0}>
|
|
76
88
|
<d-rect
|
package/src/context-menu.tsx
CHANGED
|
@@ -91,9 +91,11 @@ export function ContextMenu(props: ContextMenuProps) {
|
|
|
91
91
|
>
|
|
92
92
|
<d-rect
|
|
93
93
|
color={
|
|
94
|
-
press.pressed()
|
|
95
|
-
? theme.color.
|
|
96
|
-
: "
|
|
94
|
+
press.pressed()
|
|
95
|
+
? theme.color.overlayPressed
|
|
96
|
+
: press.hovered() && policy.interaction !== "touch"
|
|
97
|
+
? theme.color.overlayHover
|
|
98
|
+
: "transparent"
|
|
97
99
|
}
|
|
98
100
|
/>
|
|
99
101
|
<text {...bodyText(p.item.disabled ? theme.color.textMuted : theme.color.text)}>{p.item.label}</text>
|
package/src/density.tsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createContext, useContext } from "@solidrt/core"
|
|
2
|
+
import { policy, type DensityPolicy } from "./policy"
|
|
3
|
+
|
|
4
|
+
// Subtree density: a region (a toolbar, a table, a sidebar) tightens its
|
|
5
|
+
// controls wholesale instead of per-child props. The context holds an
|
|
6
|
+
// accessor so a reactive `value` stays live for consumers.
|
|
7
|
+
let DensityContext = createContext<() => DensityPolicy | undefined>(() => undefined)
|
|
8
|
+
|
|
9
|
+
export interface DensityProps {
|
|
10
|
+
value: DensityPolicy
|
|
11
|
+
children?: any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Overrides the density policy for its subtree: every density-scaled metric
|
|
16
|
+
* below - `space()`, control sizes, `Item` row paddings - resolves this value
|
|
17
|
+
* instead of the global `policy.density`. Nests; the nearest wins.
|
|
18
|
+
*
|
|
19
|
+
* <Density value="compact">
|
|
20
|
+
* <Item label="..." /> // compact rows, no per-item props
|
|
21
|
+
* </Density>
|
|
22
|
+
*/
|
|
23
|
+
export function Density(props: DensityProps) {
|
|
24
|
+
return <DensityContext value={() => props.value}>{props.children}</DensityContext>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// How density maps to component metrics: a multiplier on control sizes,
|
|
28
|
+
// paddings, and hit targets. Comfortable is the components' designed size.
|
|
29
|
+
const DENSITY_SCALE: Record<DensityPolicy, number> = { comfortable: 1, compact: 0.85, dense: 0.7 }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Reactive density multiplier for control metrics: the nearest <Density>
|
|
33
|
+
* above the calling scope, falling back to the global `policy.density`.
|
|
34
|
+
* Call it during component setup or inside JSX/thunks (an owner is needed to
|
|
35
|
+
* see a <Density> region; without one it reads the global policy).
|
|
36
|
+
*/
|
|
37
|
+
export function densityScale(): number {
|
|
38
|
+
return DENSITY_SCALE[useContext(DensityContext)() ?? policy.density]
|
|
39
|
+
}
|
package/src/divider.tsx
CHANGED
|
@@ -18,7 +18,9 @@ export interface DividerProps {
|
|
|
18
18
|
export function Divider(props: DividerProps) {
|
|
19
19
|
let vertical = () => props.orientation === "vertical"
|
|
20
20
|
let thickness = () => props.thickness ?? 1
|
|
21
|
-
|
|
21
|
+
// Theme-level per-component overrides merged under the instance style.
|
|
22
|
+
let styled = () => ({ ...theme.components.divider, ...props.style })
|
|
23
|
+
let color = () => styled().backgroundColor ?? theme.color.border
|
|
22
24
|
|
|
23
25
|
return (
|
|
24
26
|
<view
|