@solidrt/components 0.0.18 → 0.0.20
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 +40 -2
- package/README.md +267 -1
- package/package.json +5 -2
- package/src/badge.tsx +50 -0
- package/src/button.tsx +19 -10
- package/src/card.tsx +48 -0
- package/src/checkbox.tsx +65 -0
- package/src/context-menu.tsx +189 -0
- package/src/divider.tsx +33 -0
- package/src/icon.tsx +35 -0
- package/src/index.ts +31 -1
- package/src/nav-shell.tsx +160 -0
- package/src/policy.ts +108 -0
- package/src/progress-bar.tsx +81 -0
- package/src/qrcode.tsx +78 -0
- package/src/radio.tsx +100 -0
- package/src/scroll-view.tsx +5 -1
- package/src/select.tsx +203 -0
- package/src/slider.tsx +116 -0
- package/src/spinner.tsx +74 -0
- package/src/split-view.tsx +54 -0
- package/src/switch.tsx +58 -0
- package/src/text-input.tsx +39 -35
- package/src/theme.ts +72 -17
- package/src/tooltip.tsx +112 -0
- package/src/types.ts +4 -4
package/src/switch.tsx
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createSignal } from "@solidjs/signals"
|
|
2
|
+
import type { LayoutProps } from "@solidrt/core"
|
|
3
|
+
import { Pressable } from "./pressable"
|
|
4
|
+
import { theme } from "./theme"
|
|
5
|
+
import { densityScale } from "./policy"
|
|
6
|
+
import type { StyleProps } from "./types"
|
|
7
|
+
|
|
8
|
+
export interface SwitchProps {
|
|
9
|
+
// Controlled on/off. If omitted, the switch is uncontrolled.
|
|
10
|
+
value?: boolean
|
|
11
|
+
// Initial value for uncontrolled use.
|
|
12
|
+
defaultValue?: boolean
|
|
13
|
+
onChange?: (value: boolean) => void
|
|
14
|
+
disabled?: boolean
|
|
15
|
+
layout?: LayoutProps
|
|
16
|
+
style?: StyleProps
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Designed (comfortable-density) metrics; w/h below scale them by the density
|
|
20
|
+
// policy.
|
|
21
|
+
const W = 44
|
|
22
|
+
const H = 24
|
|
23
|
+
const PAD = 2
|
|
24
|
+
|
|
25
|
+
// A toggle. Track fills with primary when on, surfaceAlt when off; the thumb
|
|
26
|
+
// slides across. Controlled via value/onChange, or uncontrolled via
|
|
27
|
+
// defaultValue. Built on Pressable, so disabled takes no pointer events.
|
|
28
|
+
export function Switch(props: SwitchProps) {
|
|
29
|
+
let [internal, setInternal] = createSignal(props.defaultValue ?? false)
|
|
30
|
+
let on = () => (props.value !== undefined ? props.value : internal())
|
|
31
|
+
|
|
32
|
+
let toggle = () => {
|
|
33
|
+
let next = !on()
|
|
34
|
+
if (props.value === undefined) setInternal(next)
|
|
35
|
+
props.onChange?.(next)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let w = () => Math.round(W * densityScale())
|
|
39
|
+
let h = () => Math.round(H * densityScale())
|
|
40
|
+
let thumb = () => h() - PAD * 2
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Pressable
|
|
44
|
+
onPress={toggle}
|
|
45
|
+
disabled={props.disabled}
|
|
46
|
+
layout={{ width: w(), height: h(), ...props.layout }}
|
|
47
|
+
style={{
|
|
48
|
+
backgroundColor: on() ? theme.color.primary : theme.color.surfaceAlt,
|
|
49
|
+
borderRadius: h() / 2,
|
|
50
|
+
...props.style,
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<view position="absolute" top={PAD} left={PAD} x={on() ? w() - thumb() - PAD * 2 : 0}>
|
|
54
|
+
<d-oval w={thumb()} h={thumb()} color={theme.color.onPrimary} />
|
|
55
|
+
</view>
|
|
56
|
+
</Pressable>
|
|
57
|
+
)
|
|
58
|
+
}
|
package/src/text-input.tsx
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { createEffect, createSignal, onCleanup } from "@solidjs/signals"
|
|
2
|
-
import { setFocus } from "@solidrt/core"
|
|
2
|
+
import { measureText, setFocus } from "@solidrt/core"
|
|
3
3
|
import { createCaretScroll, createTextBuffer } from "@solidrt/core/text-input"
|
|
4
4
|
import type { LayoutProps } from "@solidrt/core"
|
|
5
5
|
import type { StyleProps } from "./types"
|
|
6
6
|
import { theme } from "./theme"
|
|
7
|
+
import { policy, densityScale } from "./policy"
|
|
7
8
|
|
|
8
9
|
// Caret thickness. Shared so the drawn caret and the scroll offset's reserved
|
|
9
10
|
// edge column cannot drift apart.
|
|
10
11
|
const CARET_WIDTH = 1
|
|
11
12
|
|
|
13
|
+
// Shaping width handed to the detached value/placeholder text: effectively
|
|
14
|
+
// unbounded, so a single line never wraps. The viewport clips it and scrollX
|
|
15
|
+
// slides it.
|
|
16
|
+
const TEXT_SHAPE_WIDTH = 1e9
|
|
17
|
+
|
|
12
18
|
export interface TextInputProps {
|
|
13
19
|
value?: string
|
|
14
20
|
defaultValue?: string
|
|
@@ -120,28 +126,29 @@ export function TextInput(props: TextInputProps) {
|
|
|
120
126
|
if (blinkId != null) clearInterval(blinkId)
|
|
121
127
|
})
|
|
122
128
|
|
|
123
|
-
// Style overrides fall back to theme defaults.
|
|
129
|
+
// Style overrides fall back to theme defaults. The border doubles as the
|
|
130
|
+
// focus ring: primary while focused, when the focus-ring policy asks for a
|
|
131
|
+
// visible indicator.
|
|
124
132
|
let textColor = () => props.style?.color ?? theme.color.text
|
|
125
133
|
let surfaceColor = () => props.style?.backgroundColor ?? theme.color.surface
|
|
126
|
-
let borderColor = () =>
|
|
134
|
+
let borderColor = () =>
|
|
135
|
+
props.style?.borderColor ?? (focused() && policy.focusRing ? theme.color.primary : theme.color.border)
|
|
127
136
|
let borderWidth = () => props.style?.borderWidth ?? theme.borderWidth.sm
|
|
128
137
|
let borderRadius = () => props.style?.borderRadius ?? theme.radius.sm
|
|
129
138
|
|
|
130
139
|
let showPlaceholder = () => !focused() && value().length === 0 && (props.placeholder ?? "").length > 0
|
|
131
140
|
let showCaret = () => focused() && caretOn() && !showPlaceholder()
|
|
132
141
|
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
let beforeCaret = () => value().slice(0, buffer.caret())
|
|
144
|
-
let afterCaret = () => value().slice(buffer.caret())
|
|
142
|
+
// Everything inside the viewport is detached: the value is one d-text shaped
|
|
143
|
+
// at an unbounded width and the caret a d-rect at the measured before-caret
|
|
144
|
+
// width, so typing, caret movement, blink and scroll never touch layout. The
|
|
145
|
+
// viewport carries an explicit height (detached content takes no layout
|
|
146
|
+
// slot) equal to the one-line paragraph height, which keeps the text where
|
|
147
|
+
// the old centered attached row sat. createCaretScroll keeps the caret in
|
|
148
|
+
// view and flushes the offset before paint; scrollX is a paint-time
|
|
149
|
+
// translate that also applies to detached children.
|
|
150
|
+
let rowHeight = () => Math.round(theme.text.body.size * theme.text.body.lineHeight)
|
|
151
|
+
let caretX = () => measureText(value().slice(0, buffer.caret()), { fontSize: theme.text.body.size }).width
|
|
145
152
|
let scrollX = createCaretScroll(
|
|
146
153
|
() => viewport,
|
|
147
154
|
() => ({
|
|
@@ -156,11 +163,11 @@ export function TextInput(props: TextInputProps) {
|
|
|
156
163
|
)
|
|
157
164
|
|
|
158
165
|
let textStyle = (color: string) => ({
|
|
166
|
+
w: TEXT_SHAPE_WIDTH,
|
|
159
167
|
fontSize: theme.text.body.size,
|
|
160
168
|
lineHeight: theme.text.body.lineHeight,
|
|
161
169
|
color,
|
|
162
170
|
maxLines: 1,
|
|
163
|
-
flexShrink: 0,
|
|
164
171
|
})
|
|
165
172
|
|
|
166
173
|
return (
|
|
@@ -168,10 +175,10 @@ export function TextInput(props: TextInputProps) {
|
|
|
168
175
|
ref={(n: { id: number }) => (node = n)}
|
|
169
176
|
flexDirection="row"
|
|
170
177
|
alignItems="center"
|
|
171
|
-
paddingLeft={theme.spacing.md}
|
|
172
|
-
paddingRight={theme.spacing.md}
|
|
173
|
-
paddingTop={theme.spacing.sm}
|
|
174
|
-
paddingBottom={theme.spacing.sm}
|
|
178
|
+
paddingLeft={Math.round(theme.spacing.md * densityScale())}
|
|
179
|
+
paddingRight={Math.round(theme.spacing.md * densityScale())}
|
|
180
|
+
paddingTop={Math.round(theme.spacing.sm * densityScale())}
|
|
181
|
+
paddingBottom={Math.round(theme.spacing.sm * densityScale())}
|
|
175
182
|
{...props.layout}
|
|
176
183
|
x={props.style?.x}
|
|
177
184
|
y={props.style?.y}
|
|
@@ -193,28 +200,25 @@ export function TextInput(props: TextInputProps) {
|
|
|
193
200
|
<view
|
|
194
201
|
ref={(n: { id: number }) => (viewport = n)}
|
|
195
202
|
flex={1}
|
|
196
|
-
|
|
197
|
-
alignItems="center"
|
|
203
|
+
height={rowHeight()}
|
|
198
204
|
overflow="hidden"
|
|
199
205
|
scrollX={scrollX()}
|
|
200
206
|
>
|
|
201
207
|
{showPlaceholder() ? (
|
|
202
|
-
<text {...textStyle(theme.color.textMuted)}>{props.placeholder ?? ""}</text>
|
|
208
|
+
<d-text {...textStyle(theme.color.textMuted)}>{props.placeholder ?? ""}</d-text>
|
|
203
209
|
) : (
|
|
204
|
-
|
|
205
|
-
<text {...textStyle(textColor())}>{
|
|
210
|
+
<>
|
|
211
|
+
<d-text {...textStyle(textColor())}>{value()}</d-text>
|
|
206
212
|
{showCaret() ? (
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
</view>
|
|
213
|
+
<d-rect
|
|
214
|
+
color={textColor()}
|
|
215
|
+
x={caretX()}
|
|
216
|
+
y={(rowHeight() - theme.text.body.size) / 2}
|
|
217
|
+
w={CARET_WIDTH}
|
|
218
|
+
h={theme.text.body.size}
|
|
219
|
+
/>
|
|
215
220
|
) : null}
|
|
216
|
-
|
|
217
|
-
</view>
|
|
221
|
+
</>
|
|
218
222
|
)}
|
|
219
223
|
</view>
|
|
220
224
|
</view>
|
package/src/theme.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createStore } from "@solidrt/core"
|
|
2
|
+
|
|
1
3
|
export type TextStyle = {
|
|
2
4
|
size: number
|
|
3
5
|
lineHeight: number
|
|
@@ -6,12 +8,24 @@ export type TextStyle = {
|
|
|
6
8
|
export type Theme = {
|
|
7
9
|
text: { body: TextStyle }
|
|
8
10
|
color: {
|
|
11
|
+
// Window fill.
|
|
12
|
+
background: string
|
|
13
|
+
// Control/card fill.
|
|
14
|
+
surface: string
|
|
15
|
+
// Subtle raised/track fill (switch off-state, slider track, ...).
|
|
16
|
+
surfaceAlt: string
|
|
17
|
+
// Hover tint for surface-colored controls (non-touch interaction policies).
|
|
18
|
+
surfaceHover: string
|
|
9
19
|
text: string
|
|
10
20
|
textMuted: string
|
|
11
|
-
surface: string
|
|
12
21
|
border: string
|
|
13
22
|
primary: string
|
|
23
|
+
// Hover tint for primary-colored controls.
|
|
24
|
+
primaryHover: string
|
|
14
25
|
onPrimary: string
|
|
26
|
+
// Validation / destructive.
|
|
27
|
+
danger: string
|
|
28
|
+
// Overlay dim behind modals.
|
|
15
29
|
scrim: string
|
|
16
30
|
}
|
|
17
31
|
spacing: { sm: number; md: number }
|
|
@@ -19,29 +33,70 @@ export type Theme = {
|
|
|
19
33
|
borderWidth: { sm: number }
|
|
20
34
|
}
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
// Scheme-independent tokens, shared by both presets.
|
|
37
|
+
const TEXT = { body: { size: 14, lineHeight: 1.5 } }
|
|
38
|
+
const SPACING = { sm: 4, md: 8 }
|
|
39
|
+
const RADIUS = { sm: 4 }
|
|
40
|
+
const BORDER_WIDTH = { sm: 1 }
|
|
41
|
+
|
|
42
|
+
export let darkTheme: Theme = {
|
|
43
|
+
text: TEXT,
|
|
26
44
|
color: {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
background: "#0b0f17",
|
|
46
|
+
surface: "#161b22",
|
|
47
|
+
surfaceAlt: "#21262d",
|
|
48
|
+
surfaceHover: "#262c34",
|
|
49
|
+
text: "#e6edf3",
|
|
50
|
+
textMuted: "rgba(230,237,243,0.5)",
|
|
51
|
+
border: "rgba(255,255,255,0.14)",
|
|
31
52
|
primary: "#1f6feb",
|
|
53
|
+
primaryHover: "#388bfd",
|
|
32
54
|
onPrimary: "#ffffff",
|
|
55
|
+
danger: "#f85149",
|
|
33
56
|
scrim: "rgba(0,0,0,0.6)",
|
|
34
57
|
},
|
|
35
|
-
spacing:
|
|
36
|
-
radius:
|
|
37
|
-
borderWidth:
|
|
58
|
+
spacing: SPACING,
|
|
59
|
+
radius: RADIUS,
|
|
60
|
+
borderWidth: BORDER_WIDTH,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export let lightTheme: Theme = {
|
|
64
|
+
text: TEXT,
|
|
65
|
+
color: {
|
|
66
|
+
background: "#ffffff",
|
|
67
|
+
surface: "#f6f8fa",
|
|
68
|
+
surfaceAlt: "#eaeef2",
|
|
69
|
+
surfaceHover: "#e0e5eb",
|
|
70
|
+
text: "#1f2328",
|
|
71
|
+
textMuted: "rgba(31,35,40,0.5)",
|
|
72
|
+
border: "rgba(0,0,0,0.15)",
|
|
73
|
+
primary: "#1f6feb",
|
|
74
|
+
primaryHover: "#1a5fd0",
|
|
75
|
+
onPrimary: "#ffffff",
|
|
76
|
+
danger: "#cf222e",
|
|
77
|
+
scrim: "rgba(0,0,0,0.4)",
|
|
78
|
+
},
|
|
79
|
+
spacing: SPACING,
|
|
80
|
+
radius: RADIUS,
|
|
81
|
+
borderWidth: BORDER_WIDTH,
|
|
38
82
|
}
|
|
39
83
|
|
|
84
|
+
// Backed by a Solid store so reads are tracked: calling setTheme at runtime
|
|
85
|
+
// recolors the live UI without remounting. Components read theme.* through
|
|
86
|
+
// thunks/JSX expressions, so they pick this up with no call-site changes.
|
|
87
|
+
let [theme, setThemeStore] = createStore<Theme>({ ...darkTheme })
|
|
88
|
+
export { theme }
|
|
89
|
+
|
|
40
90
|
type ThemePartial = { [K in keyof Theme]?: Partial<Theme[K]> }
|
|
41
91
|
|
|
92
|
+
// Switch themes with a full preset (setTheme(lightTheme)) or apply a targeted
|
|
93
|
+
// override (setTheme({ color: { primary: "#f00" } })). Merges one level deep per
|
|
94
|
+
// category, matching the previous Object.assign behavior.
|
|
42
95
|
export function setTheme(partial: ThemePartial) {
|
|
43
|
-
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
96
|
+
setThemeStore((s) => {
|
|
97
|
+
for (let key in partial) {
|
|
98
|
+
let k = key as keyof Theme
|
|
99
|
+
Object.assign(s[k], (partial as any)[k])
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|
package/src/tooltip.tsx
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { createSignal, onCleanup, createPortal, onLayout, getBoundingBox, Show, env } from "@solidrt/core"
|
|
2
|
+
import type { LayoutProps, PointerEvent } from "@solidrt/core"
|
|
3
|
+
import { theme } from "./theme"
|
|
4
|
+
import { policy, densityScale } from "./policy"
|
|
5
|
+
|
|
6
|
+
export interface TooltipProps {
|
|
7
|
+
// The tooltip body. A string/number renders as themed text; anything else
|
|
8
|
+
// renders as-is.
|
|
9
|
+
content?: any
|
|
10
|
+
// The anchor content the tooltip attaches to.
|
|
11
|
+
children?: any
|
|
12
|
+
// Hover delay in milliseconds before showing.
|
|
13
|
+
delay?: number
|
|
14
|
+
// Which side of the anchor the bubble appears on.
|
|
15
|
+
placement?: "top" | "bottom"
|
|
16
|
+
layout?: LayoutProps
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DELAY = 500
|
|
20
|
+
const GAP = 6
|
|
21
|
+
// Minimum distance kept between the bubble and the window edges.
|
|
22
|
+
const MARGIN = 4
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A hover-only affordance: under desktop/hybrid interaction policies, resting a
|
|
26
|
+
* mouse pointer on the wrapped content shows a bubble near it after a short
|
|
27
|
+
* delay. Under the touch policy it never shows, so tooltip content must stay
|
|
28
|
+
* non-essential. The bubble is portal-mounted at the window root and takes no
|
|
29
|
+
* pointer events; it hides on leave and on press.
|
|
30
|
+
*/
|
|
31
|
+
export function Tooltip(props: TooltipProps) {
|
|
32
|
+
let anchor: { id: number } | undefined
|
|
33
|
+
let [open, setOpen] = createSignal(false)
|
|
34
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
35
|
+
|
|
36
|
+
let enter = (e: PointerEvent) => {
|
|
37
|
+
// The policy gates the behavior; the pointerType check keeps a finger from
|
|
38
|
+
// arming the tooltip in a hybrid session.
|
|
39
|
+
if (policy.interaction === "touch" || e.pointerType !== "mouse") return
|
|
40
|
+
clearTimeout(timer)
|
|
41
|
+
timer = setTimeout(() => setOpen(true), props.delay ?? DELAY)
|
|
42
|
+
}
|
|
43
|
+
let hide = () => {
|
|
44
|
+
clearTimeout(timer)
|
|
45
|
+
setOpen(false)
|
|
46
|
+
}
|
|
47
|
+
onCleanup(() => clearTimeout(timer))
|
|
48
|
+
|
|
49
|
+
// The bubble sits at the window root's origin and is placed with the x/y
|
|
50
|
+
// paint transforms, so repositioning never reflows. Measured after its first
|
|
51
|
+
// layout (parked offscreen until then), then pinned to the anchor's current
|
|
52
|
+
// box; recomputed each layout so it stays attached when the anchor moves
|
|
53
|
+
// (scrolling, resizes).
|
|
54
|
+
let Bubble = () => {
|
|
55
|
+
let bubble: { id: number } | undefined
|
|
56
|
+
let [pos, setPos] = createSignal<{ x: number; y: number } | null>(null)
|
|
57
|
+
onLayout(() => {
|
|
58
|
+
let a = anchor && getBoundingBox(anchor)
|
|
59
|
+
let b = bubble && getBoundingBox(bubble)
|
|
60
|
+
if (!a || !b) return
|
|
61
|
+
let x = a.x + a.width / 2 - b.width / 2
|
|
62
|
+
x = Math.round(Math.min(Math.max(x, MARGIN), env.windowSize.width - b.width - MARGIN))
|
|
63
|
+
let y = Math.round(props.placement === "bottom" ? a.y + a.height + GAP : a.y - b.height - GAP)
|
|
64
|
+
let cur = pos()
|
|
65
|
+
if (!cur || cur.x !== x || cur.y !== y) setPos({ x, y })
|
|
66
|
+
})
|
|
67
|
+
let isText = () => typeof props.content === "string" || typeof props.content === "number"
|
|
68
|
+
return createPortal(
|
|
69
|
+
<view
|
|
70
|
+
ref={(n: { id: number }) => (bubble = n)}
|
|
71
|
+
position="absolute"
|
|
72
|
+
top={0}
|
|
73
|
+
left={0}
|
|
74
|
+
x={pos()?.x ?? -10000}
|
|
75
|
+
y={pos()?.y ?? 0}
|
|
76
|
+
paddingTop={Math.round(theme.spacing.sm * densityScale())}
|
|
77
|
+
paddingBottom={Math.round(theme.spacing.sm * densityScale())}
|
|
78
|
+
paddingLeft={Math.round(theme.spacing.md * densityScale())}
|
|
79
|
+
paddingRight={Math.round(theme.spacing.md * densityScale())}
|
|
80
|
+
pointerEvents="none"
|
|
81
|
+
>
|
|
82
|
+
<d-rect color={theme.color.surfaceAlt} radius={theme.radius.sm} />
|
|
83
|
+
<Show when={isText()} fallback={props.content}>
|
|
84
|
+
<text color={theme.color.text} fontSize={theme.text.body.size} lineHeight={theme.text.body.lineHeight}>
|
|
85
|
+
{props.content}
|
|
86
|
+
</text>
|
|
87
|
+
</Show>
|
|
88
|
+
<d-rect
|
|
89
|
+
drawStyle="stroke"
|
|
90
|
+
color={theme.color.border}
|
|
91
|
+
strokeWidth={theme.borderWidth.sm}
|
|
92
|
+
radius={theme.radius.sm}
|
|
93
|
+
/>
|
|
94
|
+
</view>,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<view
|
|
100
|
+
ref={(n: { id: number }) => (anchor = n)}
|
|
101
|
+
onPointerEnter={enter}
|
|
102
|
+
onPointerLeave={hide}
|
|
103
|
+
onPointerDown={hide}
|
|
104
|
+
{...props.layout}
|
|
105
|
+
>
|
|
106
|
+
{props.children}
|
|
107
|
+
<Show when={open()}>
|
|
108
|
+
<Bubble />
|
|
109
|
+
</Show>
|
|
110
|
+
</view>
|
|
111
|
+
)
|
|
112
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Color, LayoutProps } from "@solidrt/core"
|
|
1
|
+
import type { Color, Gradient, LayoutProps } from "@solidrt/core"
|
|
2
2
|
|
|
3
3
|
// The split between layout and style follows one rule: layout properties feed
|
|
4
4
|
// into Taffy and changing them triggers a relayout; style properties are
|
|
@@ -8,9 +8,9 @@ import type { Color, LayoutProps } from "@solidrt/core"
|
|
|
8
8
|
// drawn as a stroke overlay (not part of the box model), and the transform is
|
|
9
9
|
// applied at paint time, so both live here.
|
|
10
10
|
export interface StyleProps {
|
|
11
|
-
color?: Color
|
|
12
|
-
backgroundColor?: Color
|
|
13
|
-
borderColor?: Color
|
|
11
|
+
color?: Color | Gradient
|
|
12
|
+
backgroundColor?: Color | Gradient
|
|
13
|
+
borderColor?: Color | Gradient
|
|
14
14
|
borderWidth?: number
|
|
15
15
|
borderRadius?: number | [number, number, number, number]
|
|
16
16
|
x?: number
|