@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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = exports.BottomSheet = void 0;
|
|
6
|
+
exports.useBottomSheet = exports.default = exports.BottomSheet = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _reactNativeGestureHandler = require("react-native-gesture-handler");
|
|
@@ -32,6 +32,33 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
32
32
|
* ref.current?.close(); // dismiss
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* State + actions exposed to anything rendered inside a `<BottomSheet>` —
|
|
37
|
+
* including the `header` and `footer` slots. Read via `useBottomSheet()`.
|
|
38
|
+
*
|
|
39
|
+
* `snapIndex` is the JS-thread mirror of the current snap point. -1 means the
|
|
40
|
+
* sheet is closed (mid-close-animation included). Use it to drive footer
|
|
41
|
+
* button enable state, conditional headers, etc. If you need per-frame
|
|
42
|
+
* progress (e.g. fade a header in as the sheet expands), reach for the
|
|
43
|
+
* animated value via a future enhancement — not exposed today to keep the
|
|
44
|
+
* surface minimal.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
const BottomSheetContext = /*#__PURE__*/(0, _react.createContext)(null);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Access the enclosing `<BottomSheet>`'s state and imperative actions.
|
|
51
|
+
* Must be called from a component rendered inside a `<BottomSheet>` (as
|
|
52
|
+
* `children`, `header`, or `footer`).
|
|
53
|
+
*/
|
|
54
|
+
const useBottomSheet = () => {
|
|
55
|
+
const ctx = (0, _react.useContext)(BottomSheetContext);
|
|
56
|
+
if (!ctx) {
|
|
57
|
+
throw new Error('useBottomSheet must be used inside a <BottomSheet>.');
|
|
58
|
+
}
|
|
59
|
+
return ctx;
|
|
60
|
+
};
|
|
61
|
+
exports.useBottomSheet = useBottomSheet;
|
|
35
62
|
const SPRING_CONFIG = {
|
|
36
63
|
damping: 20,
|
|
37
64
|
stiffness: 240,
|
|
@@ -51,6 +78,10 @@ const BottomSheet = exports.BottomSheet = /*#__PURE__*/(0, _react.forwardRef)((p
|
|
|
51
78
|
mode = 'modal',
|
|
52
79
|
handleIndicatorStyle,
|
|
53
80
|
containerStyle,
|
|
81
|
+
header,
|
|
82
|
+
footer,
|
|
83
|
+
headerStyle,
|
|
84
|
+
footerStyle,
|
|
54
85
|
children,
|
|
55
86
|
accessibilityLabel,
|
|
56
87
|
accessibilityViewIsModal,
|
|
@@ -336,6 +367,13 @@ const BottomSheet = exports.BottomSheet = /*#__PURE__*/(0, _react.forwardRef)((p
|
|
|
336
367
|
if (!success) return;
|
|
337
368
|
(0, _reactNativeReanimated.runOnJS)(handleBackdropPress)();
|
|
338
369
|
}), [handleBackdropPress]);
|
|
370
|
+
const contextValue = (0, _react.useMemo)(() => ({
|
|
371
|
+
snapIndex: currentIndex,
|
|
372
|
+
snapPoints: resolvedSnapPoints,
|
|
373
|
+
expand,
|
|
374
|
+
collapse,
|
|
375
|
+
close
|
|
376
|
+
}), [currentIndex, resolvedSnapPoints, expand, collapse, close]);
|
|
339
377
|
|
|
340
378
|
// Don't render the backdrop / sheet tree when the sheet is fully closed.
|
|
341
379
|
// Inline: gated by `inlineMounted` (set in expand, cleared in
|
|
@@ -389,13 +427,23 @@ const BottomSheet = exports.BottomSheet = /*#__PURE__*/(0, _react.forwardRef)((p
|
|
|
389
427
|
backgroundColor: theme.components.bottomSheet?.handleColor ?? theme.colors.border.primary
|
|
390
428
|
}, handleIndicatorStyle]
|
|
391
429
|
})
|
|
430
|
+
}), header != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
431
|
+
style: [styles.header, headerStyle],
|
|
432
|
+
children: header
|
|
392
433
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
393
434
|
style: styles.content,
|
|
394
435
|
children: children
|
|
436
|
+
}), footer != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
437
|
+
style: [styles.footer, footerStyle],
|
|
438
|
+
children: footer
|
|
395
439
|
})]
|
|
396
440
|
})
|
|
397
441
|
})]
|
|
398
442
|
});
|
|
443
|
+
const wrappedTree = /*#__PURE__*/(0, _jsxRuntime.jsx)(BottomSheetContext.Provider, {
|
|
444
|
+
value: contextValue,
|
|
445
|
+
children: sheetTree
|
|
446
|
+
});
|
|
399
447
|
if (mode === 'modal') {
|
|
400
448
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
|
|
401
449
|
transparent: true,
|
|
@@ -410,11 +458,11 @@ const BottomSheet = exports.BottomSheet = /*#__PURE__*/(0, _react.forwardRef)((p
|
|
|
410
458
|
supportedOrientations: ['portrait', 'landscape'],
|
|
411
459
|
children: _reactNative.Platform.OS === 'android' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureHandlerRootView, {
|
|
412
460
|
style: styles.modalRoot,
|
|
413
|
-
children:
|
|
414
|
-
}) :
|
|
461
|
+
children: wrappedTree
|
|
462
|
+
}) : wrappedTree
|
|
415
463
|
});
|
|
416
464
|
}
|
|
417
|
-
return
|
|
465
|
+
return wrappedTree;
|
|
418
466
|
});
|
|
419
467
|
BottomSheet.displayName = 'BottomSheet';
|
|
420
468
|
|
|
@@ -459,6 +507,15 @@ const buildStyles = _theme => _reactNative.StyleSheet.create({
|
|
|
459
507
|
paddingTop: 10,
|
|
460
508
|
paddingBottom: 8
|
|
461
509
|
},
|
|
510
|
+
header: {
|
|
511
|
+
// No opinionated padding — slot owns its own styling. Sits between the
|
|
512
|
+
// drag handle and the scrollable content; does not flex.
|
|
513
|
+
},
|
|
514
|
+
footer: {
|
|
515
|
+
// Pinned to the bottom of the sheet (above safe-area inset, which lives
|
|
516
|
+
// on the sheet's paddingBottom). Does not flex, does not scroll with
|
|
517
|
+
// content. Rides with the keyboard via the sheet's `top` animation.
|
|
518
|
+
},
|
|
462
519
|
handle: {
|
|
463
520
|
width: 36,
|
|
464
521
|
height: 4,
|
|
@@ -15,6 +15,12 @@ Object.defineProperty(exports, "default", {
|
|
|
15
15
|
return _BottomSheet.default;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "useBottomSheet", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _BottomSheet.useBottomSheet;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
18
24
|
var _BottomSheet = _interopRequireWildcard(require("./BottomSheet.js"));
|
|
19
25
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
20
26
|
//# sourceMappingURL=index.js.map
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.resolveVariantColors = exports.resolveFieldTextStyle = exports.resolveFieldSize = exports.default = exports.FieldBase = void 0;
|
|
6
|
+
exports.resolveVariantColors = exports.resolveFieldTextStyle = exports.resolveFieldSize = exports.pickRepresentativeBorderColor = exports.default = exports.FieldBase = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _index = require("../../theme/index.js");
|
|
@@ -33,6 +33,66 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
33
33
|
* - Shake-on-error or any cross-field animation.
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
|
+
const SIDES = ['top', 'right', 'bottom', 'left'];
|
|
37
|
+
|
|
38
|
+
/** Map a single colour string to all four sides (shorthand expansion). */
|
|
39
|
+
const expandColorSides = value => {
|
|
40
|
+
if (typeof value === 'string') {
|
|
41
|
+
return {
|
|
42
|
+
top: value,
|
|
43
|
+
right: value,
|
|
44
|
+
bottom: value,
|
|
45
|
+
left: value
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// Object form: any side the consumer omitted falls through to 'transparent'.
|
|
49
|
+
// Rationale: if a consumer says `{ bottom: '#000' }`, they want only the
|
|
50
|
+
// bottom drawn — the other sides should be invisible, not inherit some
|
|
51
|
+
// unrelated colour.
|
|
52
|
+
return {
|
|
53
|
+
top: value.top ?? 'transparent',
|
|
54
|
+
right: value.right ?? 'transparent',
|
|
55
|
+
bottom: value.bottom ?? 'transparent',
|
|
56
|
+
left: value.left ?? 'transparent'
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** Map a single width number to all four sides (shorthand expansion). */
|
|
61
|
+
const expandWidthSides = value => {
|
|
62
|
+
if (typeof value === 'number') {
|
|
63
|
+
return {
|
|
64
|
+
top: value,
|
|
65
|
+
right: value,
|
|
66
|
+
bottom: value,
|
|
67
|
+
left: value
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
top: value.top ?? 0,
|
|
72
|
+
right: value.right ?? 0,
|
|
73
|
+
bottom: value.bottom ?? 0,
|
|
74
|
+
left: value.left ?? 0
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Are all four sides of a per-side record equal? Used to collapse to shorthand. */
|
|
79
|
+
const allSidesEqual = sides => sides.top === sides.right && sides.right === sides.bottom && sides.bottom === sides.left;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Pick the most "representative" side colour from a 4-side record. Used by
|
|
83
|
+
* consumers (Input's selectionColor, focus rings, etc.) that need a single
|
|
84
|
+
* colour to mirror the visible focus indicator. Priority: bottom → top →
|
|
85
|
+
* left → right, skipping `'transparent'`. The bottom-first order means
|
|
86
|
+
* underline variants pick their underline colour automatically.
|
|
87
|
+
*/
|
|
88
|
+
const pickRepresentativeBorderColor = sides => {
|
|
89
|
+
for (const side of ['bottom', 'top', 'left', 'right']) {
|
|
90
|
+
const c = sides[side];
|
|
91
|
+
if (c && c !== 'transparent') return c;
|
|
92
|
+
}
|
|
93
|
+
return sides.bottom;
|
|
94
|
+
};
|
|
95
|
+
|
|
36
96
|
/**
|
|
37
97
|
* Resolved text styling for the editable / displayed content inside a field.
|
|
38
98
|
* Single source of truth so every field component (Input, NumberInput,
|
|
@@ -41,7 +101,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
41
101
|
* same place. OTPInput intentionally overrides this with its own semibold
|
|
42
102
|
* display weight.
|
|
43
103
|
*/
|
|
44
|
-
|
|
104
|
+
exports.pickRepresentativeBorderColor = pickRepresentativeBorderColor;
|
|
45
105
|
/**
|
|
46
106
|
* Resolve the canonical text style chunk for a field's value. Reads from
|
|
47
107
|
* `theme.components.field.{textColor,disabledTextColor,placeholderColor,fontWeight}`
|
|
@@ -122,31 +182,70 @@ const DEFAULT_SIZES = {
|
|
|
122
182
|
|
|
123
183
|
/**
|
|
124
184
|
* Strict, fully-resolved colour set used internally. Mirrors `FieldVariantTokens`
|
|
125
|
-
* but every
|
|
126
|
-
*
|
|
185
|
+
* but every border colour / width is expanded to a 4-side record by the time
|
|
186
|
+
* the box renders, so the animation pipeline can treat per-side and shorthand
|
|
187
|
+
* the same way. `borderFocusedRepresentative` is a single colour pulled from
|
|
188
|
+
* the focused sides — exposed for consumers (Input's selectionColor) that
|
|
189
|
+
* need to mirror the visible focus indicator without re-resolving per side.
|
|
127
190
|
*/
|
|
128
191
|
|
|
129
192
|
/**
|
|
130
193
|
* Resolve the full set of state-aware fill + border colours for a given
|
|
131
|
-
* variant. Order: explicit override → theme token → library default.
|
|
194
|
+
* variant. Order: explicit override → theme token → library default. Border
|
|
195
|
+
* colour + width are normalized to per-side records here; callers that need
|
|
196
|
+
* a single colour read `borderFocusedRepresentative`.
|
|
132
197
|
*/
|
|
133
198
|
const resolveVariantColors = (theme, variant, override) => {
|
|
134
199
|
const tokenSet = theme.components.field?.[variant];
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
200
|
+
|
|
201
|
+
// Variant-specific defaults for resting fill + border treatment. `outlined`
|
|
202
|
+
// draws all four sides; `filled` draws nothing (fill-only); `underline`
|
|
203
|
+
// draws only the bottom edge — Material-style single-line inputs.
|
|
204
|
+
let idleEmpty;
|
|
205
|
+
let borderIdleDefault;
|
|
206
|
+
let borderWidthDefault;
|
|
207
|
+
if (variant === 'outlined') {
|
|
208
|
+
idleEmpty = theme.colors.background.primary;
|
|
209
|
+
borderIdleDefault = theme.colors.border.primary;
|
|
210
|
+
borderWidthDefault = theme.colors.border.width;
|
|
211
|
+
} else if (variant === 'underline') {
|
|
212
|
+
idleEmpty = 'transparent';
|
|
213
|
+
borderIdleDefault = {
|
|
214
|
+
bottom: theme.colors.border.primary
|
|
215
|
+
};
|
|
216
|
+
borderWidthDefault = {
|
|
217
|
+
bottom: theme.colors.border.width
|
|
218
|
+
};
|
|
219
|
+
} else {
|
|
220
|
+
idleEmpty = theme.colors.background.secondary;
|
|
221
|
+
borderIdleDefault = 'transparent';
|
|
222
|
+
borderWidthDefault = 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// For focused/error/disabled we expand using the same per-side shape as
|
|
226
|
+
// idle. If `borderIdle` ended up bottom-only (underline), an unset
|
|
227
|
+
// `borderFocused` defaults to `{ bottom: colors.border.focus }` so the
|
|
228
|
+
// focus indicator stays on the same edge. Same logic for error.
|
|
229
|
+
const wrapToIdleShape = colour => typeof borderIdleDefault === 'string' ? colour : {
|
|
230
|
+
bottom: colour
|
|
231
|
+
};
|
|
232
|
+
const borderIdle = expandColorSides(override?.borderIdle ?? tokenSet?.borderIdle ?? borderIdleDefault);
|
|
233
|
+
const borderFocused = expandColorSides(override?.borderFocused ?? tokenSet?.borderFocused ?? wrapToIdleShape(theme.colors.border.focus));
|
|
234
|
+
const borderError = expandColorSides(override?.borderError ?? tokenSet?.borderError ?? wrapToIdleShape(theme.colors.border.error));
|
|
235
|
+
const borderDisabled = expandColorSides(override?.borderDisabled ?? tokenSet?.borderDisabled ?? borderIdleDefault);
|
|
236
|
+
const borderWidth = expandWidthSides(override?.borderWidth ?? tokenSet?.borderWidth ?? borderWidthDefault);
|
|
139
237
|
return {
|
|
140
238
|
backgroundIdleEmpty: override?.backgroundIdleEmpty ?? tokenSet?.backgroundIdleEmpty ?? idleEmpty,
|
|
141
239
|
backgroundIdleFilled: override?.backgroundIdleFilled ?? tokenSet?.backgroundIdleFilled ?? override?.backgroundIdleEmpty ?? tokenSet?.backgroundIdleEmpty ?? idleEmpty,
|
|
142
240
|
backgroundFocused: override?.backgroundFocused ?? tokenSet?.backgroundFocused ?? undefined,
|
|
143
241
|
backgroundError: override?.backgroundError ?? tokenSet?.backgroundError ?? undefined,
|
|
144
242
|
backgroundDisabled: override?.backgroundDisabled ?? tokenSet?.backgroundDisabled ?? theme.colors.surface.disabled,
|
|
145
|
-
borderIdle
|
|
146
|
-
borderFocused
|
|
147
|
-
borderError
|
|
148
|
-
borderDisabled
|
|
149
|
-
borderWidth
|
|
243
|
+
borderIdle,
|
|
244
|
+
borderFocused,
|
|
245
|
+
borderError,
|
|
246
|
+
borderDisabled,
|
|
247
|
+
borderWidth,
|
|
248
|
+
borderFocusedRepresentative: pickRepresentativeBorderColor(borderFocused)
|
|
150
249
|
};
|
|
151
250
|
};
|
|
152
251
|
exports.resolveVariantColors = resolveVariantColors;
|
|
@@ -169,7 +268,15 @@ const FieldBase = props => {
|
|
|
169
268
|
paddingHorizontal: paddingHorizontalProp,
|
|
170
269
|
paddingVertical: paddingVerticalProp,
|
|
171
270
|
borderRadius: borderRadiusProp,
|
|
271
|
+
borderTopLeftRadius: borderTopLeftRadiusProp,
|
|
272
|
+
borderTopRightRadius: borderTopRightRadiusProp,
|
|
273
|
+
borderBottomLeftRadius: borderBottomLeftRadiusProp,
|
|
274
|
+
borderBottomRightRadius: borderBottomRightRadiusProp,
|
|
172
275
|
borderWidth: borderWidthProp,
|
|
276
|
+
borderTopWidth: borderTopWidthProp,
|
|
277
|
+
borderRightWidth: borderRightWidthProp,
|
|
278
|
+
borderBottomWidth: borderBottomWidthProp,
|
|
279
|
+
borderLeftWidth: borderLeftWidthProp,
|
|
173
280
|
gap: gapProp,
|
|
174
281
|
fillOverrides,
|
|
175
282
|
style,
|
|
@@ -208,13 +315,6 @@ const FieldBase = props => {
|
|
|
208
315
|
const idleFill = filled ? colors.backgroundIdleFilled : colors.backgroundIdleEmpty;
|
|
209
316
|
const focusedFill = colors.backgroundFocused ?? idleFill;
|
|
210
317
|
const errorFill = colors.backgroundError ?? idleFill;
|
|
211
|
-
const animatedBorderColor = disabled ? colors.borderDisabled : error ? errorAnim.interpolate({
|
|
212
|
-
inputRange: [0, 1],
|
|
213
|
-
outputRange: [colors.borderIdle, colors.borderError]
|
|
214
|
-
}) : focusAnim.interpolate({
|
|
215
|
-
inputRange: [0, 1],
|
|
216
|
-
outputRange: [colors.borderIdle, colors.borderFocused]
|
|
217
|
-
});
|
|
218
318
|
const animatedBackgroundColor = disabled ? colors.backgroundDisabled : error ? errorAnim.interpolate({
|
|
219
319
|
inputRange: [0, 1],
|
|
220
320
|
outputRange: [idleFill, errorFill]
|
|
@@ -222,17 +322,105 @@ const FieldBase = props => {
|
|
|
222
322
|
inputRange: [0, 1],
|
|
223
323
|
outputRange: [idleFill, focusedFill]
|
|
224
324
|
});
|
|
325
|
+
|
|
326
|
+
// Per-side border colour. For each side we resolve the "from" (idle) and
|
|
327
|
+
// "to" (focused or error, depending on state) colour, then drive a single
|
|
328
|
+
// animated interpolation. When all four sides share the same from+to pair
|
|
329
|
+
// we collapse to one `borderColor` key — the common case stays cheap.
|
|
330
|
+
const activeColors = error ? colors.borderError : colors.borderFocused;
|
|
331
|
+
const activeAnim = error ? errorAnim : focusAnim;
|
|
332
|
+
const sideFrom = disabled ? colors.borderDisabled : colors.borderIdle;
|
|
333
|
+
const sideTo = disabled ? colors.borderDisabled : activeColors;
|
|
334
|
+
|
|
335
|
+
// Per-side widths: longhand prop wins over shorthand prop wins over
|
|
336
|
+
// resolved variant token. Each side independently.
|
|
337
|
+
const propWidth = {
|
|
338
|
+
top: borderTopWidthProp,
|
|
339
|
+
right: borderRightWidthProp,
|
|
340
|
+
bottom: borderBottomWidthProp,
|
|
341
|
+
left: borderLeftWidthProp
|
|
342
|
+
};
|
|
343
|
+
const resolvedWidths = {
|
|
344
|
+
top: propWidth.top ?? borderWidthProp ?? colors.borderWidth.top,
|
|
345
|
+
right: propWidth.right ?? borderWidthProp ?? colors.borderWidth.right,
|
|
346
|
+
bottom: propWidth.bottom ?? borderWidthProp ?? colors.borderWidth.bottom,
|
|
347
|
+
left: propWidth.left ?? borderWidthProp ?? colors.borderWidth.left
|
|
348
|
+
};
|
|
225
349
|
const boxStyle = {
|
|
226
350
|
minHeight: height ?? minHeightProp ?? sizeTokens.minHeight,
|
|
227
351
|
maxHeight,
|
|
228
352
|
paddingHorizontal: paddingHorizontalProp ?? sizeTokens.paddingHorizontal,
|
|
229
353
|
paddingVertical: paddingVerticalProp ?? sizeTokens.paddingVertical,
|
|
230
|
-
borderRadius: borderRadiusProp ?? sizeTokens.borderRadius,
|
|
231
|
-
borderWidth: borderWidthProp ?? colors.borderWidth,
|
|
232
|
-
borderColor: animatedBorderColor,
|
|
233
354
|
backgroundColor: animatedBackgroundColor,
|
|
234
355
|
columnGap: gapProp ?? theme.spacing.sm
|
|
235
356
|
};
|
|
357
|
+
|
|
358
|
+
// Radius: per-corner prop wins over shorthand prop wins over size token.
|
|
359
|
+
// Collapse to single `borderRadius` when all four corners match.
|
|
360
|
+
const radiusShorthand = borderRadiusProp ?? sizeTokens.borderRadius;
|
|
361
|
+
const corners = {
|
|
362
|
+
tl: borderTopLeftRadiusProp ?? radiusShorthand,
|
|
363
|
+
tr: borderTopRightRadiusProp ?? radiusShorthand,
|
|
364
|
+
bl: borderBottomLeftRadiusProp ?? radiusShorthand,
|
|
365
|
+
br: borderBottomRightRadiusProp ?? radiusShorthand
|
|
366
|
+
};
|
|
367
|
+
if (corners.tl === corners.tr && corners.tr === corners.bl && corners.bl === corners.br) {
|
|
368
|
+
boxStyle.borderRadius = corners.tl;
|
|
369
|
+
} else {
|
|
370
|
+
boxStyle.borderTopLeftRadius = corners.tl;
|
|
371
|
+
boxStyle.borderTopRightRadius = corners.tr;
|
|
372
|
+
boxStyle.borderBottomLeftRadius = corners.bl;
|
|
373
|
+
boxStyle.borderBottomRightRadius = corners.br;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Width: collapse to single `borderWidth` when all four sides match.
|
|
377
|
+
if (resolvedWidths.top === resolvedWidths.right && resolvedWidths.right === resolvedWidths.bottom && resolvedWidths.bottom === resolvedWidths.left) {
|
|
378
|
+
boxStyle.borderWidth = resolvedWidths.top;
|
|
379
|
+
} else {
|
|
380
|
+
boxStyle.borderTopWidth = resolvedWidths.top;
|
|
381
|
+
boxStyle.borderRightWidth = resolvedWidths.right;
|
|
382
|
+
boxStyle.borderBottomWidth = resolvedWidths.bottom;
|
|
383
|
+
boxStyle.borderLeftWidth = resolvedWidths.left;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Colour: collapse to single `borderColor` only when both endpoints are
|
|
387
|
+
// identical across all sides. Otherwise emit four `borderXColor`
|
|
388
|
+
// interpolations. Disabled state is static (no animation needed).
|
|
389
|
+
const fromAllEqual = allSidesEqual(sideFrom);
|
|
390
|
+
const toAllEqual = allSidesEqual(sideTo);
|
|
391
|
+
if (disabled) {
|
|
392
|
+
if (fromAllEqual) {
|
|
393
|
+
boxStyle.borderColor = sideFrom.top;
|
|
394
|
+
} else {
|
|
395
|
+
boxStyle.borderTopColor = sideFrom.top;
|
|
396
|
+
boxStyle.borderRightColor = sideFrom.right;
|
|
397
|
+
boxStyle.borderBottomColor = sideFrom.bottom;
|
|
398
|
+
boxStyle.borderLeftColor = sideFrom.left;
|
|
399
|
+
}
|
|
400
|
+
} else if (fromAllEqual && toAllEqual && sideFrom.top === sideFrom.right && sideTo.top === sideTo.right) {
|
|
401
|
+
// Common case: shorthand on both ends → single interpolation.
|
|
402
|
+
boxStyle.borderColor = activeAnim.interpolate({
|
|
403
|
+
inputRange: [0, 1],
|
|
404
|
+
outputRange: [sideFrom.top, sideTo.top]
|
|
405
|
+
});
|
|
406
|
+
} else {
|
|
407
|
+
boxStyle.borderTopColor = activeAnim.interpolate({
|
|
408
|
+
inputRange: [0, 1],
|
|
409
|
+
outputRange: [sideFrom.top, sideTo.top]
|
|
410
|
+
});
|
|
411
|
+
boxStyle.borderRightColor = activeAnim.interpolate({
|
|
412
|
+
inputRange: [0, 1],
|
|
413
|
+
outputRange: [sideFrom.right, sideTo.right]
|
|
414
|
+
});
|
|
415
|
+
boxStyle.borderBottomColor = activeAnim.interpolate({
|
|
416
|
+
inputRange: [0, 1],
|
|
417
|
+
outputRange: [sideFrom.bottom, sideTo.bottom]
|
|
418
|
+
});
|
|
419
|
+
boxStyle.borderLeftColor = activeAnim.interpolate({
|
|
420
|
+
inputRange: [0, 1],
|
|
421
|
+
outputRange: [sideFrom.left, sideTo.left]
|
|
422
|
+
});
|
|
423
|
+
}
|
|
236
424
|
if (width !== undefined) boxStyle.width = width;
|
|
237
425
|
if (height !== undefined) boxStyle.height = height;
|
|
238
426
|
const a11yState = {
|
|
@@ -168,7 +168,7 @@ const Input = exports.Input = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =
|
|
|
168
168
|
// Selection / caret / handle colour walks the same resolution chain as the
|
|
169
169
|
// focused border so brands that override `components.field.<variant>.borderFocused`
|
|
170
170
|
// get a matching selection tint without also having to update root `border.focus`.
|
|
171
|
-
const selectionColor = (0, _react.useMemo)(() => (0, _FieldBase.resolveVariantColors)(theme, variant, fillOverrides).
|
|
171
|
+
const selectionColor = (0, _react.useMemo)(() => (0, _FieldBase.resolveVariantColors)(theme, variant, fillOverrides).borderFocusedRepresentative, [theme, variant, fillOverrides]);
|
|
172
172
|
const borderRadiusOverride = borderRadiusProp ?? theme.components.input?.borderRadius;
|
|
173
173
|
|
|
174
174
|
// Floating label styles
|
|
@@ -40,7 +40,13 @@ const SearchBar = exports.SearchBar = /*#__PURE__*/(0, _react.forwardRef)((props
|
|
|
40
40
|
// field-family token (`components.field.defaultVariant`) → library default.
|
|
41
41
|
// Library default stays `'filled'` so SearchBar reads as the traditional
|
|
42
42
|
// pill-shaped, borderless search box when no theme opinion is expressed.
|
|
43
|
-
|
|
43
|
+
// SearchBar deliberately only supports 'filled' and 'outlined' — an
|
|
44
|
+
// underline-only pill makes no visual sense. If the shared field default is
|
|
45
|
+
// 'underline', fall through to 'filled' rather than carry an unsupported
|
|
46
|
+
// value down to the variant token lookup.
|
|
47
|
+
const sharedDefault = theme.components.field?.defaultVariant;
|
|
48
|
+
const sharedFallback = sharedDefault === 'outlined' || sharedDefault === 'filled' ? sharedDefault : 'filled';
|
|
49
|
+
const variant = variantProp ?? theme.components.searchBar?.defaultVariant ?? sharedFallback;
|
|
44
50
|
const fieldTokens = (0, _FieldBase.resolveFieldSize)(theme, size);
|
|
45
51
|
const sizeStyles = {
|
|
46
52
|
...fieldTokens,
|
|
@@ -184,7 +190,7 @@ const SearchBar = exports.SearchBar = /*#__PURE__*/(0, _react.forwardRef)((props
|
|
|
184
190
|
autoFocus: autoFocus,
|
|
185
191
|
editable: !disabled,
|
|
186
192
|
returnKeyType: "search",
|
|
187
|
-
selectionColor: (0, _FieldBase.resolveVariantColors)(theme, variant).
|
|
193
|
+
selectionColor: (0, _FieldBase.resolveVariantColors)(theme, variant).borderFocusedRepresentative,
|
|
188
194
|
accessibilityLabel: accessibilityLabel ?? placeholder,
|
|
189
195
|
accessibilityState: {
|
|
190
196
|
disabled
|
|
@@ -326,7 +326,7 @@ const Select = exports.Select = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
|
|
|
326
326
|
onChangeText: setQuery,
|
|
327
327
|
placeholder: "Search\u2026",
|
|
328
328
|
placeholderTextColor: theme.colors.text.tertiary,
|
|
329
|
-
selectionColor: (0, _FieldBase.resolveVariantColors)(theme, theme.components.field?.defaultVariant ?? 'outlined').
|
|
329
|
+
selectionColor: (0, _FieldBase.resolveVariantColors)(theme, theme.components.field?.defaultVariant ?? 'outlined').borderFocusedRepresentative,
|
|
330
330
|
accessibilityLabel: "Search options",
|
|
331
331
|
style: [styles.searchInput, {
|
|
332
332
|
color: theme.colors.text.primary,
|
|
@@ -345,6 +345,12 @@ Object.defineProperty(exports, "toast", {
|
|
|
345
345
|
return _index41.toast;
|
|
346
346
|
}
|
|
347
347
|
});
|
|
348
|
+
Object.defineProperty(exports, "useBottomSheet", {
|
|
349
|
+
enumerable: true,
|
|
350
|
+
get: function () {
|
|
351
|
+
return _index6.useBottomSheet;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
348
354
|
Object.defineProperty(exports, "useReduceMotion", {
|
|
349
355
|
enumerable: true,
|
|
350
356
|
get: function () {
|
|
@@ -19,14 +19,40 @@
|
|
|
19
19
|
* ref.current?.close(); // dismiss
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
22
|
+
import React, { createContext, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
23
23
|
import { Dimensions, Keyboard, Modal, Platform, StyleSheet, View } from 'react-native';
|
|
24
24
|
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
25
25
|
import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
|
|
26
26
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
27
27
|
import { useTheme } from "../../theme/index.js";
|
|
28
28
|
import { triggerHaptic } from "../../utils/index.js";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* State + actions exposed to anything rendered inside a `<BottomSheet>` —
|
|
32
|
+
* including the `header` and `footer` slots. Read via `useBottomSheet()`.
|
|
33
|
+
*
|
|
34
|
+
* `snapIndex` is the JS-thread mirror of the current snap point. -1 means the
|
|
35
|
+
* sheet is closed (mid-close-animation included). Use it to drive footer
|
|
36
|
+
* button enable state, conditional headers, etc. If you need per-frame
|
|
37
|
+
* progress (e.g. fade a header in as the sheet expands), reach for the
|
|
38
|
+
* animated value via a future enhancement — not exposed today to keep the
|
|
39
|
+
* surface minimal.
|
|
40
|
+
*/
|
|
29
41
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
42
|
+
const BottomSheetContext = /*#__PURE__*/createContext(null);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Access the enclosing `<BottomSheet>`'s state and imperative actions.
|
|
46
|
+
* Must be called from a component rendered inside a `<BottomSheet>` (as
|
|
47
|
+
* `children`, `header`, or `footer`).
|
|
48
|
+
*/
|
|
49
|
+
export const useBottomSheet = () => {
|
|
50
|
+
const ctx = useContext(BottomSheetContext);
|
|
51
|
+
if (!ctx) {
|
|
52
|
+
throw new Error('useBottomSheet must be used inside a <BottomSheet>.');
|
|
53
|
+
}
|
|
54
|
+
return ctx;
|
|
55
|
+
};
|
|
30
56
|
const SPRING_CONFIG = {
|
|
31
57
|
damping: 20,
|
|
32
58
|
stiffness: 240,
|
|
@@ -46,6 +72,10 @@ const BottomSheet = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
46
72
|
mode = 'modal',
|
|
47
73
|
handleIndicatorStyle,
|
|
48
74
|
containerStyle,
|
|
75
|
+
header,
|
|
76
|
+
footer,
|
|
77
|
+
headerStyle,
|
|
78
|
+
footerStyle,
|
|
49
79
|
children,
|
|
50
80
|
accessibilityLabel,
|
|
51
81
|
accessibilityViewIsModal,
|
|
@@ -331,6 +361,13 @@ const BottomSheet = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
331
361
|
if (!success) return;
|
|
332
362
|
runOnJS(handleBackdropPress)();
|
|
333
363
|
}), [handleBackdropPress]);
|
|
364
|
+
const contextValue = useMemo(() => ({
|
|
365
|
+
snapIndex: currentIndex,
|
|
366
|
+
snapPoints: resolvedSnapPoints,
|
|
367
|
+
expand,
|
|
368
|
+
collapse,
|
|
369
|
+
close
|
|
370
|
+
}), [currentIndex, resolvedSnapPoints, expand, collapse, close]);
|
|
334
371
|
|
|
335
372
|
// Don't render the backdrop / sheet tree when the sheet is fully closed.
|
|
336
373
|
// Inline: gated by `inlineMounted` (set in expand, cleared in
|
|
@@ -384,13 +421,23 @@ const BottomSheet = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
384
421
|
backgroundColor: theme.components.bottomSheet?.handleColor ?? theme.colors.border.primary
|
|
385
422
|
}, handleIndicatorStyle]
|
|
386
423
|
})
|
|
424
|
+
}), header != null && /*#__PURE__*/_jsx(View, {
|
|
425
|
+
style: [styles.header, headerStyle],
|
|
426
|
+
children: header
|
|
387
427
|
}), /*#__PURE__*/_jsx(View, {
|
|
388
428
|
style: styles.content,
|
|
389
429
|
children: children
|
|
430
|
+
}), footer != null && /*#__PURE__*/_jsx(View, {
|
|
431
|
+
style: [styles.footer, footerStyle],
|
|
432
|
+
children: footer
|
|
390
433
|
})]
|
|
391
434
|
})
|
|
392
435
|
})]
|
|
393
436
|
});
|
|
437
|
+
const wrappedTree = /*#__PURE__*/_jsx(BottomSheetContext.Provider, {
|
|
438
|
+
value: contextValue,
|
|
439
|
+
children: sheetTree
|
|
440
|
+
});
|
|
394
441
|
if (mode === 'modal') {
|
|
395
442
|
return /*#__PURE__*/_jsx(Modal, {
|
|
396
443
|
transparent: true,
|
|
@@ -405,11 +452,11 @@ const BottomSheet = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
405
452
|
supportedOrientations: ['portrait', 'landscape'],
|
|
406
453
|
children: Platform.OS === 'android' ? /*#__PURE__*/_jsx(GestureHandlerRootView, {
|
|
407
454
|
style: styles.modalRoot,
|
|
408
|
-
children:
|
|
409
|
-
}) :
|
|
455
|
+
children: wrappedTree
|
|
456
|
+
}) : wrappedTree
|
|
410
457
|
});
|
|
411
458
|
}
|
|
412
|
-
return
|
|
459
|
+
return wrappedTree;
|
|
413
460
|
});
|
|
414
461
|
BottomSheet.displayName = 'BottomSheet';
|
|
415
462
|
|
|
@@ -454,6 +501,15 @@ const buildStyles = _theme => StyleSheet.create({
|
|
|
454
501
|
paddingTop: 10,
|
|
455
502
|
paddingBottom: 8
|
|
456
503
|
},
|
|
504
|
+
header: {
|
|
505
|
+
// No opinionated padding — slot owns its own styling. Sits between the
|
|
506
|
+
// drag handle and the scrollable content; does not flex.
|
|
507
|
+
},
|
|
508
|
+
footer: {
|
|
509
|
+
// Pinned to the bottom of the sheet (above safe-area inset, which lives
|
|
510
|
+
// on the sheet's paddingBottom). Does not flex, does not scroll with
|
|
511
|
+
// content. Rides with the keyboard via the sheet's `top` animation.
|
|
512
|
+
},
|
|
457
513
|
handle: {
|
|
458
514
|
width: 36,
|
|
459
515
|
height: 4,
|