@dloizides/ui-forms 1.9.1 → 1.12.0
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/CHANGELOG.md +327 -280
- package/dist/index.d.mts +79 -3
- package/dist/index.d.ts +79 -3
- package/dist/index.js +240 -166
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +216 -142
- package/dist/index.mjs.map +1 -1
- package/package.json +110 -107
package/dist/index.js
CHANGED
|
@@ -1,25 +1,63 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React2 = require('react');
|
|
4
4
|
var reactNative = require('react-native');
|
|
5
5
|
var uiFeedback = require('@dloizides/ui-feedback');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
7
|
var uiButtons = require('@dloizides/ui-buttons');
|
|
8
|
+
var a11y = require('@dloizides/a11y');
|
|
8
9
|
|
|
9
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
11
|
|
|
11
|
-
var
|
|
12
|
+
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
12
13
|
|
|
13
14
|
// src/Field/Field.tsx
|
|
15
|
+
function attachControlId(child, candidateId, describedById) {
|
|
16
|
+
if (!React2__default.default.isValidElement(child) || child.type === React2__default.default.Fragment) {
|
|
17
|
+
return { node: child };
|
|
18
|
+
}
|
|
19
|
+
const props = child.props;
|
|
20
|
+
const existing = props.id ?? props.nativeID;
|
|
21
|
+
const element = child;
|
|
22
|
+
const controlId = existing !== void 0 && existing !== "" ? existing : candidateId;
|
|
23
|
+
const injected = { id: controlId };
|
|
24
|
+
if (describedById !== void 0 && props.describedById === void 0) {
|
|
25
|
+
injected.describedById = describedById;
|
|
26
|
+
}
|
|
27
|
+
return { node: React2__default.default.cloneElement(element, injected), controlId };
|
|
28
|
+
}
|
|
29
|
+
var IS_WEB = reactNative.Platform.OS === "web";
|
|
30
|
+
function isAttributeHost(node) {
|
|
31
|
+
return typeof node === "object" && node !== null && typeof node.setAttribute === "function";
|
|
32
|
+
}
|
|
33
|
+
function useHtmlFor(controlId) {
|
|
34
|
+
const ref = React2.useRef(null);
|
|
35
|
+
React2.useEffect(() => {
|
|
36
|
+
const node = ref.current;
|
|
37
|
+
if (!IS_WEB || !isAttributeHost(node)) return;
|
|
38
|
+
const target = controlId !== void 0 && controlId !== "" ? node.ownerDocument?.getElementById(controlId) : null;
|
|
39
|
+
if (target === null || target === void 0) {
|
|
40
|
+
node.removeAttribute("for");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
node.setAttribute("for", controlId);
|
|
44
|
+
}, [controlId]);
|
|
45
|
+
return ref;
|
|
46
|
+
}
|
|
47
|
+
var IS_WEB2 = reactNative.Platform.OS === "web";
|
|
14
48
|
function FieldLabel({
|
|
15
49
|
label,
|
|
16
50
|
marker,
|
|
17
51
|
optionalLabel,
|
|
52
|
+
controlId,
|
|
53
|
+
labelId,
|
|
18
54
|
style,
|
|
19
55
|
requiredMarkStyle,
|
|
20
56
|
optionalMarkStyle
|
|
21
57
|
}) {
|
|
22
|
-
|
|
58
|
+
const webProps = IS_WEB2 ? { role: "label" } : {};
|
|
59
|
+
const labelRef = useHtmlFor(controlId);
|
|
60
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { id: labelId, ref: labelRef, style, ...webProps, children: [
|
|
23
61
|
label,
|
|
24
62
|
" ",
|
|
25
63
|
marker === "required" ? (
|
|
@@ -96,15 +134,15 @@ var labelVariantStyles = reactNative.StyleSheet.create({
|
|
|
96
134
|
function resolveLabelVariantStyle(variant) {
|
|
97
135
|
return variant === "control" ? labelVariantStyles.control : labelVariantStyles.field;
|
|
98
136
|
}
|
|
99
|
-
var FormGridContext =
|
|
100
|
-
var GapOwnedContext =
|
|
137
|
+
var FormGridContext = React2__default.default.createContext(null);
|
|
138
|
+
var GapOwnedContext = React2__default.default.createContext(false);
|
|
101
139
|
var FormGridProvider = FormGridContext.Provider;
|
|
102
140
|
var GapOwnedProvider = GapOwnedContext.Provider;
|
|
103
141
|
function useFormGridMetrics() {
|
|
104
|
-
return
|
|
142
|
+
return React2__default.default.useContext(FormGridContext);
|
|
105
143
|
}
|
|
106
144
|
function useIsGapOwned() {
|
|
107
|
-
return
|
|
145
|
+
return React2__default.default.useContext(GapOwnedContext);
|
|
108
146
|
}
|
|
109
147
|
var ERROR_FONT_SIZE = 12;
|
|
110
148
|
var HINT_FONT_SIZE = 12;
|
|
@@ -122,10 +160,15 @@ var styles = reactNative.StyleSheet.create({
|
|
|
122
160
|
});
|
|
123
161
|
var fieldSeq = 0;
|
|
124
162
|
function useFieldIds() {
|
|
125
|
-
const [ids] =
|
|
163
|
+
const [ids] = React2__default.default.useState(() => {
|
|
126
164
|
fieldSeq += 1;
|
|
127
165
|
const seq = String(fieldSeq);
|
|
128
|
-
return {
|
|
166
|
+
return {
|
|
167
|
+
hintId: `field-hint-${seq}`,
|
|
168
|
+
errorId: `field-error-${seq}`,
|
|
169
|
+
controlId: `field-control-${seq}`,
|
|
170
|
+
labelId: `field-label-${seq}`
|
|
171
|
+
};
|
|
129
172
|
});
|
|
130
173
|
return ids;
|
|
131
174
|
}
|
|
@@ -163,8 +206,8 @@ var Field = ({
|
|
|
163
206
|
const labelText = label ?? "";
|
|
164
207
|
const hasLabel = labelText !== "";
|
|
165
208
|
const marker = resolveFieldMarker(required, optional);
|
|
166
|
-
const { hintId, errorId } = useFieldIds();
|
|
167
|
-
const themeStyles =
|
|
209
|
+
const { hintId, errorId, controlId, labelId } = useFieldIds();
|
|
210
|
+
const themeStyles = React2__default.default.useMemo(
|
|
168
211
|
() => ({
|
|
169
212
|
label: { color: colors.textSecondary },
|
|
170
213
|
requiredMark: { color: errorColor },
|
|
@@ -176,12 +219,16 @@ var Field = ({
|
|
|
176
219
|
[colors.textSecondary, errorColor]
|
|
177
220
|
);
|
|
178
221
|
const describedById = joinDescribedBy(hasHint ? hintId : void 0, hasError ? errorId : void 0);
|
|
179
|
-
const
|
|
222
|
+
const isRenderChild = typeof children === "function";
|
|
223
|
+
const attached = isRenderChild ? { node: children({ describedById, hasError, controlId, labelId }), controlId } : attachControlId(children, controlId, describedById);
|
|
224
|
+
const control = attached.node;
|
|
180
225
|
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [spacingStyles.container, containerStyle], testID, children: [
|
|
181
226
|
hasLabel ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
182
227
|
FieldLabel,
|
|
183
228
|
{
|
|
229
|
+
controlId: attached.controlId,
|
|
184
230
|
label: labelText,
|
|
231
|
+
labelId,
|
|
185
232
|
marker,
|
|
186
233
|
optionalLabel,
|
|
187
234
|
optionalMarkStyle: [styles.optionalMark, themeStyles.optionalMark],
|
|
@@ -211,7 +258,7 @@ var FormGrid = ({
|
|
|
211
258
|
style,
|
|
212
259
|
testID
|
|
213
260
|
}) => {
|
|
214
|
-
const metrics =
|
|
261
|
+
const metrics = React2__default.default.useMemo(() => ({ minWidth, basis }), [minWidth, basis]);
|
|
215
262
|
return /* @__PURE__ */ jsxRuntime.jsx(FormGridProvider, { value: metrics, children: /* @__PURE__ */ jsxRuntime.jsx(GapOwnedProvider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.grid, { gap }, style], testID, children }) }) });
|
|
216
263
|
};
|
|
217
264
|
var GROW = 1;
|
|
@@ -246,7 +293,7 @@ var styles3 = reactNative.StyleSheet.create({
|
|
|
246
293
|
});
|
|
247
294
|
var sectionSeq = 0;
|
|
248
295
|
function useSectionLegendId() {
|
|
249
|
-
const [id] =
|
|
296
|
+
const [id] = React2__default.default.useState(() => {
|
|
250
297
|
sectionSeq += 1;
|
|
251
298
|
return `form-section-legend-${String(sectionSeq)}`;
|
|
252
299
|
});
|
|
@@ -333,7 +380,90 @@ var FormActions = ({
|
|
|
333
380
|
}
|
|
334
381
|
)
|
|
335
382
|
] });
|
|
336
|
-
var
|
|
383
|
+
var FIELD_GAP = 4;
|
|
384
|
+
var LABEL_FONT = 11;
|
|
385
|
+
var LABEL_LETTER_SPACING = 0.4;
|
|
386
|
+
var INPUT_RADIUS = 8;
|
|
387
|
+
var INPUT_PAD_H = 12;
|
|
388
|
+
var INPUT_PAD_V = 10;
|
|
389
|
+
var INPUT_FONT = 14;
|
|
390
|
+
var INPUT_BORDER = 1;
|
|
391
|
+
var DATE_ROW_GAP = 8;
|
|
392
|
+
var CHEVRON_FONT = 10;
|
|
393
|
+
var MENU_TOP_GAP = 4;
|
|
394
|
+
var MENU_RADIUS = 8;
|
|
395
|
+
var MENU_PAD_V = 4;
|
|
396
|
+
var MENU_OPT_PAD_H = 12;
|
|
397
|
+
var MENU_OPT_PAD_V = 8;
|
|
398
|
+
var MENU_MIN_WIDTH = 160;
|
|
399
|
+
var MENU_MAX_HEIGHT = 260;
|
|
400
|
+
var MENU_Z = 1e3;
|
|
401
|
+
var MENU_ELEVATION = 8;
|
|
402
|
+
var MENU_BOX_SHADOW = "0px 2px 8px rgba(0, 0, 0, 0.15)";
|
|
403
|
+
var OPTION_FONT = 14;
|
|
404
|
+
var ERROR_FONT = 12;
|
|
405
|
+
var ERROR_GAP = 3;
|
|
406
|
+
var controlStyles = reactNative.StyleSheet.create({
|
|
407
|
+
/**
|
|
408
|
+
* The shared bordered box every dense text control renders in.
|
|
409
|
+
*
|
|
410
|
+
* `minHeight` is the kit floor: padding alone produced a ~38px box, and a bare input with no
|
|
411
|
+
* box style at all measured **19px** in production. Height is a hit-target guarantee, so it
|
|
412
|
+
* is stated rather than left to emerge from font size + padding.
|
|
413
|
+
*/
|
|
414
|
+
input: {
|
|
415
|
+
borderWidth: INPUT_BORDER,
|
|
416
|
+
borderRadius: INPUT_RADIUS,
|
|
417
|
+
paddingHorizontal: INPUT_PAD_H,
|
|
418
|
+
paddingVertical: INPUT_PAD_V,
|
|
419
|
+
fontSize: INPUT_FONT,
|
|
420
|
+
minHeight: a11y.MIN_TOUCH_TARGET_PX
|
|
421
|
+
},
|
|
422
|
+
/** The select trigger: a bordered field box matching the text inputs + a chevron. */
|
|
423
|
+
selectTrigger: {
|
|
424
|
+
minHeight: a11y.MIN_TOUCH_TARGET_PX,
|
|
425
|
+
flexDirection: "row",
|
|
426
|
+
alignItems: "center",
|
|
427
|
+
justifyContent: "space-between",
|
|
428
|
+
gap: DATE_ROW_GAP,
|
|
429
|
+
borderWidth: INPUT_BORDER,
|
|
430
|
+
borderRadius: INPUT_RADIUS,
|
|
431
|
+
paddingHorizontal: INPUT_PAD_H,
|
|
432
|
+
paddingVertical: INPUT_PAD_V
|
|
433
|
+
},
|
|
434
|
+
selectTriggerText: { fontSize: INPUT_FONT, flexShrink: 1 },
|
|
435
|
+
chevron: { fontSize: CHEVRON_FONT },
|
|
436
|
+
dateRow: { flexDirection: "row", gap: DATE_ROW_GAP },
|
|
437
|
+
dateCol: { gap: FIELD_GAP, flex: 1 },
|
|
438
|
+
/**
|
|
439
|
+
* The micro-caption above a sub-control (a date range's From/To) and beside a switch. Note it
|
|
440
|
+
* is deliberately NOT the `control` label voice: it carries no `700`/uppercase, because it
|
|
441
|
+
* names a HALF of a control that already has a `control`-voice label above it. Uppercasing it
|
|
442
|
+
* too would make one field shout twice.
|
|
443
|
+
*/
|
|
444
|
+
subLabel: { fontSize: LABEL_FONT, letterSpacing: LABEL_LETTER_SPACING },
|
|
445
|
+
/** Positioning context for a floating `AnchoredMenu` (`position:absolute; top:100%`). */
|
|
446
|
+
anchor: { position: "relative" },
|
|
447
|
+
menu: {
|
|
448
|
+
position: "absolute",
|
|
449
|
+
top: "100%",
|
|
450
|
+
left: 0,
|
|
451
|
+
marginTop: MENU_TOP_GAP,
|
|
452
|
+
minWidth: MENU_MIN_WIDTH,
|
|
453
|
+
maxHeight: MENU_MAX_HEIGHT,
|
|
454
|
+
borderWidth: INPUT_BORDER,
|
|
455
|
+
borderRadius: MENU_RADIUS,
|
|
456
|
+
paddingVertical: MENU_PAD_V,
|
|
457
|
+
zIndex: MENU_Z,
|
|
458
|
+
boxShadow: MENU_BOX_SHADOW,
|
|
459
|
+
elevation: MENU_ELEVATION,
|
|
460
|
+
overflow: "hidden"
|
|
461
|
+
},
|
|
462
|
+
option: { paddingHorizontal: MENU_OPT_PAD_H, paddingVertical: MENU_OPT_PAD_V },
|
|
463
|
+
optionText: { fontSize: OPTION_FONT },
|
|
464
|
+
error: { fontSize: ERROR_FONT, marginTop: ERROR_GAP }
|
|
465
|
+
});
|
|
466
|
+
var IS_WEB3 = reactNative.Platform.OS === "web";
|
|
337
467
|
var TRANSITION_PROPERTY = "border-color, background-color, box-shadow";
|
|
338
468
|
var TRANSITION_DURATION = "0.14s";
|
|
339
469
|
var TRANSITION_TIMING = "ease";
|
|
@@ -348,7 +478,7 @@ var GREEN_START = 3;
|
|
|
348
478
|
var BLUE_START = 5;
|
|
349
479
|
var CHANNEL_END_OFFSET = 2;
|
|
350
480
|
function prefersReducedMotion() {
|
|
351
|
-
if (!
|
|
481
|
+
if (!IS_WEB3 || typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
352
482
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
353
483
|
}
|
|
354
484
|
function withAlpha(color, alpha) {
|
|
@@ -365,33 +495,33 @@ function useThemedInput(options = {}) {
|
|
|
365
495
|
const { hasError = false, onFocus, onBlur } = options;
|
|
366
496
|
const { theme } = uiFeedback.useUi();
|
|
367
497
|
const { colors, palette, semantic } = theme;
|
|
368
|
-
const [isFocused, setIsFocused] =
|
|
369
|
-
const [isHovered, setIsHovered] =
|
|
370
|
-
const handleFocus =
|
|
498
|
+
const [isFocused, setIsFocused] = React2.useState(false);
|
|
499
|
+
const [isHovered, setIsHovered] = React2.useState(false);
|
|
500
|
+
const handleFocus = React2.useCallback(
|
|
371
501
|
(e) => {
|
|
372
502
|
setIsFocused(true);
|
|
373
503
|
onFocus?.(e);
|
|
374
504
|
},
|
|
375
505
|
[onFocus]
|
|
376
506
|
);
|
|
377
|
-
const handleBlur =
|
|
507
|
+
const handleBlur = React2.useCallback(
|
|
378
508
|
(e) => {
|
|
379
509
|
setIsFocused(false);
|
|
380
510
|
onBlur?.(e);
|
|
381
511
|
},
|
|
382
512
|
[onBlur]
|
|
383
513
|
);
|
|
384
|
-
const handleHoverIn =
|
|
385
|
-
const handleHoverOut =
|
|
514
|
+
const handleHoverIn = React2.useCallback(() => setIsHovered(true), []);
|
|
515
|
+
const handleHoverOut = React2.useCallback(() => setIsHovered(false), []);
|
|
386
516
|
const primary = palette.primary["500"];
|
|
387
517
|
const errorColor = semantic.error["500"];
|
|
388
|
-
const style =
|
|
518
|
+
const style = React2.useMemo(() => {
|
|
389
519
|
const restBorder = hasError ? errorColor : colors.border;
|
|
390
520
|
const focusBorder = hasError ? errorColor : primary;
|
|
391
521
|
const borderColor = isFocused ? focusBorder : isHovered ? withAlpha(colors.text, HOVER_BORDER_ALPHA) : restBorder;
|
|
392
522
|
const backgroundColor = isFocused ? colors.surface : colors.surfaceElevated;
|
|
393
523
|
const base = { color: colors.text, borderColor, backgroundColor };
|
|
394
|
-
if (!
|
|
524
|
+
if (!IS_WEB3) return base;
|
|
395
525
|
const ringColor = hasError ? errorColor : primary;
|
|
396
526
|
const ring = isFocused ? `0 0 0 ${FOCUS_RING_WIDTH}px ${withAlpha(ringColor, FOCUS_RING_ALPHA)}` : "none";
|
|
397
527
|
const webStyle = {
|
|
@@ -414,9 +544,9 @@ function useThemedInput(options = {}) {
|
|
|
414
544
|
primary,
|
|
415
545
|
errorColor
|
|
416
546
|
]);
|
|
417
|
-
const focusBind =
|
|
418
|
-
const hoverBind =
|
|
419
|
-
() =>
|
|
547
|
+
const focusBind = React2.useMemo(() => ({ onFocus: handleFocus, onBlur: handleBlur }), [handleFocus, handleBlur]);
|
|
548
|
+
const hoverBind = React2.useMemo(
|
|
549
|
+
() => IS_WEB3 ? { onMouseEnter: handleHoverIn, onMouseLeave: handleHoverOut } : {},
|
|
420
550
|
[handleHoverIn, handleHoverOut]
|
|
421
551
|
);
|
|
422
552
|
return {
|
|
@@ -428,9 +558,9 @@ function useThemedInput(options = {}) {
|
|
|
428
558
|
hoverBind
|
|
429
559
|
};
|
|
430
560
|
}
|
|
431
|
-
var
|
|
561
|
+
var IS_WEB4 = reactNative.Platform.OS === "web";
|
|
432
562
|
function webFieldA11y({ describedById, hasError = false, required = false }) {
|
|
433
|
-
if (!
|
|
563
|
+
if (!IS_WEB4) {
|
|
434
564
|
return {};
|
|
435
565
|
}
|
|
436
566
|
return {
|
|
@@ -439,7 +569,24 @@ function webFieldA11y({ describedById, hasError = false, required = false }) {
|
|
|
439
569
|
"aria-required": required ? true : void 0
|
|
440
570
|
};
|
|
441
571
|
}
|
|
442
|
-
var
|
|
572
|
+
var MULTILINE_MIN_HEIGHT = 96;
|
|
573
|
+
var boxStyles = reactNative.StyleSheet.create({
|
|
574
|
+
multiline: {
|
|
575
|
+
minHeight: MULTILINE_MIN_HEIGHT,
|
|
576
|
+
textAlignVertical: "top"
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
var ThemedTextInput = React2__default.default.forwardRef(function ThemedTextInput2({
|
|
580
|
+
hasError = false,
|
|
581
|
+
describedById,
|
|
582
|
+
requiredField = false,
|
|
583
|
+
unstyledBox = false,
|
|
584
|
+
onFocus,
|
|
585
|
+
onBlur,
|
|
586
|
+
style,
|
|
587
|
+
placeholderTextColor,
|
|
588
|
+
...rest
|
|
589
|
+
}, ref) {
|
|
443
590
|
const {
|
|
444
591
|
style: themedStyle,
|
|
445
592
|
placeholderTextColor: themedPlaceholder,
|
|
@@ -447,6 +594,8 @@ var ThemedTextInput = React__default.default.forwardRef(function ThemedTextInput
|
|
|
447
594
|
hoverBind
|
|
448
595
|
} = useThemedInput({ hasError, onFocus, onBlur });
|
|
449
596
|
const webA11y = webFieldA11y({ describedById, hasError, required: requiredField });
|
|
597
|
+
const boxStyle = unstyledBox ? null : controlStyles.input;
|
|
598
|
+
const multilineStyle = rest.multiline === true && !unstyledBox ? boxStyles.multiline : null;
|
|
450
599
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
451
600
|
reactNative.TextInput,
|
|
452
601
|
{
|
|
@@ -457,41 +606,39 @@ var ThemedTextInput = React__default.default.forwardRef(function ThemedTextInput
|
|
|
457
606
|
...hoverBind,
|
|
458
607
|
onFocus: focusBind.onFocus,
|
|
459
608
|
onBlur: focusBind.onBlur,
|
|
460
|
-
style: [style, themedStyle]
|
|
609
|
+
style: [boxStyle, multilineStyle, style, themedStyle]
|
|
461
610
|
}
|
|
462
611
|
);
|
|
463
612
|
});
|
|
464
|
-
var INPUT_PADDING_V = 10;
|
|
465
|
-
var INPUT_PADDING_H = 12;
|
|
466
|
-
var INPUT_BORDER_RADIUS = 8;
|
|
467
|
-
var INPUT_BORDER_WIDTH = 1;
|
|
468
|
-
var styles5 = reactNative.StyleSheet.create({
|
|
469
|
-
input: {
|
|
470
|
-
paddingVertical: INPUT_PADDING_V,
|
|
471
|
-
paddingHorizontal: INPUT_PADDING_H,
|
|
472
|
-
borderRadius: INPUT_BORDER_RADIUS,
|
|
473
|
-
borderWidth: INPUT_BORDER_WIDTH
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
613
|
var FormField = ({
|
|
477
614
|
label,
|
|
615
|
+
labelHidden = false,
|
|
478
616
|
required = false,
|
|
479
617
|
error,
|
|
480
618
|
containerStyle,
|
|
481
619
|
...textInputProps
|
|
482
|
-
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
483
|
-
|
|
620
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
621
|
+
Field,
|
|
484
622
|
{
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
623
|
+
containerStyle,
|
|
624
|
+
error,
|
|
625
|
+
label: labelHidden ? void 0 : label,
|
|
626
|
+
required,
|
|
627
|
+
children: ({ describedById, hasError, controlId }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
628
|
+
ThemedTextInput,
|
|
629
|
+
{
|
|
630
|
+
accessibilityHint: `Enter ${label}`,
|
|
631
|
+
accessibilityLabel: label,
|
|
632
|
+
describedById,
|
|
633
|
+
hasError,
|
|
634
|
+
id: controlId,
|
|
635
|
+
requiredField: required,
|
|
636
|
+
testID: "form-field-input",
|
|
637
|
+
...textInputProps
|
|
638
|
+
}
|
|
639
|
+
)
|
|
493
640
|
}
|
|
494
|
-
)
|
|
641
|
+
);
|
|
495
642
|
var TRANSPARENT_COLOR = "transparent";
|
|
496
643
|
var WHITE_COLOR = "#fff";
|
|
497
644
|
var SOLID_RADIUS = 20;
|
|
@@ -525,7 +672,7 @@ function withAlpha2(hex, alpha) {
|
|
|
525
672
|
const isValid = !Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b);
|
|
526
673
|
return isValid ? `rgba(${r}, ${g}, ${b}, ${alpha})` : hex;
|
|
527
674
|
}
|
|
528
|
-
var
|
|
675
|
+
var styles5 = reactNative.StyleSheet.create({
|
|
529
676
|
chipContainer: {
|
|
530
677
|
flexDirection: "row",
|
|
531
678
|
flexWrap: "wrap",
|
|
@@ -577,7 +724,7 @@ function OutlineChip({
|
|
|
577
724
|
ariaPressed,
|
|
578
725
|
onPress
|
|
579
726
|
}) {
|
|
580
|
-
const [hovered, setHovered] =
|
|
727
|
+
const [hovered, setHovered] = React2__default.default.useState(false);
|
|
581
728
|
const active = selected || hovered;
|
|
582
729
|
const borderColor = active ? colors.primary : colors.border;
|
|
583
730
|
const backgroundColor = selected ? colors.tint : colors.muted;
|
|
@@ -596,7 +743,7 @@ function OutlineChip({
|
|
|
596
743
|
onHoverIn: () => setHovered(true),
|
|
597
744
|
onHoverOut: () => setHovered(false),
|
|
598
745
|
onPress,
|
|
599
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
746
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.chipText, styles5.chipTextOutline, { color: textColor }], children: option.label }) })
|
|
600
747
|
}
|
|
601
748
|
);
|
|
602
749
|
}
|
|
@@ -624,7 +771,7 @@ function SolidChip({
|
|
|
624
771
|
hitSlop: CHIP_HIT_SLOP,
|
|
625
772
|
testID,
|
|
626
773
|
onPress,
|
|
627
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
774
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.chipText, { color: textColor }], children: option.label }) })
|
|
628
775
|
}
|
|
629
776
|
);
|
|
630
777
|
}
|
|
@@ -646,7 +793,7 @@ var ChipSelector = ({
|
|
|
646
793
|
}) => {
|
|
647
794
|
const { theme } = uiFeedback.useUi();
|
|
648
795
|
const { colors, palette } = theme;
|
|
649
|
-
const chipColors =
|
|
796
|
+
const chipColors = React2__default.default.useMemo(() => {
|
|
650
797
|
const primary = palette.primary["500"];
|
|
651
798
|
return {
|
|
652
799
|
border: colors.border,
|
|
@@ -689,12 +836,12 @@ var ChipSelector = ({
|
|
|
689
836
|
required,
|
|
690
837
|
spacing,
|
|
691
838
|
testID,
|
|
692
|
-
children: ({ describedById, hasError }) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
839
|
+
children: ({ describedById, hasError }) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles5.chipContainer, ...webFieldA11y({ describedById, hasError }), children: options.map(renderChip) })
|
|
693
840
|
}
|
|
694
841
|
);
|
|
695
842
|
};
|
|
696
843
|
var DESCRIPTION_FONT_SIZE2 = 12;
|
|
697
|
-
var
|
|
844
|
+
var styles6 = reactNative.StyleSheet.create({
|
|
698
845
|
container: {
|
|
699
846
|
flexDirection: "row",
|
|
700
847
|
justifyContent: "space-between",
|
|
@@ -725,7 +872,7 @@ var FormSwitch = ({
|
|
|
725
872
|
const { theme } = uiFeedback.useUi();
|
|
726
873
|
const { colors } = theme;
|
|
727
874
|
const hasDescription = typeof description === "string" && description !== "";
|
|
728
|
-
const themeStyles =
|
|
875
|
+
const themeStyles = React2__default.default.useMemo(() => {
|
|
729
876
|
const labelStyle = { color: colors.text };
|
|
730
877
|
const descriptionStyle = { color: colors.textSecondary };
|
|
731
878
|
return { label: labelStyle, description: descriptionStyle };
|
|
@@ -734,13 +881,13 @@ var FormSwitch = ({
|
|
|
734
881
|
reactNative.View,
|
|
735
882
|
{
|
|
736
883
|
style: [
|
|
737
|
-
|
|
884
|
+
styles6.container,
|
|
738
885
|
containerStyle
|
|
739
886
|
],
|
|
740
887
|
children: [
|
|
741
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
742
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
743
|
-
hasDescription ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
888
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.textColumn, children: [
|
|
889
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.label, themeStyles.label], children: label }),
|
|
890
|
+
hasDescription ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.description, themeStyles.description], children: description }) : null
|
|
744
891
|
] }),
|
|
745
892
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
746
893
|
reactNative.Switch,
|
|
@@ -765,7 +912,7 @@ var BOX_SIZE = 20;
|
|
|
765
912
|
var BOX_RADIUS = 5;
|
|
766
913
|
var CHECK_FONT_SIZE = 14;
|
|
767
914
|
var DISABLED_OPACITY = 0.5;
|
|
768
|
-
var
|
|
915
|
+
var styles7 = reactNative.StyleSheet.create({
|
|
769
916
|
container: {
|
|
770
917
|
flexDirection: "row",
|
|
771
918
|
alignItems: "flex-start",
|
|
@@ -816,7 +963,7 @@ var FormCheckbox = ({
|
|
|
816
963
|
const brand = palette.primary["500"];
|
|
817
964
|
const hintText = typeof hint === "string" && hint !== "" ? hint : description;
|
|
818
965
|
const hasHint = typeof hintText === "string" && hintText !== "";
|
|
819
|
-
const themeStyles =
|
|
966
|
+
const themeStyles = React2__default.default.useMemo(() => {
|
|
820
967
|
const boxOn = { backgroundColor: brand, borderColor: brand };
|
|
821
968
|
const boxOff = { backgroundColor: "transparent", borderColor: colors.border };
|
|
822
969
|
const labelStyle = { color: colors.text };
|
|
@@ -847,101 +994,26 @@ var FormCheckbox = ({
|
|
|
847
994
|
disabled,
|
|
848
995
|
onPress: toggle,
|
|
849
996
|
role: "checkbox",
|
|
850
|
-
style: [
|
|
997
|
+
style: [styles7.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
|
|
851
998
|
testID,
|
|
852
999
|
...keyboardProps,
|
|
853
1000
|
children: [
|
|
854
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
855
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
856
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
857
|
-
hasHint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1001
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles7.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
|
|
1002
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.textColumn, children: [
|
|
1003
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.label, themeStyles.label], children: label }),
|
|
1004
|
+
hasHint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.hint, themeStyles.hint], children: hintText }) : null
|
|
858
1005
|
] })
|
|
859
1006
|
]
|
|
860
1007
|
}
|
|
861
1008
|
);
|
|
862
1009
|
};
|
|
863
|
-
var
|
|
864
|
-
var LABEL_FONT = 11;
|
|
865
|
-
var LABEL_LETTER_SPACING = 0.4;
|
|
866
|
-
var INPUT_RADIUS = 8;
|
|
867
|
-
var INPUT_PAD_H = 12;
|
|
868
|
-
var INPUT_PAD_V = 10;
|
|
869
|
-
var INPUT_FONT = 14;
|
|
870
|
-
var INPUT_BORDER = 1;
|
|
871
|
-
var DATE_ROW_GAP = 8;
|
|
872
|
-
var CHEVRON_FONT = 10;
|
|
873
|
-
var MENU_TOP_GAP = 4;
|
|
874
|
-
var MENU_RADIUS = 8;
|
|
875
|
-
var MENU_PAD_V = 4;
|
|
876
|
-
var MENU_OPT_PAD_H = 12;
|
|
877
|
-
var MENU_OPT_PAD_V = 8;
|
|
878
|
-
var MENU_MIN_WIDTH = 160;
|
|
879
|
-
var MENU_MAX_HEIGHT = 260;
|
|
880
|
-
var MENU_Z = 1e3;
|
|
881
|
-
var MENU_ELEVATION = 8;
|
|
882
|
-
var MENU_BOX_SHADOW = "0px 2px 8px rgba(0, 0, 0, 0.15)";
|
|
883
|
-
var OPTION_FONT = 14;
|
|
884
|
-
var ERROR_FONT = 12;
|
|
885
|
-
var ERROR_GAP = 3;
|
|
886
|
-
var controlStyles = reactNative.StyleSheet.create({
|
|
887
|
-
/** The shared bordered box every dense text control renders in. */
|
|
888
|
-
input: {
|
|
889
|
-
borderWidth: INPUT_BORDER,
|
|
890
|
-
borderRadius: INPUT_RADIUS,
|
|
891
|
-
paddingHorizontal: INPUT_PAD_H,
|
|
892
|
-
paddingVertical: INPUT_PAD_V,
|
|
893
|
-
fontSize: INPUT_FONT
|
|
894
|
-
},
|
|
895
|
-
/** The select trigger: a bordered field box matching the text inputs + a chevron. */
|
|
896
|
-
selectTrigger: {
|
|
897
|
-
flexDirection: "row",
|
|
898
|
-
alignItems: "center",
|
|
899
|
-
justifyContent: "space-between",
|
|
900
|
-
gap: DATE_ROW_GAP,
|
|
901
|
-
borderWidth: INPUT_BORDER,
|
|
902
|
-
borderRadius: INPUT_RADIUS,
|
|
903
|
-
paddingHorizontal: INPUT_PAD_H,
|
|
904
|
-
paddingVertical: INPUT_PAD_V
|
|
905
|
-
},
|
|
906
|
-
selectTriggerText: { fontSize: INPUT_FONT, flexShrink: 1 },
|
|
907
|
-
chevron: { fontSize: CHEVRON_FONT },
|
|
908
|
-
dateRow: { flexDirection: "row", gap: DATE_ROW_GAP },
|
|
909
|
-
dateCol: { gap: FIELD_GAP, flex: 1 },
|
|
910
|
-
/**
|
|
911
|
-
* The micro-caption above a sub-control (a date range's From/To) and beside a switch. Note it
|
|
912
|
-
* is deliberately NOT the `control` label voice: it carries no `700`/uppercase, because it
|
|
913
|
-
* names a HALF of a control that already has a `control`-voice label above it. Uppercasing it
|
|
914
|
-
* too would make one field shout twice.
|
|
915
|
-
*/
|
|
916
|
-
subLabel: { fontSize: LABEL_FONT, letterSpacing: LABEL_LETTER_SPACING },
|
|
917
|
-
/** Positioning context for a floating `AnchoredMenu` (`position:absolute; top:100%`). */
|
|
918
|
-
anchor: { position: "relative" },
|
|
919
|
-
menu: {
|
|
920
|
-
position: "absolute",
|
|
921
|
-
top: "100%",
|
|
922
|
-
left: 0,
|
|
923
|
-
marginTop: MENU_TOP_GAP,
|
|
924
|
-
minWidth: MENU_MIN_WIDTH,
|
|
925
|
-
maxHeight: MENU_MAX_HEIGHT,
|
|
926
|
-
borderWidth: INPUT_BORDER,
|
|
927
|
-
borderRadius: MENU_RADIUS,
|
|
928
|
-
paddingVertical: MENU_PAD_V,
|
|
929
|
-
zIndex: MENU_Z,
|
|
930
|
-
boxShadow: MENU_BOX_SHADOW,
|
|
931
|
-
elevation: MENU_ELEVATION,
|
|
932
|
-
overflow: "hidden"
|
|
933
|
-
},
|
|
934
|
-
option: { paddingHorizontal: MENU_OPT_PAD_H, paddingVertical: MENU_OPT_PAD_V },
|
|
935
|
-
optionText: { fontSize: OPTION_FONT },
|
|
936
|
-
error: { fontSize: ERROR_FONT, marginTop: ERROR_GAP }
|
|
937
|
-
});
|
|
938
|
-
var IS_WEB3 = typeof document !== "undefined";
|
|
1010
|
+
var IS_WEB5 = typeof document !== "undefined";
|
|
939
1011
|
var ESCAPE_KEY = "Escape";
|
|
940
1012
|
var ACTIVE_WEIGHT = "700";
|
|
941
1013
|
var IDLE_WEIGHT = "400";
|
|
942
1014
|
function useDismiss(anchorRef, menuRef, onDismiss) {
|
|
943
|
-
|
|
944
|
-
if (!
|
|
1015
|
+
React2.useEffect(() => {
|
|
1016
|
+
if (!IS_WEB5) return void 0;
|
|
945
1017
|
const onMouseDown = (event) => {
|
|
946
1018
|
const anchorNode = anchorRef.current;
|
|
947
1019
|
const menuNode = menuRef.current;
|
|
@@ -971,7 +1043,7 @@ function AnchoredMenu({
|
|
|
971
1043
|
testID,
|
|
972
1044
|
anchorRef
|
|
973
1045
|
}) {
|
|
974
|
-
const menuRef =
|
|
1046
|
+
const menuRef = React2__default.default.useRef(null);
|
|
975
1047
|
useDismiss(anchorRef, menuRef, onDismiss);
|
|
976
1048
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
977
1049
|
reactNative.View,
|
|
@@ -1010,25 +1082,26 @@ function SelectControl({
|
|
|
1010
1082
|
placeholder = "",
|
|
1011
1083
|
accessibilityLabel,
|
|
1012
1084
|
accessibilityHint,
|
|
1085
|
+
labelledById,
|
|
1013
1086
|
optionHint = "",
|
|
1014
1087
|
style,
|
|
1015
1088
|
testID
|
|
1016
1089
|
}) {
|
|
1017
1090
|
const { theme } = uiFeedback.useUi();
|
|
1018
1091
|
const { colors, palette } = theme;
|
|
1019
|
-
const anchorRef =
|
|
1020
|
-
const [isOpen, setIsOpen] =
|
|
1021
|
-
const selectedLabel =
|
|
1092
|
+
const anchorRef = React2.useRef(null);
|
|
1093
|
+
const [isOpen, setIsOpen] = React2.useState(false);
|
|
1094
|
+
const selectedLabel = React2.useMemo(() => {
|
|
1022
1095
|
const found = options.find((o) => o.value === value);
|
|
1023
1096
|
return found?.label ?? placeholder;
|
|
1024
1097
|
}, [options, placeholder, value]);
|
|
1025
|
-
const close =
|
|
1098
|
+
const close = React2.useCallback(() => {
|
|
1026
1099
|
setIsOpen(false);
|
|
1027
1100
|
}, []);
|
|
1028
|
-
const toggle =
|
|
1101
|
+
const toggle = React2.useCallback(() => {
|
|
1029
1102
|
setIsOpen((prev) => !prev);
|
|
1030
1103
|
}, []);
|
|
1031
|
-
const select =
|
|
1104
|
+
const select = React2.useCallback(
|
|
1032
1105
|
(next) => {
|
|
1033
1106
|
onChange(next);
|
|
1034
1107
|
setIsOpen(false);
|
|
@@ -1044,6 +1117,7 @@ function SelectControl({
|
|
|
1044
1117
|
accessibilityHint,
|
|
1045
1118
|
accessibilityState: { expanded: isOpen },
|
|
1046
1119
|
"aria-expanded": isOpen,
|
|
1120
|
+
"aria-labelledby": labelledById,
|
|
1047
1121
|
onPress: toggle,
|
|
1048
1122
|
style: [controlStyles.selectTrigger, { borderColor: colors.border, backgroundColor: colors.surface }],
|
|
1049
1123
|
testID: `${testID}-trigger`,
|
|
@@ -1120,22 +1194,22 @@ function TypeaheadControl({
|
|
|
1120
1194
|
}) {
|
|
1121
1195
|
const { theme } = uiFeedback.useUi();
|
|
1122
1196
|
const { colors, palette, semantic } = theme;
|
|
1123
|
-
const anchorRef =
|
|
1124
|
-
const [isOpen, setIsOpen] =
|
|
1125
|
-
const exactPick =
|
|
1126
|
-
const suggestions =
|
|
1197
|
+
const anchorRef = React2.useRef(null);
|
|
1198
|
+
const [isOpen, setIsOpen] = React2.useState(false);
|
|
1199
|
+
const exactPick = React2.useMemo(() => options.some((o) => o.label === value), [options, value]);
|
|
1200
|
+
const suggestions = React2.useMemo(
|
|
1127
1201
|
() => exactPick ? [] : suggestOptions(options, value, minChars, maxSuggestions),
|
|
1128
1202
|
[exactPick, options, value, minChars, maxSuggestions]
|
|
1129
1203
|
);
|
|
1130
1204
|
const showMenu = isOpen && suggestions.length > 0;
|
|
1131
|
-
const handleChange =
|
|
1205
|
+
const handleChange = React2.useCallback(
|
|
1132
1206
|
(next) => {
|
|
1133
1207
|
onChange(next);
|
|
1134
1208
|
setIsOpen(true);
|
|
1135
1209
|
},
|
|
1136
1210
|
[onChange]
|
|
1137
1211
|
);
|
|
1138
|
-
const pick =
|
|
1212
|
+
const pick = React2.useCallback(
|
|
1139
1213
|
(optionValue) => {
|
|
1140
1214
|
const opt = options.find((o) => o.value === optionValue);
|
|
1141
1215
|
onChange(opt?.label ?? optionValue);
|
|
@@ -1143,10 +1217,10 @@ function TypeaheadControl({
|
|
|
1143
1217
|
},
|
|
1144
1218
|
[options, onChange]
|
|
1145
1219
|
);
|
|
1146
|
-
const handleBlur =
|
|
1220
|
+
const handleBlur = React2.useCallback(() => {
|
|
1147
1221
|
setTimeout(() => setIsOpen(false), CLOSE_DELAY_MS);
|
|
1148
1222
|
}, []);
|
|
1149
|
-
const handleSubmit =
|
|
1223
|
+
const handleSubmit = React2.useCallback(() => {
|
|
1150
1224
|
setIsOpen(false);
|
|
1151
1225
|
onSubmit?.();
|
|
1152
1226
|
}, [onSubmit]);
|