@webority-technologies/mobile 0.0.20 → 0.0.22
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/lib/commonjs/components/BottomSheet/BottomSheet.js +61 -4
- package/lib/commonjs/components/BottomSheet/index.js +6 -0
- package/lib/commonjs/components/FieldBase/FieldBase.js +212 -24
- package/lib/commonjs/components/Input/Input.js +1 -1
- package/lib/commonjs/components/SearchBar/SearchBar.js +8 -2
- package/lib/commonjs/components/Select/Select.js +1 -1
- package/lib/commonjs/components/index.js +6 -0
- package/lib/module/components/BottomSheet/BottomSheet.js +60 -4
- package/lib/module/components/BottomSheet/index.js +1 -1
- package/lib/module/components/FieldBase/FieldBase.js +211 -23
- package/lib/module/components/Input/Input.js +1 -1
- package/lib/module/components/SearchBar/SearchBar.js +8 -2
- package/lib/module/components/Select/Select.js +1 -1
- package/lib/module/components/index.js +1 -1
- package/lib/typescript/commonjs/components/BottomSheet/BottomSheet.d.ts +41 -0
- package/lib/typescript/commonjs/components/BottomSheet/index.d.ts +2 -2
- package/lib/typescript/commonjs/components/FieldBase/FieldBase.d.ts +43 -12
- package/lib/typescript/commonjs/components/Input/Input.d.ts +1 -1
- package/lib/typescript/commonjs/components/index.d.ts +2 -2
- package/lib/typescript/commonjs/theme/types.d.ts +31 -7
- package/lib/typescript/module/components/BottomSheet/BottomSheet.d.ts +41 -0
- package/lib/typescript/module/components/BottomSheet/index.d.ts +2 -2
- package/lib/typescript/module/components/FieldBase/FieldBase.d.ts +43 -12
- package/lib/typescript/module/components/Input/Input.d.ts +1 -1
- package/lib/typescript/module/components/index.d.ts +2 -2
- package/lib/typescript/module/theme/types.d.ts +31 -7
- package/package.json +1 -1
|
@@ -27,6 +27,66 @@
|
|
|
27
27
|
import React, { useEffect, useMemo, useRef } from 'react';
|
|
28
28
|
import { Animated, Easing, Pressable, StyleSheet, View } from 'react-native';
|
|
29
29
|
import { useTheme, createAnimatedValue, fontFor } from "../../theme/index.js";
|
|
30
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
31
|
+
const SIDES = ['top', 'right', 'bottom', 'left'];
|
|
32
|
+
|
|
33
|
+
/** Map a single colour string to all four sides (shorthand expansion). */
|
|
34
|
+
const expandColorSides = value => {
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
return {
|
|
37
|
+
top: value,
|
|
38
|
+
right: value,
|
|
39
|
+
bottom: value,
|
|
40
|
+
left: value
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Object form: any side the consumer omitted falls through to 'transparent'.
|
|
44
|
+
// Rationale: if a consumer says `{ bottom: '#000' }`, they want only the
|
|
45
|
+
// bottom drawn — the other sides should be invisible, not inherit some
|
|
46
|
+
// unrelated colour.
|
|
47
|
+
return {
|
|
48
|
+
top: value.top ?? 'transparent',
|
|
49
|
+
right: value.right ?? 'transparent',
|
|
50
|
+
bottom: value.bottom ?? 'transparent',
|
|
51
|
+
left: value.left ?? 'transparent'
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Map a single width number to all four sides (shorthand expansion). */
|
|
56
|
+
const expandWidthSides = value => {
|
|
57
|
+
if (typeof value === 'number') {
|
|
58
|
+
return {
|
|
59
|
+
top: value,
|
|
60
|
+
right: value,
|
|
61
|
+
bottom: value,
|
|
62
|
+
left: value
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
top: value.top ?? 0,
|
|
67
|
+
right: value.right ?? 0,
|
|
68
|
+
bottom: value.bottom ?? 0,
|
|
69
|
+
left: value.left ?? 0
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/** Are all four sides of a per-side record equal? Used to collapse to shorthand. */
|
|
74
|
+
const allSidesEqual = sides => sides.top === sides.right && sides.right === sides.bottom && sides.bottom === sides.left;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Pick the most "representative" side colour from a 4-side record. Used by
|
|
78
|
+
* consumers (Input's selectionColor, focus rings, etc.) that need a single
|
|
79
|
+
* colour to mirror the visible focus indicator. Priority: bottom → top →
|
|
80
|
+
* left → right, skipping `'transparent'`. The bottom-first order means
|
|
81
|
+
* underline variants pick their underline colour automatically.
|
|
82
|
+
*/
|
|
83
|
+
export const pickRepresentativeBorderColor = sides => {
|
|
84
|
+
for (const side of ['bottom', 'top', 'left', 'right']) {
|
|
85
|
+
const c = sides[side];
|
|
86
|
+
if (c && c !== 'transparent') return c;
|
|
87
|
+
}
|
|
88
|
+
return sides.bottom;
|
|
89
|
+
};
|
|
30
90
|
|
|
31
91
|
/**
|
|
32
92
|
* Resolved text styling for the editable / displayed content inside a field.
|
|
@@ -36,7 +96,7 @@ import { useTheme, createAnimatedValue, fontFor } from "../../theme/index.js";
|
|
|
36
96
|
* same place. OTPInput intentionally overrides this with its own semibold
|
|
37
97
|
* display weight.
|
|
38
98
|
*/
|
|
39
|
-
|
|
99
|
+
|
|
40
100
|
/**
|
|
41
101
|
* Resolve the canonical text style chunk for a field's value. Reads from
|
|
42
102
|
* `theme.components.field.{textColor,disabledTextColor,placeholderColor,fontWeight}`
|
|
@@ -115,31 +175,70 @@ const DEFAULT_SIZES = {
|
|
|
115
175
|
|
|
116
176
|
/**
|
|
117
177
|
* Strict, fully-resolved colour set used internally. Mirrors `FieldVariantTokens`
|
|
118
|
-
* but every
|
|
119
|
-
*
|
|
178
|
+
* but every border colour / width is expanded to a 4-side record by the time
|
|
179
|
+
* the box renders, so the animation pipeline can treat per-side and shorthand
|
|
180
|
+
* the same way. `borderFocusedRepresentative` is a single colour pulled from
|
|
181
|
+
* the focused sides — exposed for consumers (Input's selectionColor) that
|
|
182
|
+
* need to mirror the visible focus indicator without re-resolving per side.
|
|
120
183
|
*/
|
|
121
184
|
|
|
122
185
|
/**
|
|
123
186
|
* Resolve the full set of state-aware fill + border colours for a given
|
|
124
|
-
* variant. Order: explicit override → theme token → library default.
|
|
187
|
+
* variant. Order: explicit override → theme token → library default. Border
|
|
188
|
+
* colour + width are normalized to per-side records here; callers that need
|
|
189
|
+
* a single colour read `borderFocusedRepresentative`.
|
|
125
190
|
*/
|
|
126
191
|
export const resolveVariantColors = (theme, variant, override) => {
|
|
127
192
|
const tokenSet = theme.components.field?.[variant];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
193
|
+
|
|
194
|
+
// Variant-specific defaults for resting fill + border treatment. `outlined`
|
|
195
|
+
// draws all four sides; `filled` draws nothing (fill-only); `underline`
|
|
196
|
+
// draws only the bottom edge — Material-style single-line inputs.
|
|
197
|
+
let idleEmpty;
|
|
198
|
+
let borderIdleDefault;
|
|
199
|
+
let borderWidthDefault;
|
|
200
|
+
if (variant === 'outlined') {
|
|
201
|
+
idleEmpty = theme.colors.background.primary;
|
|
202
|
+
borderIdleDefault = theme.colors.border.primary;
|
|
203
|
+
borderWidthDefault = theme.colors.border.width;
|
|
204
|
+
} else if (variant === 'underline') {
|
|
205
|
+
idleEmpty = 'transparent';
|
|
206
|
+
borderIdleDefault = {
|
|
207
|
+
bottom: theme.colors.border.primary
|
|
208
|
+
};
|
|
209
|
+
borderWidthDefault = {
|
|
210
|
+
bottom: theme.colors.border.width
|
|
211
|
+
};
|
|
212
|
+
} else {
|
|
213
|
+
idleEmpty = theme.colors.background.secondary;
|
|
214
|
+
borderIdleDefault = 'transparent';
|
|
215
|
+
borderWidthDefault = 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// For focused/error/disabled we expand using the same per-side shape as
|
|
219
|
+
// idle. If `borderIdle` ended up bottom-only (underline), an unset
|
|
220
|
+
// `borderFocused` defaults to `{ bottom: colors.border.focus }` so the
|
|
221
|
+
// focus indicator stays on the same edge. Same logic for error.
|
|
222
|
+
const wrapToIdleShape = colour => typeof borderIdleDefault === 'string' ? colour : {
|
|
223
|
+
bottom: colour
|
|
224
|
+
};
|
|
225
|
+
const borderIdle = expandColorSides(override?.borderIdle ?? tokenSet?.borderIdle ?? borderIdleDefault);
|
|
226
|
+
const borderFocused = expandColorSides(override?.borderFocused ?? tokenSet?.borderFocused ?? wrapToIdleShape(theme.colors.border.focus));
|
|
227
|
+
const borderError = expandColorSides(override?.borderError ?? tokenSet?.borderError ?? wrapToIdleShape(theme.colors.border.error));
|
|
228
|
+
const borderDisabled = expandColorSides(override?.borderDisabled ?? tokenSet?.borderDisabled ?? borderIdleDefault);
|
|
229
|
+
const borderWidth = expandWidthSides(override?.borderWidth ?? tokenSet?.borderWidth ?? borderWidthDefault);
|
|
132
230
|
return {
|
|
133
231
|
backgroundIdleEmpty: override?.backgroundIdleEmpty ?? tokenSet?.backgroundIdleEmpty ?? idleEmpty,
|
|
134
232
|
backgroundIdleFilled: override?.backgroundIdleFilled ?? tokenSet?.backgroundIdleFilled ?? override?.backgroundIdleEmpty ?? tokenSet?.backgroundIdleEmpty ?? idleEmpty,
|
|
135
233
|
backgroundFocused: override?.backgroundFocused ?? tokenSet?.backgroundFocused ?? undefined,
|
|
136
234
|
backgroundError: override?.backgroundError ?? tokenSet?.backgroundError ?? undefined,
|
|
137
235
|
backgroundDisabled: override?.backgroundDisabled ?? tokenSet?.backgroundDisabled ?? theme.colors.surface.disabled,
|
|
138
|
-
borderIdle
|
|
139
|
-
borderFocused
|
|
140
|
-
borderError
|
|
141
|
-
borderDisabled
|
|
142
|
-
borderWidth
|
|
236
|
+
borderIdle,
|
|
237
|
+
borderFocused,
|
|
238
|
+
borderError,
|
|
239
|
+
borderDisabled,
|
|
240
|
+
borderWidth,
|
|
241
|
+
borderFocusedRepresentative: pickRepresentativeBorderColor(borderFocused)
|
|
143
242
|
};
|
|
144
243
|
};
|
|
145
244
|
export const FieldBase = props => {
|
|
@@ -161,7 +260,15 @@ export const FieldBase = props => {
|
|
|
161
260
|
paddingHorizontal: paddingHorizontalProp,
|
|
162
261
|
paddingVertical: paddingVerticalProp,
|
|
163
262
|
borderRadius: borderRadiusProp,
|
|
263
|
+
borderTopLeftRadius: borderTopLeftRadiusProp,
|
|
264
|
+
borderTopRightRadius: borderTopRightRadiusProp,
|
|
265
|
+
borderBottomLeftRadius: borderBottomLeftRadiusProp,
|
|
266
|
+
borderBottomRightRadius: borderBottomRightRadiusProp,
|
|
164
267
|
borderWidth: borderWidthProp,
|
|
268
|
+
borderTopWidth: borderTopWidthProp,
|
|
269
|
+
borderRightWidth: borderRightWidthProp,
|
|
270
|
+
borderBottomWidth: borderBottomWidthProp,
|
|
271
|
+
borderLeftWidth: borderLeftWidthProp,
|
|
165
272
|
gap: gapProp,
|
|
166
273
|
fillOverrides,
|
|
167
274
|
style,
|
|
@@ -200,13 +307,6 @@ export const FieldBase = props => {
|
|
|
200
307
|
const idleFill = filled ? colors.backgroundIdleFilled : colors.backgroundIdleEmpty;
|
|
201
308
|
const focusedFill = colors.backgroundFocused ?? idleFill;
|
|
202
309
|
const errorFill = colors.backgroundError ?? idleFill;
|
|
203
|
-
const animatedBorderColor = disabled ? colors.borderDisabled : error ? errorAnim.interpolate({
|
|
204
|
-
inputRange: [0, 1],
|
|
205
|
-
outputRange: [colors.borderIdle, colors.borderError]
|
|
206
|
-
}) : focusAnim.interpolate({
|
|
207
|
-
inputRange: [0, 1],
|
|
208
|
-
outputRange: [colors.borderIdle, colors.borderFocused]
|
|
209
|
-
});
|
|
210
310
|
const animatedBackgroundColor = disabled ? colors.backgroundDisabled : error ? errorAnim.interpolate({
|
|
211
311
|
inputRange: [0, 1],
|
|
212
312
|
outputRange: [idleFill, errorFill]
|
|
@@ -214,17 +314,105 @@ export const FieldBase = props => {
|
|
|
214
314
|
inputRange: [0, 1],
|
|
215
315
|
outputRange: [idleFill, focusedFill]
|
|
216
316
|
});
|
|
317
|
+
|
|
318
|
+
// Per-side border colour. For each side we resolve the "from" (idle) and
|
|
319
|
+
// "to" (focused or error, depending on state) colour, then drive a single
|
|
320
|
+
// animated interpolation. When all four sides share the same from+to pair
|
|
321
|
+
// we collapse to one `borderColor` key — the common case stays cheap.
|
|
322
|
+
const activeColors = error ? colors.borderError : colors.borderFocused;
|
|
323
|
+
const activeAnim = error ? errorAnim : focusAnim;
|
|
324
|
+
const sideFrom = disabled ? colors.borderDisabled : colors.borderIdle;
|
|
325
|
+
const sideTo = disabled ? colors.borderDisabled : activeColors;
|
|
326
|
+
|
|
327
|
+
// Per-side widths: longhand prop wins over shorthand prop wins over
|
|
328
|
+
// resolved variant token. Each side independently.
|
|
329
|
+
const propWidth = {
|
|
330
|
+
top: borderTopWidthProp,
|
|
331
|
+
right: borderRightWidthProp,
|
|
332
|
+
bottom: borderBottomWidthProp,
|
|
333
|
+
left: borderLeftWidthProp
|
|
334
|
+
};
|
|
335
|
+
const resolvedWidths = {
|
|
336
|
+
top: propWidth.top ?? borderWidthProp ?? colors.borderWidth.top,
|
|
337
|
+
right: propWidth.right ?? borderWidthProp ?? colors.borderWidth.right,
|
|
338
|
+
bottom: propWidth.bottom ?? borderWidthProp ?? colors.borderWidth.bottom,
|
|
339
|
+
left: propWidth.left ?? borderWidthProp ?? colors.borderWidth.left
|
|
340
|
+
};
|
|
217
341
|
const boxStyle = {
|
|
218
342
|
minHeight: height ?? minHeightProp ?? sizeTokens.minHeight,
|
|
219
343
|
maxHeight,
|
|
220
344
|
paddingHorizontal: paddingHorizontalProp ?? sizeTokens.paddingHorizontal,
|
|
221
345
|
paddingVertical: paddingVerticalProp ?? sizeTokens.paddingVertical,
|
|
222
|
-
borderRadius: borderRadiusProp ?? sizeTokens.borderRadius,
|
|
223
|
-
borderWidth: borderWidthProp ?? colors.borderWidth,
|
|
224
|
-
borderColor: animatedBorderColor,
|
|
225
346
|
backgroundColor: animatedBackgroundColor,
|
|
226
347
|
columnGap: gapProp ?? theme.spacing.sm
|
|
227
348
|
};
|
|
349
|
+
|
|
350
|
+
// Radius: per-corner prop wins over shorthand prop wins over size token.
|
|
351
|
+
// Collapse to single `borderRadius` when all four corners match.
|
|
352
|
+
const radiusShorthand = borderRadiusProp ?? sizeTokens.borderRadius;
|
|
353
|
+
const corners = {
|
|
354
|
+
tl: borderTopLeftRadiusProp ?? radiusShorthand,
|
|
355
|
+
tr: borderTopRightRadiusProp ?? radiusShorthand,
|
|
356
|
+
bl: borderBottomLeftRadiusProp ?? radiusShorthand,
|
|
357
|
+
br: borderBottomRightRadiusProp ?? radiusShorthand
|
|
358
|
+
};
|
|
359
|
+
if (corners.tl === corners.tr && corners.tr === corners.bl && corners.bl === corners.br) {
|
|
360
|
+
boxStyle.borderRadius = corners.tl;
|
|
361
|
+
} else {
|
|
362
|
+
boxStyle.borderTopLeftRadius = corners.tl;
|
|
363
|
+
boxStyle.borderTopRightRadius = corners.tr;
|
|
364
|
+
boxStyle.borderBottomLeftRadius = corners.bl;
|
|
365
|
+
boxStyle.borderBottomRightRadius = corners.br;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Width: collapse to single `borderWidth` when all four sides match.
|
|
369
|
+
if (resolvedWidths.top === resolvedWidths.right && resolvedWidths.right === resolvedWidths.bottom && resolvedWidths.bottom === resolvedWidths.left) {
|
|
370
|
+
boxStyle.borderWidth = resolvedWidths.top;
|
|
371
|
+
} else {
|
|
372
|
+
boxStyle.borderTopWidth = resolvedWidths.top;
|
|
373
|
+
boxStyle.borderRightWidth = resolvedWidths.right;
|
|
374
|
+
boxStyle.borderBottomWidth = resolvedWidths.bottom;
|
|
375
|
+
boxStyle.borderLeftWidth = resolvedWidths.left;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Colour: collapse to single `borderColor` only when both endpoints are
|
|
379
|
+
// identical across all sides. Otherwise emit four `borderXColor`
|
|
380
|
+
// interpolations. Disabled state is static (no animation needed).
|
|
381
|
+
const fromAllEqual = allSidesEqual(sideFrom);
|
|
382
|
+
const toAllEqual = allSidesEqual(sideTo);
|
|
383
|
+
if (disabled) {
|
|
384
|
+
if (fromAllEqual) {
|
|
385
|
+
boxStyle.borderColor = sideFrom.top;
|
|
386
|
+
} else {
|
|
387
|
+
boxStyle.borderTopColor = sideFrom.top;
|
|
388
|
+
boxStyle.borderRightColor = sideFrom.right;
|
|
389
|
+
boxStyle.borderBottomColor = sideFrom.bottom;
|
|
390
|
+
boxStyle.borderLeftColor = sideFrom.left;
|
|
391
|
+
}
|
|
392
|
+
} else if (fromAllEqual && toAllEqual && sideFrom.top === sideFrom.right && sideTo.top === sideTo.right) {
|
|
393
|
+
// Common case: shorthand on both ends → single interpolation.
|
|
394
|
+
boxStyle.borderColor = activeAnim.interpolate({
|
|
395
|
+
inputRange: [0, 1],
|
|
396
|
+
outputRange: [sideFrom.top, sideTo.top]
|
|
397
|
+
});
|
|
398
|
+
} else {
|
|
399
|
+
boxStyle.borderTopColor = activeAnim.interpolate({
|
|
400
|
+
inputRange: [0, 1],
|
|
401
|
+
outputRange: [sideFrom.top, sideTo.top]
|
|
402
|
+
});
|
|
403
|
+
boxStyle.borderRightColor = activeAnim.interpolate({
|
|
404
|
+
inputRange: [0, 1],
|
|
405
|
+
outputRange: [sideFrom.right, sideTo.right]
|
|
406
|
+
});
|
|
407
|
+
boxStyle.borderBottomColor = activeAnim.interpolate({
|
|
408
|
+
inputRange: [0, 1],
|
|
409
|
+
outputRange: [sideFrom.bottom, sideTo.bottom]
|
|
410
|
+
});
|
|
411
|
+
boxStyle.borderLeftColor = activeAnim.interpolate({
|
|
412
|
+
inputRange: [0, 1],
|
|
413
|
+
outputRange: [sideFrom.left, sideTo.left]
|
|
414
|
+
});
|
|
415
|
+
}
|
|
228
416
|
if (width !== undefined) boxStyle.width = width;
|
|
229
417
|
if (height !== undefined) boxStyle.height = height;
|
|
230
418
|
const a11yState = {
|
|
@@ -163,7 +163,7 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
163
163
|
// Selection / caret / handle colour walks the same resolution chain as the
|
|
164
164
|
// focused border so brands that override `components.field.<variant>.borderFocused`
|
|
165
165
|
// get a matching selection tint without also having to update root `border.focus`.
|
|
166
|
-
const selectionColor = useMemo(() => resolveVariantColors(theme, variant, fillOverrides).
|
|
166
|
+
const selectionColor = useMemo(() => resolveVariantColors(theme, variant, fillOverrides).borderFocusedRepresentative, [theme, variant, fillOverrides]);
|
|
167
167
|
const borderRadiusOverride = borderRadiusProp ?? theme.components.input?.borderRadius;
|
|
168
168
|
|
|
169
169
|
// Floating label styles
|
|
@@ -35,7 +35,13 @@ const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
35
35
|
// field-family token (`components.field.defaultVariant`) → library default.
|
|
36
36
|
// Library default stays `'filled'` so SearchBar reads as the traditional
|
|
37
37
|
// pill-shaped, borderless search box when no theme opinion is expressed.
|
|
38
|
-
|
|
38
|
+
// SearchBar deliberately only supports 'filled' and 'outlined' — an
|
|
39
|
+
// underline-only pill makes no visual sense. If the shared field default is
|
|
40
|
+
// 'underline', fall through to 'filled' rather than carry an unsupported
|
|
41
|
+
// value down to the variant token lookup.
|
|
42
|
+
const sharedDefault = theme.components.field?.defaultVariant;
|
|
43
|
+
const sharedFallback = sharedDefault === 'outlined' || sharedDefault === 'filled' ? sharedDefault : 'filled';
|
|
44
|
+
const variant = variantProp ?? theme.components.searchBar?.defaultVariant ?? sharedFallback;
|
|
39
45
|
const fieldTokens = resolveFieldSize(theme, size);
|
|
40
46
|
const sizeStyles = {
|
|
41
47
|
...fieldTokens,
|
|
@@ -179,7 +185,7 @@ const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
179
185
|
autoFocus: autoFocus,
|
|
180
186
|
editable: !disabled,
|
|
181
187
|
returnKeyType: "search",
|
|
182
|
-
selectionColor: resolveVariantColors(theme, variant).
|
|
188
|
+
selectionColor: resolveVariantColors(theme, variant).borderFocusedRepresentative,
|
|
183
189
|
accessibilityLabel: accessibilityLabel ?? placeholder,
|
|
184
190
|
accessibilityState: {
|
|
185
191
|
disabled
|
|
@@ -321,7 +321,7 @@ const Select = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
321
321
|
onChangeText: setQuery,
|
|
322
322
|
placeholder: "Search\u2026",
|
|
323
323
|
placeholderTextColor: theme.colors.text.tertiary,
|
|
324
|
-
selectionColor: resolveVariantColors(theme, theme.components.field?.defaultVariant ?? 'outlined').
|
|
324
|
+
selectionColor: resolveVariantColors(theme, theme.components.field?.defaultVariant ?? 'outlined').borderFocusedRepresentative,
|
|
325
325
|
accessibilityLabel: "Search options",
|
|
326
326
|
style: [styles.searchInput, {
|
|
327
327
|
color: theme.colors.text.primary,
|
|
@@ -5,7 +5,7 @@ export { Avatar, AvatarGroup } from "./Avatar/index.js";
|
|
|
5
5
|
export { Badge } from "./Badge/index.js";
|
|
6
6
|
export { Banner } from "./Banner/index.js";
|
|
7
7
|
export { BottomNavigation } from "./BottomNavigation/index.js";
|
|
8
|
-
export { BottomSheet } from "./BottomSheet/index.js";
|
|
8
|
+
export { BottomSheet, useBottomSheet } from "./BottomSheet/index.js";
|
|
9
9
|
export { Button } from "./Button/index.js";
|
|
10
10
|
export { Card } from "./Card/index.js";
|
|
11
11
|
export { Carousel } from "./Carousel/index.js";
|
|
@@ -56,6 +56,23 @@ export interface BottomSheetProps {
|
|
|
56
56
|
mode?: BottomSheetMode;
|
|
57
57
|
handleIndicatorStyle?: StyleProp<ViewStyle>;
|
|
58
58
|
containerStyle?: StyleProp<ViewStyle>;
|
|
59
|
+
/**
|
|
60
|
+
* Sticky region rendered between the drag handle and the scrollable content.
|
|
61
|
+
* Does not scroll with `children`. Can call `useBottomSheet()` to read sheet
|
|
62
|
+
* state.
|
|
63
|
+
*/
|
|
64
|
+
header?: React.ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* Sticky region pinned to the bottom of the sheet, above the safe-area
|
|
67
|
+
* inset. Does not scroll with `children`, and rides up with the keyboard
|
|
68
|
+
* when `keyboardBehavior='shift'` (no extra wiring needed — the whole sheet
|
|
69
|
+
* shifts). Typical use: action button rows.
|
|
70
|
+
*/
|
|
71
|
+
footer?: React.ReactNode;
|
|
72
|
+
/** Style applied to the header region wrapper. */
|
|
73
|
+
headerStyle?: StyleProp<ViewStyle>;
|
|
74
|
+
/** Style applied to the footer region wrapper. */
|
|
75
|
+
footerStyle?: StyleProp<ViewStyle>;
|
|
59
76
|
children?: React.ReactNode;
|
|
60
77
|
accessibilityLabel?: string;
|
|
61
78
|
accessibilityViewIsModal?: boolean;
|
|
@@ -66,6 +83,30 @@ export interface BottomSheetRef {
|
|
|
66
83
|
collapse: () => void;
|
|
67
84
|
close: () => void;
|
|
68
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* State + actions exposed to anything rendered inside a `<BottomSheet>` —
|
|
88
|
+
* including the `header` and `footer` slots. Read via `useBottomSheet()`.
|
|
89
|
+
*
|
|
90
|
+
* `snapIndex` is the JS-thread mirror of the current snap point. -1 means the
|
|
91
|
+
* sheet is closed (mid-close-animation included). Use it to drive footer
|
|
92
|
+
* button enable state, conditional headers, etc. If you need per-frame
|
|
93
|
+
* progress (e.g. fade a header in as the sheet expands), reach for the
|
|
94
|
+
* animated value via a future enhancement — not exposed today to keep the
|
|
95
|
+
* surface minimal.
|
|
96
|
+
*/
|
|
97
|
+
export interface BottomSheetContextValue {
|
|
98
|
+
snapIndex: number;
|
|
99
|
+
snapPoints: number[];
|
|
100
|
+
expand: (index?: number) => void;
|
|
101
|
+
collapse: () => void;
|
|
102
|
+
close: () => void;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Access the enclosing `<BottomSheet>`'s state and imperative actions.
|
|
106
|
+
* Must be called from a component rendered inside a `<BottomSheet>` (as
|
|
107
|
+
* `children`, `header`, or `footer`).
|
|
108
|
+
*/
|
|
109
|
+
export declare const useBottomSheet: () => BottomSheetContextValue;
|
|
69
110
|
declare const BottomSheet: React.ForwardRefExoticComponent<BottomSheetProps & React.RefAttributes<BottomSheetRef>>;
|
|
70
111
|
export { BottomSheet };
|
|
71
112
|
export default BottomSheet;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { BottomSheet, default } from './BottomSheet';
|
|
2
|
-
export type { BottomSheetProps, BottomSheetRef, SnapPoint } from './BottomSheet';
|
|
1
|
+
export { BottomSheet, default, useBottomSheet } from './BottomSheet';
|
|
2
|
+
export type { BottomSheetContextValue, BottomSheetProps, BottomSheetRef, SnapPoint } from './BottomSheet';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -23,9 +23,17 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import React from 'react';
|
|
25
25
|
import type { AccessibilityRole, AccessibilityState, StyleProp, TextStyle, ViewStyle } from 'react-native';
|
|
26
|
-
import type { FieldSizeTokens, FieldVariantTokens, Theme } from '../../theme/types';
|
|
26
|
+
import type { FieldSide, FieldSizeTokens, FieldVariantTokens, Theme } from '../../theme/types';
|
|
27
27
|
export type FieldBaseSize = 'sm' | 'md' | 'lg';
|
|
28
|
-
export type FieldBaseVariant = 'outlined' | 'filled';
|
|
28
|
+
export type FieldBaseVariant = 'outlined' | 'filled' | 'underline';
|
|
29
|
+
/**
|
|
30
|
+
* Pick the most "representative" side colour from a 4-side record. Used by
|
|
31
|
+
* consumers (Input's selectionColor, focus rings, etc.) that need a single
|
|
32
|
+
* colour to mirror the visible focus indicator. Priority: bottom → top →
|
|
33
|
+
* left → right, skipping `'transparent'`. The bottom-first order means
|
|
34
|
+
* underline variants pick their underline colour automatically.
|
|
35
|
+
*/
|
|
36
|
+
export declare const pickRepresentativeBorderColor: (sides: Record<FieldSide, string>) => string;
|
|
29
37
|
export interface FieldBaseProps {
|
|
30
38
|
/** Resolves to dimension tokens at `theme.components.field[size]`. Default `'md'`. */
|
|
31
39
|
size?: FieldBaseSize;
|
|
@@ -65,10 +73,26 @@ export interface FieldBaseProps {
|
|
|
65
73
|
paddingHorizontal?: number;
|
|
66
74
|
/** Override vertical padding (single-line). Multiline parents usually leave this default. */
|
|
67
75
|
paddingVertical?: number;
|
|
68
|
-
/** Override corner radius. SearchBar pill uses this to apply `radius.full`. */
|
|
76
|
+
/** Override corner radius (shorthand — all four corners). SearchBar pill uses this to apply `radius.full`. */
|
|
69
77
|
borderRadius?: number;
|
|
70
|
-
/**
|
|
78
|
+
/** Top-left corner radius. Wins over `borderRadius`. */
|
|
79
|
+
borderTopLeftRadius?: number;
|
|
80
|
+
/** Top-right corner radius. Wins over `borderRadius`. */
|
|
81
|
+
borderTopRightRadius?: number;
|
|
82
|
+
/** Bottom-left corner radius. Wins over `borderRadius`. */
|
|
83
|
+
borderBottomLeftRadius?: number;
|
|
84
|
+
/** Bottom-right corner radius. Wins over `borderRadius`. */
|
|
85
|
+
borderBottomRightRadius?: number;
|
|
86
|
+
/** Override border width (shorthand — all four sides). OTP uses 1.5; everything else inherits `colors.border.width`. */
|
|
71
87
|
borderWidth?: number;
|
|
88
|
+
/** Top border width. Wins over `borderWidth`. */
|
|
89
|
+
borderTopWidth?: number;
|
|
90
|
+
/** Right border width. Wins over `borderWidth`. */
|
|
91
|
+
borderRightWidth?: number;
|
|
92
|
+
/** Bottom border width. Wins over `borderWidth`. */
|
|
93
|
+
borderBottomWidth?: number;
|
|
94
|
+
/** Left border width. Wins over `borderWidth`. */
|
|
95
|
+
borderLeftWidth?: number;
|
|
72
96
|
/** Gap between leading / children / trailing. Defaults to `theme.spacing.sm`. */
|
|
73
97
|
gap?: number;
|
|
74
98
|
fillOverrides?: Partial<FieldVariantTokens>;
|
|
@@ -114,8 +138,11 @@ export declare const resolveFieldTextStyle: (theme: Theme, options?: FieldTextSt
|
|
|
114
138
|
export declare const resolveFieldSize: (theme: Theme, size: FieldBaseSize) => FieldSizeTokens;
|
|
115
139
|
/**
|
|
116
140
|
* Strict, fully-resolved colour set used internally. Mirrors `FieldVariantTokens`
|
|
117
|
-
* but every
|
|
118
|
-
*
|
|
141
|
+
* but every border colour / width is expanded to a 4-side record by the time
|
|
142
|
+
* the box renders, so the animation pipeline can treat per-side and shorthand
|
|
143
|
+
* the same way. `borderFocusedRepresentative` is a single colour pulled from
|
|
144
|
+
* the focused sides — exposed for consumers (Input's selectionColor) that
|
|
145
|
+
* need to mirror the visible focus indicator without re-resolving per side.
|
|
119
146
|
*/
|
|
120
147
|
interface ResolvedFieldColors {
|
|
121
148
|
backgroundIdleEmpty: string;
|
|
@@ -125,15 +152,19 @@ interface ResolvedFieldColors {
|
|
|
125
152
|
/** Optional override fill while in error — when undefined, error animates from idle to idle. */
|
|
126
153
|
backgroundError: string | undefined;
|
|
127
154
|
backgroundDisabled: string;
|
|
128
|
-
borderIdle: string
|
|
129
|
-
borderFocused: string
|
|
130
|
-
borderError: string
|
|
131
|
-
borderDisabled: string
|
|
132
|
-
borderWidth: number
|
|
155
|
+
borderIdle: Record<FieldSide, string>;
|
|
156
|
+
borderFocused: Record<FieldSide, string>;
|
|
157
|
+
borderError: Record<FieldSide, string>;
|
|
158
|
+
borderDisabled: Record<FieldSide, string>;
|
|
159
|
+
borderWidth: Record<FieldSide, number>;
|
|
160
|
+
/** Single representative colour from `borderFocused` — first non-transparent side, bottom-priority. */
|
|
161
|
+
borderFocusedRepresentative: string;
|
|
133
162
|
}
|
|
134
163
|
/**
|
|
135
164
|
* Resolve the full set of state-aware fill + border colours for a given
|
|
136
|
-
* variant. Order: explicit override → theme token → library default.
|
|
165
|
+
* variant. Order: explicit override → theme token → library default. Border
|
|
166
|
+
* colour + width are normalized to per-side records here; callers that need
|
|
167
|
+
* a single colour read `borderFocusedRepresentative`.
|
|
137
168
|
*/
|
|
138
169
|
export declare const resolveVariantColors: (theme: Theme, variant: FieldBaseVariant, override?: Partial<FieldVariantTokens>) => ResolvedFieldColors;
|
|
139
170
|
export declare const FieldBase: React.FC<FieldBaseProps>;
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { TextInput } from 'react-native';
|
|
3
3
|
import type { BlurEvent, FocusEvent, StyleProp, TextInputProps, TextStyle, ViewStyle } from 'react-native';
|
|
4
4
|
export type InputSize = 'sm' | 'md' | 'lg';
|
|
5
|
-
export type InputVariant = 'outlined' | 'filled';
|
|
5
|
+
export type InputVariant = 'outlined' | 'filled' | 'underline';
|
|
6
6
|
export type InputLabelMode = 'float' | 'top';
|
|
7
7
|
export interface InputProps extends Omit<TextInputProps, 'style'> {
|
|
8
8
|
label?: string;
|
|
@@ -8,8 +8,8 @@ export { Banner } from './Banner';
|
|
|
8
8
|
export type { BannerProps, BannerVariant, BannerAction } from './Banner';
|
|
9
9
|
export { BottomNavigation } from './BottomNavigation';
|
|
10
10
|
export type { BottomNavigationProps, TabConfig } from './BottomNavigation';
|
|
11
|
-
export { BottomSheet } from './BottomSheet';
|
|
12
|
-
export type { BottomSheetProps, BottomSheetRef } from './BottomSheet';
|
|
11
|
+
export { BottomSheet, useBottomSheet } from './BottomSheet';
|
|
12
|
+
export type { BottomSheetContextValue, BottomSheetProps, BottomSheetRef } from './BottomSheet';
|
|
13
13
|
export { Button } from './Button';
|
|
14
14
|
export type { ButtonProps, ButtonVariant, ButtonTone, ButtonSize } from './Button';
|
|
15
15
|
export { Card } from './Card';
|
|
@@ -391,12 +391,33 @@ export interface FieldSizeTokens {
|
|
|
391
391
|
borderRadius: number;
|
|
392
392
|
iconSize: number;
|
|
393
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* One of the four field-box sides. Used everywhere the library accepts
|
|
396
|
+
* per-side overrides for border width / colour.
|
|
397
|
+
*/
|
|
398
|
+
export type FieldSide = 'top' | 'right' | 'bottom' | 'left';
|
|
399
|
+
/**
|
|
400
|
+
* Shorthand-or-longhand value. A bare `T` means "apply to all four sides";
|
|
401
|
+
* an object form lets a consumer set specific sides while the others fall
|
|
402
|
+
* through to whatever the resolver decided. Mirrors how React Native's
|
|
403
|
+
* `borderWidth` + `borderXWidth` (and CSS `border` + `border-X`) compose.
|
|
404
|
+
*/
|
|
405
|
+
export type FieldPerSide<T> = T | Partial<Record<FieldSide, T>>;
|
|
406
|
+
/** Border colour value: a single colour string or a per-side colour map. */
|
|
407
|
+
export type FieldBorderColor = FieldPerSide<string>;
|
|
408
|
+
/** Border width value: a single width or a per-side width map. */
|
|
409
|
+
export type FieldBorderWidth = FieldPerSide<number>;
|
|
394
410
|
/**
|
|
395
411
|
* State-aware fill + border colours for a field variant. Resolution order
|
|
396
412
|
* inside FieldBase: variant token → library default. Idle distinguishes
|
|
397
413
|
* empty vs filled so consumers can tint the resting field differently once
|
|
398
414
|
* the user has typed. When focused/error/disabled fills are omitted, the
|
|
399
415
|
* box animates from the current idle fill to itself (no fill change).
|
|
416
|
+
*
|
|
417
|
+
* Border colour + width are `FieldPerSide` values — pass a bare string /
|
|
418
|
+
* number to set all four sides (the common case), or an object like
|
|
419
|
+
* `{ bottom: '#1A73E8' }` to draw only one side. Sides not specified in the
|
|
420
|
+
* object fall through to the shorthand default.
|
|
400
421
|
*/
|
|
401
422
|
export interface FieldVariantTokens {
|
|
402
423
|
backgroundIdleEmpty: string;
|
|
@@ -404,18 +425,20 @@ export interface FieldVariantTokens {
|
|
|
404
425
|
backgroundFocused?: string;
|
|
405
426
|
backgroundError?: string;
|
|
406
427
|
backgroundDisabled: string;
|
|
407
|
-
borderIdle:
|
|
408
|
-
borderFocused:
|
|
409
|
-
borderError:
|
|
410
|
-
borderDisabled:
|
|
411
|
-
borderWidth:
|
|
428
|
+
borderIdle: FieldBorderColor;
|
|
429
|
+
borderFocused: FieldBorderColor;
|
|
430
|
+
borderError: FieldBorderColor;
|
|
431
|
+
borderDisabled: FieldBorderColor;
|
|
432
|
+
borderWidth: FieldBorderWidth;
|
|
412
433
|
}
|
|
413
434
|
export interface FieldTokens extends Partial<Record<'sm' | 'md' | 'lg', FieldSizeTokens>> {
|
|
414
435
|
/** Default `variant` when caller doesn't pass one. Library default: 'outlined'. */
|
|
415
|
-
defaultVariant?: 'outlined' | 'filled';
|
|
436
|
+
defaultVariant?: 'outlined' | 'filled' | 'underline';
|
|
416
437
|
/** State-aware fill + border colours per variant. */
|
|
417
438
|
outlined?: Partial<FieldVariantTokens>;
|
|
418
439
|
filled?: Partial<FieldVariantTokens>;
|
|
440
|
+
/** Bottom-border-only variant — Material-style underline inputs. */
|
|
441
|
+
underline?: Partial<FieldVariantTokens>;
|
|
419
442
|
/** Text colour for the editable / displayed value. Defaults to `colors.text.primary`. */
|
|
420
443
|
textColor?: string;
|
|
421
444
|
/** Text colour when the field is disabled. Defaults to `colors.text.disabled`. */
|
|
@@ -439,7 +462,7 @@ export interface InputFillTokens {
|
|
|
439
462
|
}
|
|
440
463
|
export interface InputComponentTokens extends Partial<Record<ComponentSizeKey, InputSizeTokens>> {
|
|
441
464
|
/** Default `variant` when caller doesn't pass one. Library default: 'outlined'. */
|
|
442
|
-
defaultVariant?: 'outlined' | 'filled';
|
|
465
|
+
defaultVariant?: 'outlined' | 'filled' | 'underline';
|
|
443
466
|
/** Default `labelMode` when caller doesn't pass one. Library default: 'float'. */
|
|
444
467
|
defaultLabelMode?: 'float' | 'top';
|
|
445
468
|
/** Corner radius in pixels for the field wrapper. Library default: size-driven (sm=10, md=12, lg=14). */
|
|
@@ -468,6 +491,7 @@ export interface InputComponentTokens extends Partial<Record<ComponentSizeKey, I
|
|
|
468
491
|
fillColors?: {
|
|
469
492
|
outlined?: InputFillTokens;
|
|
470
493
|
filled?: InputFillTokens;
|
|
494
|
+
underline?: InputFillTokens;
|
|
471
495
|
};
|
|
472
496
|
}
|
|
473
497
|
export interface BottomSheetTokens {
|
|
@@ -56,6 +56,23 @@ export interface BottomSheetProps {
|
|
|
56
56
|
mode?: BottomSheetMode;
|
|
57
57
|
handleIndicatorStyle?: StyleProp<ViewStyle>;
|
|
58
58
|
containerStyle?: StyleProp<ViewStyle>;
|
|
59
|
+
/**
|
|
60
|
+
* Sticky region rendered between the drag handle and the scrollable content.
|
|
61
|
+
* Does not scroll with `children`. Can call `useBottomSheet()` to read sheet
|
|
62
|
+
* state.
|
|
63
|
+
*/
|
|
64
|
+
header?: React.ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* Sticky region pinned to the bottom of the sheet, above the safe-area
|
|
67
|
+
* inset. Does not scroll with `children`, and rides up with the keyboard
|
|
68
|
+
* when `keyboardBehavior='shift'` (no extra wiring needed — the whole sheet
|
|
69
|
+
* shifts). Typical use: action button rows.
|
|
70
|
+
*/
|
|
71
|
+
footer?: React.ReactNode;
|
|
72
|
+
/** Style applied to the header region wrapper. */
|
|
73
|
+
headerStyle?: StyleProp<ViewStyle>;
|
|
74
|
+
/** Style applied to the footer region wrapper. */
|
|
75
|
+
footerStyle?: StyleProp<ViewStyle>;
|
|
59
76
|
children?: React.ReactNode;
|
|
60
77
|
accessibilityLabel?: string;
|
|
61
78
|
accessibilityViewIsModal?: boolean;
|
|
@@ -66,6 +83,30 @@ export interface BottomSheetRef {
|
|
|
66
83
|
collapse: () => void;
|
|
67
84
|
close: () => void;
|
|
68
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* State + actions exposed to anything rendered inside a `<BottomSheet>` —
|
|
88
|
+
* including the `header` and `footer` slots. Read via `useBottomSheet()`.
|
|
89
|
+
*
|
|
90
|
+
* `snapIndex` is the JS-thread mirror of the current snap point. -1 means the
|
|
91
|
+
* sheet is closed (mid-close-animation included). Use it to drive footer
|
|
92
|
+
* button enable state, conditional headers, etc. If you need per-frame
|
|
93
|
+
* progress (e.g. fade a header in as the sheet expands), reach for the
|
|
94
|
+
* animated value via a future enhancement — not exposed today to keep the
|
|
95
|
+
* surface minimal.
|
|
96
|
+
*/
|
|
97
|
+
export interface BottomSheetContextValue {
|
|
98
|
+
snapIndex: number;
|
|
99
|
+
snapPoints: number[];
|
|
100
|
+
expand: (index?: number) => void;
|
|
101
|
+
collapse: () => void;
|
|
102
|
+
close: () => void;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Access the enclosing `<BottomSheet>`'s state and imperative actions.
|
|
106
|
+
* Must be called from a component rendered inside a `<BottomSheet>` (as
|
|
107
|
+
* `children`, `header`, or `footer`).
|
|
108
|
+
*/
|
|
109
|
+
export declare const useBottomSheet: () => BottomSheetContextValue;
|
|
69
110
|
declare const BottomSheet: React.ForwardRefExoticComponent<BottomSheetProps & React.RefAttributes<BottomSheetRef>>;
|
|
70
111
|
export { BottomSheet };
|
|
71
112
|
export default BottomSheet;
|