@solidrt/components 0.0.50 → 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 +93 -85
- package/README.md +421 -311
- 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/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 +35 -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 +68 -0
- package/docs/tooltip.md +11 -0
- package/docs/types.md +9 -0
- package/docs/typography.md +5 -0
- package/docs/view.md +14 -0
- package/docs/window.md +15 -0
- package/package.json +8 -4
- package/src/badge.tsx +30 -21
- package/src/button.tsx +50 -32
- package/src/card.tsx +22 -13
- package/src/checkbox.tsx +29 -11
- package/src/context-menu.tsx +14 -9
- package/src/density.tsx +39 -0
- package/src/divider.tsx +11 -4
- package/src/editor-field.tsx +368 -0
- package/src/field.tsx +47 -0
- package/src/icon.tsx +8 -4
- package/src/image.tsx +10 -3
- package/src/index.ts +19 -2
- package/src/item.tsx +121 -0
- package/src/nav-shell.tsx +7 -17
- package/src/policy.ts +9 -12
- package/src/press.ts +37 -8
- package/src/pressable.tsx +15 -3
- package/src/progress-bar.tsx +8 -5
- package/src/qrcode.tsx +7 -5
- package/src/radio.tsx +26 -16
- package/src/rich-text-document.ts +247 -0
- package/src/rich-text-editor.tsx +151 -0
- package/src/scroll-view.tsx +76 -7
- package/src/segmented-control.tsx +33 -19
- package/src/select.tsx +59 -30
- package/src/slider.tsx +32 -6
- package/src/spacing.ts +1 -1
- package/src/spinner.tsx +11 -6
- package/src/split-view.tsx +3 -4
- package/src/switch.tsx +10 -3
- package/src/text-input.tsx +54 -264
- package/src/text.tsx +22 -2
- package/src/theme.ts +220 -81
- package/src/tooltip.tsx +23 -9
- package/src/types.ts +119 -1
- package/src/view.tsx +11 -2
package/src/text-input.tsx
CHANGED
|
@@ -1,32 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
focusedNode,
|
|
7
|
-
measureText,
|
|
8
|
-
setFocus,
|
|
9
|
-
startTextInput,
|
|
10
|
-
textInputActive,
|
|
11
|
-
} from "@solidrt/core"
|
|
12
|
-
import { createCaretScroll, createTextBuffer } from "@solidrt/core/text-input"
|
|
13
|
-
import type { Color, Gradient, KeyEvent, LayoutProps, TextInputHints } from "@solidrt/core"
|
|
14
|
-
import { registerNavAction } from "./focus-nav"
|
|
15
|
-
import type { StyleProps } from "./types"
|
|
1
|
+
import { untrack } from "@solidrt/core"
|
|
2
|
+
import { createTextBuffer } from "@solidrt/core/text-input"
|
|
3
|
+
import type { LayoutProps, TextInputHints } from "@solidrt/core"
|
|
4
|
+
import { EditorField } from "./editor-field"
|
|
5
|
+
import type { StyleProps, TransitionProps } from "./types"
|
|
16
6
|
import { theme } from "./theme"
|
|
17
|
-
import { policy } from "./policy"
|
|
18
|
-
import { space } from "./spacing"
|
|
19
7
|
|
|
20
|
-
|
|
21
|
-
// edge column cannot drift apart.
|
|
22
|
-
const CARET_WIDTH = 1
|
|
23
|
-
|
|
24
|
-
// Shaping width handed to the detached value/placeholder text: effectively
|
|
25
|
-
// unbounded, so a single line never wraps. The viewport clips it and scrollX
|
|
26
|
-
// slides it.
|
|
27
|
-
const TEXT_SHAPE_WIDTH = 1e9
|
|
28
|
-
|
|
29
|
-
export interface TextInputProps {
|
|
8
|
+
export interface TextInputProps extends TransitionProps {
|
|
30
9
|
value?: string
|
|
31
10
|
defaultValue?: string
|
|
32
11
|
onInput?: (value: string) => void
|
|
@@ -38,6 +17,15 @@ export interface TextInputProps {
|
|
|
38
17
|
maxLength?: number
|
|
39
18
|
disabled?: boolean
|
|
40
19
|
autoFocus?: boolean
|
|
20
|
+
/**
|
|
21
|
+
* Multi-line editing: lines wrap at the field's width, Enter inserts a
|
|
22
|
+
* newline (onSubmit never fires), Up/Down move by line. Without a
|
|
23
|
+
* `layout.height` the field grows with its content (up to `maxRows` rows,
|
|
24
|
+
* then scrolls); with one it is a fixed box that scrolls to the caret.
|
|
25
|
+
*/
|
|
26
|
+
multiline?: boolean
|
|
27
|
+
/** Multiline without an explicit height: rows to grow to before scrolling. Default unbounded. */
|
|
28
|
+
maxRows?: number
|
|
41
29
|
/**
|
|
42
30
|
* IME behavior for the field's text sessions (keyboard type,
|
|
43
31
|
* capitalization, autocorrect). Identifier-like fields want
|
|
@@ -50,244 +38,46 @@ export interface TextInputProps {
|
|
|
50
38
|
style?: StyleProps
|
|
51
39
|
}
|
|
52
40
|
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
// text
|
|
56
|
-
//
|
|
57
|
-
// from bubbling further. Focused and editing are distinct (see
|
|
58
|
-
// activateField): navigation focuses, select begins editing, Enter while
|
|
59
|
-
// editing submits. Range selection (shift-movement,
|
|
60
|
-
// highlight, click-to-position) is not wired yet. Outside-click-to-blur is the
|
|
61
|
-
// caller's job.
|
|
41
|
+
// A plain string field over the shared EditorField shell: the buffer is
|
|
42
|
+
// core's createTextBuffer and every line is one d-text of the value's slice.
|
|
43
|
+
// Detached text takes its width from the box it is drawn in; +1 so the ink
|
|
44
|
+
// width rounding never wraps a line's own text.
|
|
62
45
|
export function TextInput(props: TextInputProps) {
|
|
63
|
-
let
|
|
64
|
-
|
|
65
|
-
let node: { id: number } | undefined
|
|
66
|
-
let viewport: { id: number } | undefined
|
|
67
|
-
let blinkId: any = null
|
|
68
|
-
|
|
69
|
-
// Derived from core's reactive focus (setFocus is the only writer); the
|
|
70
|
-
// onFocus/onBlur handlers below keep only their side effects (blink timer,
|
|
71
|
-
// caller callbacks). focusedNode() is read FIRST, unconditionally: the
|
|
72
|
-
// memo may first compute before the ref has set `node`, and
|
|
73
|
-
// short-circuiting past the read would leave it dependency-free, frozen
|
|
74
|
-
// false forever.
|
|
75
|
-
let focused = createMemo(() => {
|
|
76
|
-
let id = focusedNode()
|
|
77
|
-
return id != null && id === node?.id
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
let buffer = createTextBuffer({
|
|
81
|
-
value: () => props.value,
|
|
82
|
-
defaultValue: props.defaultValue,
|
|
83
|
-
onInput: (v) => props.onInput?.(v),
|
|
84
|
-
maxLength: () => props.maxLength,
|
|
85
|
-
})
|
|
86
|
-
let value = buffer.value
|
|
87
|
-
|
|
88
|
-
// autoFocus runs in an effect, not the ref: setFocus fires onFocus and reads
|
|
89
|
-
// the node's onTextInput handler to toggle the keyboard, and those handlers
|
|
90
|
-
// are only registered after the element's props are applied. The ref can fire
|
|
91
|
-
// before that, so focusing there would no-op.
|
|
92
|
-
createEffect(
|
|
93
|
-
() => props.autoFocus,
|
|
94
|
-
(autoFocus) => {
|
|
95
|
-
if (autoFocus && node) setFocus(node.id)
|
|
96
|
-
},
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
let handlePointerDown = () => {
|
|
100
|
-
if (props.disabled) return
|
|
101
|
-
if (node) setFocus(node.id)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
let handleFocus = () => {
|
|
105
|
-
setCaretOn(true)
|
|
106
|
-
if (blinkId == null) {
|
|
107
|
-
blinkId = setInterval(() => setCaretOn((v) => !v), 500)
|
|
108
|
-
}
|
|
109
|
-
props.onFocus?.()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let handleBlur = () => {
|
|
113
|
-
if (blinkId != null) {
|
|
114
|
-
clearInterval(blinkId)
|
|
115
|
-
blinkId = null
|
|
116
|
-
}
|
|
117
|
-
props.onBlur?.()
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Keys the input consumes stop propagating: an ancestor (or an app-global
|
|
121
|
-
// shortcut on the window) must not also act on an ArrowLeft that moved the
|
|
122
|
-
// caret. Anything else (e.g. ctrl+s) bubbles on.
|
|
123
|
-
let handleKeyDown = (e: KeyEvent) => {
|
|
124
|
-
if (props.disabled) return
|
|
125
|
-
let consumed = true
|
|
126
|
-
if (e.key === "Backspace") {
|
|
127
|
-
buffer.deleteBackward()
|
|
128
|
-
setCaretOn(true)
|
|
129
|
-
} else if (e.key === "Delete") {
|
|
130
|
-
buffer.deleteForward()
|
|
131
|
-
setCaretOn(true)
|
|
132
|
-
} else if (e.key === "ArrowLeft") {
|
|
133
|
-
buffer.move("left")
|
|
134
|
-
setCaretOn(true)
|
|
135
|
-
} else if (e.key === "ArrowRight") {
|
|
136
|
-
buffer.move("right")
|
|
137
|
-
setCaretOn(true)
|
|
138
|
-
} else if (e.key === "Home") {
|
|
139
|
-
buffer.move("start")
|
|
140
|
-
setCaretOn(true)
|
|
141
|
-
} else if (e.key === "End") {
|
|
142
|
-
buffer.move("end")
|
|
143
|
-
setCaretOn(true)
|
|
144
|
-
} else if (e.key === "Enter" || e.code === "Select") {
|
|
145
|
-
// The remote center key's `key` is "Unidentified"; match its code.
|
|
146
|
-
activateField()
|
|
147
|
-
} else if (e.key === "Escape") {
|
|
148
|
-
if (node) setFocus(null)
|
|
149
|
-
} else {
|
|
150
|
-
consumed = false
|
|
151
|
-
}
|
|
152
|
-
if (consumed) e.stopPropagation()
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
let handleTextInput = (e: any) => {
|
|
156
|
-
if (props.disabled) return
|
|
157
|
-
buffer.insertText(e.text ?? "")
|
|
158
|
-
setCaretOn(true)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Select on the focused field: focused and editing are distinct states. A
|
|
162
|
-
// field reached by navigation is focused but has no text session yet -
|
|
163
|
-
// select begins one (raising the on-screen keyboard where used, e.g. a TV
|
|
164
|
-
// with no keyboard attached); while editing, it submits. On platforms
|
|
165
|
-
// where the session starts invisibly at focus (desktop, physical
|
|
166
|
-
// keyboard) the first branch never runs and Enter submits as always.
|
|
167
|
-
// Registered as the nav action too, for a controller's south button.
|
|
168
|
-
let activateField = () => {
|
|
169
|
-
if (props.disabled) return
|
|
170
|
-
if (!textInputActive()) {
|
|
171
|
-
startTextInput()
|
|
172
|
-
} else {
|
|
173
|
-
props.onSubmit?.(value())
|
|
174
|
-
setFocus(null)
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
let unregisterNav: (() => void) | null = null
|
|
179
|
-
|
|
180
|
-
onCleanup(() => {
|
|
181
|
-
if (blinkId != null) clearInterval(blinkId)
|
|
182
|
-
unregisterNav?.()
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
// Style overrides fall back to theme defaults. The border doubles as the
|
|
186
|
-
// focus ring: primary while focused, when the focus-ring policy asks for a
|
|
187
|
-
// visible indicator.
|
|
188
|
-
let textColor = () => props.style?.color ?? theme.color.text
|
|
189
|
-
let surfaceColor = () => props.style?.backgroundColor ?? theme.color.surface
|
|
190
|
-
let borderColor = () =>
|
|
191
|
-
props.style?.borderColor ?? (focused() && policy.focusRing ? theme.color.primary : theme.color.border)
|
|
192
|
-
let borderWidth = () => props.style?.borderWidth ?? theme.borderWidth.sm
|
|
193
|
-
let borderRadius = () => props.style?.borderRadius ?? theme.radius.sm
|
|
194
|
-
|
|
195
|
-
let showPlaceholder = () => !focused() && value().length === 0 && (props.placeholder ?? "").length > 0
|
|
196
|
-
let showCaret = () => focused() && caretOn() && !showPlaceholder()
|
|
197
|
-
|
|
198
|
-
// Everything inside the viewport is detached: the value is one d-text shaped
|
|
199
|
-
// at an unbounded width and the caret a d-rect at the measured before-caret
|
|
200
|
-
// width, so typing, caret movement, blink and scroll never touch layout. The
|
|
201
|
-
// viewport carries an explicit height (detached content takes no layout
|
|
202
|
-
// slot) equal to the one-line paragraph height, which keeps the text where
|
|
203
|
-
// the old centered attached row sat. createCaretScroll keeps the caret in
|
|
204
|
-
// view and flushes the offset before paint; scrollX is a paint-time
|
|
205
|
-
// translate that also applies to detached children.
|
|
206
|
-
// All one-line metrics derive from the scaled body size, so the field, the
|
|
207
|
-
// caret, and the scroll math grow together under policy.textScale.
|
|
208
|
-
let fontSize = () => theme.text.body.size * policy.textScale
|
|
209
|
-
let rowHeight = () => Math.round(fontSize() * theme.text.body.lineHeight)
|
|
210
|
-
let caretX = () => measureText(value().slice(0, buffer.caret()), { fontSize: fontSize() }).width
|
|
211
|
-
let scrollX = createCaretScroll(
|
|
212
|
-
() => viewport,
|
|
213
|
-
() => ({
|
|
214
|
-
text: value(),
|
|
215
|
-
fontSize: fontSize(),
|
|
216
|
-
caret: buffer.caret(),
|
|
217
|
-
// Constant, not tied to caret visibility: the caret's footprint does not
|
|
218
|
-
// change as it blinks, so reserving the column only when shown would swing
|
|
219
|
-
// the scroll offset every blink and shift text that exactly fills the box.
|
|
220
|
-
caretWidth: CARET_WIDTH,
|
|
221
|
-
}),
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
let textStyle = (color: Color | Gradient) => ({
|
|
225
|
-
w: TEXT_SHAPE_WIDTH,
|
|
226
|
-
fontSize: fontSize(),
|
|
227
|
-
lineHeight: theme.text.body.lineHeight,
|
|
228
|
-
color,
|
|
229
|
-
maxLines: 1,
|
|
230
|
-
})
|
|
231
|
-
|
|
46
|
+
let value = (): string => ""
|
|
232
47
|
return (
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
48
|
+
<EditorField
|
|
49
|
+
transition={props.transition}
|
|
50
|
+
onTransitionEnd={props.onTransitionEnd}
|
|
51
|
+
buffer={(step) => {
|
|
52
|
+
let buffer = createTextBuffer({
|
|
53
|
+
value: () => props.value,
|
|
54
|
+
// A one-shot initial value by contract (see createTextBuffer): read
|
|
55
|
+
// once, deliberately untracked.
|
|
56
|
+
defaultValue: untrack(() => props.defaultValue),
|
|
57
|
+
onInput: (v) => props.onInput?.(v),
|
|
58
|
+
maxLength: () => props.maxLength,
|
|
59
|
+
step,
|
|
60
|
+
})
|
|
61
|
+
value = buffer.value
|
|
62
|
+
return buffer
|
|
239
63
|
}}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
{
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
onTextInput={handleTextInput}
|
|
259
|
-
>
|
|
260
|
-
<d-rect color={surfaceColor()} radius={borderRadius()} />
|
|
261
|
-
<d-rect
|
|
262
|
-
drawStyle="stroke"
|
|
263
|
-
color={borderColor()}
|
|
264
|
-
strokeWidth={borderWidth()}
|
|
265
|
-
radius={borderRadius()}
|
|
266
|
-
/>
|
|
267
|
-
<view
|
|
268
|
-
ref={(n: { id: number }) => (viewport = n)}
|
|
269
|
-
flex={1}
|
|
270
|
-
height={rowHeight()}
|
|
271
|
-
overflow="hidden"
|
|
272
|
-
scrollX={scrollX()}
|
|
273
|
-
>
|
|
274
|
-
{showPlaceholder() ? (
|
|
275
|
-
<d-text {...textStyle(theme.color.textMuted)}>{props.placeholder ?? ""}</d-text>
|
|
276
|
-
) : (
|
|
277
|
-
<>
|
|
278
|
-
<d-text {...textStyle(textColor())}>{value()}</d-text>
|
|
279
|
-
{showCaret() ? (
|
|
280
|
-
<d-rect
|
|
281
|
-
color={textColor()}
|
|
282
|
-
x={caretX()}
|
|
283
|
-
y={(rowHeight() - fontSize()) / 2}
|
|
284
|
-
w={CARET_WIDTH}
|
|
285
|
-
h={fontSize()}
|
|
286
|
-
/>
|
|
287
|
-
) : null}
|
|
288
|
-
</>
|
|
289
|
-
)}
|
|
290
|
-
</view>
|
|
291
|
-
</view>
|
|
64
|
+
renderLine={({ line, font, color }) => (
|
|
65
|
+
<d-text y={line().y} w={line().width + 1} {...font()} color={color()} maxLines={1}>
|
|
66
|
+
{value().slice(line().start, line().end)}
|
|
67
|
+
</d-text>
|
|
68
|
+
)}
|
|
69
|
+
onSubmit={props.onSubmit}
|
|
70
|
+
onFocus={props.onFocus}
|
|
71
|
+
onBlur={props.onBlur}
|
|
72
|
+
placeholder={props.placeholder}
|
|
73
|
+
disabled={props.disabled}
|
|
74
|
+
autoFocus={props.autoFocus}
|
|
75
|
+
multiline={props.multiline}
|
|
76
|
+
maxRows={props.maxRows}
|
|
77
|
+
hints={props.hints}
|
|
78
|
+
ref={props.ref}
|
|
79
|
+
layout={props.layout}
|
|
80
|
+
style={{ ...theme.components.textInput, ...props.style }}
|
|
81
|
+
/>
|
|
292
82
|
)
|
|
293
|
-
}
|
|
83
|
+
}
|
package/src/text.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createMemo } from "@solidrt/core"
|
|
2
2
|
import type { PointerProps } from "@solidrt/core"
|
|
3
|
-
import type { StyleProps, TextLayoutProps } from "./types"
|
|
3
|
+
import type { StyleProps, TextLayoutProps, TransitionProps, TransitionViewProp } from "./types"
|
|
4
|
+
import { splitTransition, transitionEndFor } from "./types"
|
|
4
5
|
import { theme, type TextVariant } from "./theme"
|
|
5
6
|
import { policy } from "./policy"
|
|
6
7
|
import { typeWeight } from "./typography"
|
|
@@ -9,7 +10,7 @@ import { typeWeight } from "./typography"
|
|
|
9
10
|
// make sense as a text fill; style.color takes raw values for anything else.
|
|
10
11
|
export type TextColor = "text" | "textMuted" | "primary" | "onPrimary" | "danger"
|
|
11
12
|
|
|
12
|
-
export interface TextProps extends PointerProps {
|
|
13
|
+
export interface TextProps extends PointerProps, TransitionProps<TransitionViewProp | "color"> {
|
|
13
14
|
children?: any
|
|
14
15
|
// Typography role from the theme's type scale; defaults to "body". Explicit
|
|
15
16
|
// layout font props override the role's fields individually. fontSize
|
|
@@ -54,8 +55,25 @@ export function Text(props: TextProps) {
|
|
|
54
55
|
return out
|
|
55
56
|
})
|
|
56
57
|
|
|
58
|
+
// The text node owns `color`; everything else a Text animates is on the
|
|
59
|
+
// wrapper view. A shorthand or `all` reaches both.
|
|
60
|
+
let split = () => {
|
|
61
|
+
let t = splitTransition(props.transition)
|
|
62
|
+
if (t.root == null || typeof t.root === "string") return { root: t.root, text: t.root }
|
|
63
|
+
let { color, ...rest } = t.root as Record<string, unknown>
|
|
64
|
+
let text: Record<string, unknown> = {}
|
|
65
|
+
if (color !== undefined) text.color = color
|
|
66
|
+
if (rest.all !== undefined) text.all = rest.all
|
|
67
|
+
return {
|
|
68
|
+
root: Object.keys(rest).length ? (rest as typeof t.root) : undefined,
|
|
69
|
+
text: Object.keys(text).length ? (text as typeof t.root) : undefined,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
57
73
|
return (
|
|
58
74
|
<view
|
|
75
|
+
transition={split().root}
|
|
76
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
59
77
|
ref={props.ref}
|
|
60
78
|
{...box()}
|
|
61
79
|
x={props.style?.x}
|
|
@@ -77,6 +95,8 @@ export function Text(props: TextProps) {
|
|
|
77
95
|
pointerEvents={props.pointerEvents}
|
|
78
96
|
>
|
|
79
97
|
<text
|
|
98
|
+
transition={split().text}
|
|
99
|
+
onTransitionEnd={transitionEndFor("root", props.onTransitionEnd)}
|
|
80
100
|
color={color()}
|
|
81
101
|
fontFamily={props.layout?.fontFamily ?? theme.text.fontFamily}
|
|
82
102
|
fontSize={size()}
|