@dloizides/ui-forms 1.6.0 → 1.8.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 +118 -0
- package/README.md +42 -0
- package/dist/index.d.mts +436 -9
- package/dist/index.d.ts +436 -9
- package/dist/index.js +625 -52
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +616 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +110 -108
package/dist/index.mjs
CHANGED
|
@@ -1,45 +1,116 @@
|
|
|
1
|
-
import React, { useState, useCallback, useMemo } from 'react';
|
|
1
|
+
import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
|
2
2
|
import { StyleSheet, Platform, TextInput, View, Text, Switch, Pressable, TouchableOpacity } from 'react-native';
|
|
3
3
|
import { useUi } from '@dloizides/ui-feedback';
|
|
4
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { Button } from '@dloizides/ui-buttons';
|
|
5
6
|
|
|
6
7
|
// src/Field/Field.tsx
|
|
8
|
+
function FieldLabel({
|
|
9
|
+
label,
|
|
10
|
+
marker,
|
|
11
|
+
optionalLabel,
|
|
12
|
+
style,
|
|
13
|
+
requiredMarkStyle,
|
|
14
|
+
optionalMarkStyle
|
|
15
|
+
}) {
|
|
16
|
+
return /* @__PURE__ */ jsxs(Text, { style, children: [
|
|
17
|
+
label,
|
|
18
|
+
" ",
|
|
19
|
+
marker === "required" ? (
|
|
20
|
+
// The asterisk is decorative — the control's `aria-required` conveys "required" to
|
|
21
|
+
// assistive tech, so a screen reader says "required" instead of "star".
|
|
22
|
+
/* @__PURE__ */ jsx(Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: requiredMarkStyle, children: "*" })
|
|
23
|
+
) : null,
|
|
24
|
+
marker === "optional" && optionalLabel !== "" ? (
|
|
25
|
+
// Also decorative: "optional" is the ABSENCE of `aria-required`, which assistive tech
|
|
26
|
+
// already conveys. Rendering it to a screen reader would double-announce the same fact.
|
|
27
|
+
/* @__PURE__ */ jsx(Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: optionalMarkStyle, children: optionalLabel })
|
|
28
|
+
) : null
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/Field/fieldMarkers.ts
|
|
33
|
+
var DEFAULT_OPTIONAL_LABEL = "(optional)";
|
|
34
|
+
function resolveFieldMarker(required, optional) {
|
|
35
|
+
if (required) return "required";
|
|
36
|
+
if (optional) return "optional";
|
|
37
|
+
return "none";
|
|
38
|
+
}
|
|
39
|
+
var FIELD_FONT_SIZE = 13;
|
|
40
|
+
var FIELD_FONT_WEIGHT = "600";
|
|
41
|
+
var CONTROL_FONT_SIZE = 11;
|
|
42
|
+
var CONTROL_FONT_WEIGHT = "700";
|
|
43
|
+
var CONTROL_LETTER_SPACING = 0.4;
|
|
44
|
+
var labelVariantStyles = StyleSheet.create({
|
|
45
|
+
field: {
|
|
46
|
+
fontSize: FIELD_FONT_SIZE,
|
|
47
|
+
fontWeight: FIELD_FONT_WEIGHT
|
|
48
|
+
},
|
|
49
|
+
control: {
|
|
50
|
+
fontSize: CONTROL_FONT_SIZE,
|
|
51
|
+
fontWeight: CONTROL_FONT_WEIGHT,
|
|
52
|
+
letterSpacing: CONTROL_LETTER_SPACING,
|
|
53
|
+
textTransform: "uppercase"
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
function resolveLabelVariantStyle(variant) {
|
|
57
|
+
return variant === "control" ? labelVariantStyles.control : labelVariantStyles.field;
|
|
58
|
+
}
|
|
7
59
|
var CONTAINER_MARGIN_BOTTOM = 16;
|
|
8
|
-
var LABEL_FONT_SIZE = 13;
|
|
9
|
-
var LABEL_FONT_WEIGHT = "600";
|
|
10
60
|
var LABEL_MARGIN_BOTTOM = 4;
|
|
11
61
|
var ERROR_FONT_SIZE = 12;
|
|
12
62
|
var ERROR_MARGIN_TOP = 4;
|
|
63
|
+
var HINT_FONT_SIZE = 12;
|
|
64
|
+
var HINT_MARGIN_TOP = 4;
|
|
65
|
+
var OPTIONAL_MARK_FONT_WEIGHT = "400";
|
|
13
66
|
var styles = StyleSheet.create({
|
|
14
67
|
container: {
|
|
15
68
|
marginBottom: CONTAINER_MARGIN_BOTTOM
|
|
16
69
|
},
|
|
17
70
|
label: {
|
|
18
|
-
marginBottom: LABEL_MARGIN_BOTTOM
|
|
19
|
-
|
|
20
|
-
|
|
71
|
+
marginBottom: LABEL_MARGIN_BOTTOM
|
|
72
|
+
},
|
|
73
|
+
hintText: {
|
|
74
|
+
fontSize: HINT_FONT_SIZE,
|
|
75
|
+
marginTop: HINT_MARGIN_TOP
|
|
21
76
|
},
|
|
22
77
|
errorText: {
|
|
23
78
|
fontSize: ERROR_FONT_SIZE,
|
|
24
79
|
marginTop: ERROR_MARGIN_TOP
|
|
80
|
+
},
|
|
81
|
+
optionalMark: {
|
|
82
|
+
fontWeight: OPTIONAL_MARK_FONT_WEIGHT
|
|
25
83
|
}
|
|
26
84
|
});
|
|
27
85
|
var fieldSeq = 0;
|
|
28
|
-
function
|
|
29
|
-
const [
|
|
86
|
+
function useFieldIds() {
|
|
87
|
+
const [ids] = React.useState(() => {
|
|
30
88
|
fieldSeq += 1;
|
|
31
|
-
|
|
89
|
+
const seq = String(fieldSeq);
|
|
90
|
+
return { hintId: `field-hint-${seq}`, errorId: `field-error-${seq}` };
|
|
32
91
|
});
|
|
33
|
-
return
|
|
92
|
+
return ids;
|
|
93
|
+
}
|
|
94
|
+
function isPresent(value) {
|
|
95
|
+
return typeof value === "string" && value !== "";
|
|
34
96
|
}
|
|
35
97
|
function hasFieldError(error) {
|
|
36
|
-
return
|
|
98
|
+
return isPresent(error);
|
|
99
|
+
}
|
|
100
|
+
function joinDescribedBy(hintId, errorId) {
|
|
101
|
+
const ids = [hintId, errorId].filter((id) => isPresent(id));
|
|
102
|
+
return ids.length > 0 ? ids.join(" ") : void 0;
|
|
37
103
|
}
|
|
38
104
|
var Field = ({
|
|
39
105
|
label,
|
|
40
106
|
children,
|
|
41
107
|
required = false,
|
|
108
|
+
optional = false,
|
|
109
|
+
optionalLabel = DEFAULT_OPTIONAL_LABEL,
|
|
110
|
+
hint,
|
|
42
111
|
error,
|
|
112
|
+
labelVariant,
|
|
113
|
+
labelStyle,
|
|
43
114
|
containerStyle,
|
|
44
115
|
testID
|
|
45
116
|
}) => {
|
|
@@ -47,31 +118,89 @@ var Field = ({
|
|
|
47
118
|
const { colors, semantic } = theme;
|
|
48
119
|
const errorColor = semantic.error["500"];
|
|
49
120
|
const hasError = hasFieldError(error);
|
|
50
|
-
const
|
|
51
|
-
const
|
|
121
|
+
const hasHint = isPresent(hint);
|
|
122
|
+
const labelText = label ?? "";
|
|
123
|
+
const hasLabel = labelText !== "";
|
|
124
|
+
const marker = resolveFieldMarker(required, optional);
|
|
125
|
+
const { hintId, errorId } = useFieldIds();
|
|
52
126
|
const themeStyles = React.useMemo(
|
|
53
127
|
() => ({
|
|
54
128
|
label: { color: colors.textSecondary },
|
|
55
129
|
requiredMark: { color: errorColor },
|
|
130
|
+
// The optional marker is an aside, so it sits a step back from the label's own ink.
|
|
131
|
+
optionalMark: { color: colors.textSecondary },
|
|
132
|
+
hintText: { color: colors.textSecondary },
|
|
56
133
|
errorText: { color: errorColor }
|
|
57
134
|
}),
|
|
58
135
|
[colors.textSecondary, errorColor]
|
|
59
136
|
);
|
|
60
|
-
const
|
|
137
|
+
const describedById = joinDescribedBy(hasHint ? hintId : void 0, hasError ? errorId : void 0);
|
|
138
|
+
const control = typeof children === "function" ? children({ describedById, hasError }) : children;
|
|
61
139
|
return /* @__PURE__ */ jsxs(View, { style: [styles.container, containerStyle], testID, children: [
|
|
62
|
-
hasLabel ? /* @__PURE__ */
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
140
|
+
hasLabel ? /* @__PURE__ */ jsx(
|
|
141
|
+
FieldLabel,
|
|
142
|
+
{
|
|
143
|
+
label: labelText,
|
|
144
|
+
marker,
|
|
145
|
+
optionalLabel,
|
|
146
|
+
optionalMarkStyle: [styles.optionalMark, themeStyles.optionalMark],
|
|
147
|
+
requiredMarkStyle: themeStyles.requiredMark,
|
|
148
|
+
style: [styles.label, resolveLabelVariantStyle(labelVariant), themeStyles.label, labelStyle]
|
|
149
|
+
}
|
|
150
|
+
) : null,
|
|
71
151
|
control,
|
|
152
|
+
hasHint ? /* @__PURE__ */ jsx(Text, { nativeID: hintId, style: [styles.hintText, themeStyles.hintText], children: hint }) : null,
|
|
72
153
|
hasError ? /* @__PURE__ */ jsx(Text, { nativeID: errorId, role: "alert", style: [styles.errorText, themeStyles.errorText], children: error }) : null
|
|
73
154
|
] });
|
|
74
155
|
};
|
|
156
|
+
var ACTIONS_GAP = 12;
|
|
157
|
+
var styles2 = StyleSheet.create({
|
|
158
|
+
container: {
|
|
159
|
+
flexDirection: "row",
|
|
160
|
+
justifyContent: "flex-end",
|
|
161
|
+
gap: ACTIONS_GAP
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
var FormActions = ({
|
|
165
|
+
onSave,
|
|
166
|
+
onCancel,
|
|
167
|
+
saveLabel,
|
|
168
|
+
cancelLabel = "",
|
|
169
|
+
saveHint,
|
|
170
|
+
cancelHint,
|
|
171
|
+
saving = false,
|
|
172
|
+
saveDisabled = false,
|
|
173
|
+
containerStyle,
|
|
174
|
+
saveTestID = "save-button",
|
|
175
|
+
cancelTestID = "cancel-button",
|
|
176
|
+
testID
|
|
177
|
+
}) => /* @__PURE__ */ jsxs(View, { style: [styles2.container, containerStyle], testID, children: [
|
|
178
|
+
typeof onCancel === "function" ? /* @__PURE__ */ jsx(
|
|
179
|
+
Button,
|
|
180
|
+
{
|
|
181
|
+
accessibilityHint: cancelHint ?? cancelLabel,
|
|
182
|
+
accessibilityLabel: cancelLabel,
|
|
183
|
+
disabled: saving,
|
|
184
|
+
label: cancelLabel,
|
|
185
|
+
testID: cancelTestID,
|
|
186
|
+
variant: "outline",
|
|
187
|
+
onPress: onCancel
|
|
188
|
+
}
|
|
189
|
+
) : null,
|
|
190
|
+
/* @__PURE__ */ jsx(
|
|
191
|
+
Button,
|
|
192
|
+
{
|
|
193
|
+
accessibilityHint: saveHint ?? saveLabel,
|
|
194
|
+
accessibilityLabel: saveLabel,
|
|
195
|
+
disabled: saveDisabled,
|
|
196
|
+
label: saveLabel,
|
|
197
|
+
loading: saving,
|
|
198
|
+
testID: saveTestID,
|
|
199
|
+
variant: "primary",
|
|
200
|
+
onPress: onSave
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
] });
|
|
75
204
|
var IS_WEB = Platform.OS === "web";
|
|
76
205
|
var TRANSITION_PROPERTY = "border-color, background-color, box-shadow";
|
|
77
206
|
var TRANSITION_DURATION = "0.14s";
|
|
@@ -131,7 +260,8 @@ function useThemedInput(options = {}) {
|
|
|
131
260
|
const backgroundColor = isFocused ? colors.surface : colors.surfaceElevated;
|
|
132
261
|
const base = { color: colors.text, borderColor, backgroundColor };
|
|
133
262
|
if (!IS_WEB) return base;
|
|
134
|
-
const
|
|
263
|
+
const ringColor = hasError ? errorColor : primary;
|
|
264
|
+
const ring = isFocused ? `0 0 0 ${FOCUS_RING_WIDTH}px ${withAlpha(ringColor, FOCUS_RING_ALPHA)}` : "none";
|
|
135
265
|
const webStyle = {
|
|
136
266
|
transitionProperty: TRANSITION_PROPERTY,
|
|
137
267
|
// Collapse the eased transition to instant when the user prefers reduced motion.
|
|
@@ -197,7 +327,7 @@ var INPUT_PADDING_V = 10;
|
|
|
197
327
|
var INPUT_PADDING_H = 12;
|
|
198
328
|
var INPUT_BORDER_RADIUS = 8;
|
|
199
329
|
var INPUT_BORDER_WIDTH = 1;
|
|
200
|
-
var
|
|
330
|
+
var styles3 = StyleSheet.create({
|
|
201
331
|
input: {
|
|
202
332
|
paddingVertical: INPUT_PADDING_V,
|
|
203
333
|
paddingHorizontal: INPUT_PADDING_H,
|
|
@@ -219,7 +349,7 @@ var FormField = ({
|
|
|
219
349
|
describedById,
|
|
220
350
|
hasError,
|
|
221
351
|
requiredField: required,
|
|
222
|
-
style:
|
|
352
|
+
style: styles3.input,
|
|
223
353
|
testID: "form-field-input",
|
|
224
354
|
...textInputProps
|
|
225
355
|
}
|
|
@@ -240,6 +370,13 @@ var RGB_G_END = 4;
|
|
|
240
370
|
var RGB_B_END = 6;
|
|
241
371
|
var CHIP_HIT_SLOP = { top: 10, bottom: 10, left: 4, right: 4 };
|
|
242
372
|
var CHIP_GUTTER = 8;
|
|
373
|
+
var DEFAULT_CHIP_TESTID_PREFIX = "chip-selector-chip";
|
|
374
|
+
var ON_BRAND_SCALE = "onBrand";
|
|
375
|
+
var MAIN_STEP = "500";
|
|
376
|
+
function onBrandInk(palette) {
|
|
377
|
+
const scales = palette;
|
|
378
|
+
return scales[ON_BRAND_SCALE]?.[MAIN_STEP] ?? WHITE_COLOR;
|
|
379
|
+
}
|
|
243
380
|
function withAlpha2(hex, alpha) {
|
|
244
381
|
const normalized = hex.replace("#", "");
|
|
245
382
|
const isShort = normalized.length === HEX_SHORT_LENGTH2;
|
|
@@ -250,7 +387,7 @@ function withAlpha2(hex, alpha) {
|
|
|
250
387
|
const isValid = !Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b);
|
|
251
388
|
return isValid ? `rgba(${r}, ${g}, ${b}, ${alpha})` : hex;
|
|
252
389
|
}
|
|
253
|
-
var
|
|
390
|
+
var styles4 = StyleSheet.create({
|
|
254
391
|
chipContainer: {
|
|
255
392
|
flexDirection: "row",
|
|
256
393
|
flexWrap: "wrap",
|
|
@@ -279,11 +416,23 @@ var styles3 = StyleSheet.create({
|
|
|
279
416
|
chipText: {},
|
|
280
417
|
chipTextOutline: { fontSize: OUTLINE_FONT_SIZE }
|
|
281
418
|
});
|
|
419
|
+
function chipIdentity(option, selected, config) {
|
|
420
|
+
return {
|
|
421
|
+
testID: `${config.prefix}-${String(option.value)}`,
|
|
422
|
+
// The English default is retained verbatim: erevna/katalogos assert the exact string
|
|
423
|
+
// `Selects Red`. Consumers that localize pass `optionAccessibilityHint`.
|
|
424
|
+
accessibilityHint: config.hint ?? `Selects ${option.label}`,
|
|
425
|
+
ariaPressed: config.multiple ? selected : void 0
|
|
426
|
+
};
|
|
427
|
+
}
|
|
282
428
|
function OutlineChip({
|
|
283
429
|
option,
|
|
284
430
|
selected,
|
|
285
431
|
disabled,
|
|
286
432
|
colors,
|
|
433
|
+
testID,
|
|
434
|
+
accessibilityHint,
|
|
435
|
+
ariaPressed,
|
|
287
436
|
onPress
|
|
288
437
|
}) {
|
|
289
438
|
const [hovered, setHovered] = React.useState(false);
|
|
@@ -294,18 +443,19 @@ function OutlineChip({
|
|
|
294
443
|
return /* @__PURE__ */ jsx(
|
|
295
444
|
Pressable,
|
|
296
445
|
{
|
|
297
|
-
accessibilityHint
|
|
446
|
+
accessibilityHint,
|
|
298
447
|
accessibilityLabel: option.label,
|
|
299
448
|
accessibilityRole: "button",
|
|
300
449
|
accessibilityState: { selected, disabled },
|
|
450
|
+
"aria-pressed": ariaPressed,
|
|
301
451
|
disabled,
|
|
302
452
|
hitSlop: CHIP_HIT_SLOP,
|
|
303
|
-
style:
|
|
304
|
-
testID
|
|
453
|
+
style: styles4.chipWrapper,
|
|
454
|
+
testID,
|
|
305
455
|
onHoverIn: () => setHovered(true),
|
|
306
456
|
onHoverOut: () => setHovered(false),
|
|
307
457
|
onPress,
|
|
308
|
-
children: /* @__PURE__ */ jsx(View, { style: [
|
|
458
|
+
children: /* @__PURE__ */ jsx(View, { style: [styles4.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles4.chipText, styles4.chipTextOutline, { color: textColor }], children: option.label }) })
|
|
309
459
|
}
|
|
310
460
|
);
|
|
311
461
|
}
|
|
@@ -314,23 +464,27 @@ function SolidChip({
|
|
|
314
464
|
selected,
|
|
315
465
|
disabled,
|
|
316
466
|
colors,
|
|
467
|
+
testID,
|
|
468
|
+
accessibilityHint,
|
|
469
|
+
ariaPressed,
|
|
317
470
|
onPress
|
|
318
471
|
}) {
|
|
319
472
|
const backgroundColor = selected ? colors.primary : TRANSPARENT_COLOR;
|
|
320
|
-
const textColor = selected ?
|
|
473
|
+
const textColor = selected ? colors.onPrimary : colors.textStrong;
|
|
321
474
|
return /* @__PURE__ */ jsx(
|
|
322
475
|
TouchableOpacity,
|
|
323
476
|
{
|
|
324
|
-
accessibilityHint
|
|
477
|
+
accessibilityHint,
|
|
325
478
|
accessibilityLabel: option.label,
|
|
326
479
|
accessibilityRole: "button",
|
|
327
480
|
accessibilityState: { selected, disabled },
|
|
481
|
+
"aria-pressed": ariaPressed,
|
|
328
482
|
disabled,
|
|
329
483
|
hitSlop: CHIP_HIT_SLOP,
|
|
330
|
-
style:
|
|
331
|
-
testID
|
|
484
|
+
style: styles4.chipWrapper,
|
|
485
|
+
testID,
|
|
332
486
|
onPress,
|
|
333
|
-
children: /* @__PURE__ */ jsx(View, { style: [
|
|
487
|
+
children: /* @__PURE__ */ jsx(View, { style: [styles4.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles4.chipText, { color: textColor }], children: option.label }) })
|
|
334
488
|
}
|
|
335
489
|
);
|
|
336
490
|
}
|
|
@@ -350,7 +504,9 @@ var ChipSelector = ({
|
|
|
350
504
|
containerStyle,
|
|
351
505
|
required = false,
|
|
352
506
|
error,
|
|
353
|
-
testID
|
|
507
|
+
testID,
|
|
508
|
+
chipTestIDPrefix = DEFAULT_CHIP_TESTID_PREFIX,
|
|
509
|
+
optionAccessibilityHint
|
|
354
510
|
}) => {
|
|
355
511
|
const { theme } = useUi();
|
|
356
512
|
const { colors, palette } = theme;
|
|
@@ -361,30 +517,37 @@ var ChipSelector = ({
|
|
|
361
517
|
textStrong: colors.text,
|
|
362
518
|
textMuted: colors.textSecondary,
|
|
363
519
|
primary,
|
|
520
|
+
onPrimary: onBrandInk(palette),
|
|
364
521
|
muted: colors.background,
|
|
365
522
|
tint: withAlpha2(primary, OUTLINE_TINT_ALPHA)
|
|
366
523
|
};
|
|
367
|
-
}, [colors.border, colors.text, colors.textSecondary, colors.background, palette
|
|
524
|
+
}, [colors.border, colors.text, colors.textSecondary, colors.background, palette]);
|
|
368
525
|
function isSelected(optionValue) {
|
|
369
526
|
if (multiple && Array.isArray(value))
|
|
370
527
|
return value.includes(optionValue);
|
|
371
528
|
return value === optionValue;
|
|
372
529
|
}
|
|
373
530
|
function renderChip(option) {
|
|
531
|
+
const selected = isSelected(option.value);
|
|
374
532
|
const chipProps = {
|
|
375
533
|
colors: chipColors,
|
|
376
534
|
disabled,
|
|
377
535
|
option,
|
|
378
|
-
selected
|
|
536
|
+
selected,
|
|
537
|
+
...chipIdentity(option, selected, {
|
|
538
|
+
prefix: chipTestIDPrefix,
|
|
539
|
+
hint: optionAccessibilityHint,
|
|
540
|
+
multiple
|
|
541
|
+
}),
|
|
379
542
|
onPress: () => onChange(option.value)
|
|
380
543
|
};
|
|
381
544
|
const key = String(option.value);
|
|
382
545
|
return variant === "outline" ? /* @__PURE__ */ jsx(OutlineChip, { ...chipProps }, key) : /* @__PURE__ */ jsx(SolidChip, { ...chipProps }, key);
|
|
383
546
|
}
|
|
384
|
-
return /* @__PURE__ */ jsx(Field, { containerStyle, error, label, required, testID, children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(View, { style:
|
|
547
|
+
return /* @__PURE__ */ jsx(Field, { containerStyle, error, label, required, testID, children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(View, { style: styles4.chipContainer, ...groupA11yProps(describedById, hasError), children: options.map(renderChip) }) });
|
|
385
548
|
};
|
|
386
549
|
var DESCRIPTION_FONT_SIZE = 12;
|
|
387
|
-
var
|
|
550
|
+
var styles5 = StyleSheet.create({
|
|
388
551
|
container: {
|
|
389
552
|
flexDirection: "row",
|
|
390
553
|
justifyContent: "space-between",
|
|
@@ -424,13 +587,13 @@ var FormSwitch = ({
|
|
|
424
587
|
View,
|
|
425
588
|
{
|
|
426
589
|
style: [
|
|
427
|
-
|
|
590
|
+
styles5.container,
|
|
428
591
|
containerStyle
|
|
429
592
|
],
|
|
430
593
|
children: [
|
|
431
|
-
/* @__PURE__ */ jsxs(View, { style:
|
|
432
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
433
|
-
hasDescription ? /* @__PURE__ */ jsx(Text, { style: [
|
|
594
|
+
/* @__PURE__ */ jsxs(View, { style: styles5.textColumn, children: [
|
|
595
|
+
/* @__PURE__ */ jsx(Text, { style: [styles5.label, themeStyles.label], children: label }),
|
|
596
|
+
hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles5.description, themeStyles.description], children: description }) : null
|
|
434
597
|
] }),
|
|
435
598
|
/* @__PURE__ */ jsx(
|
|
436
599
|
Switch,
|
|
@@ -450,12 +613,12 @@ var FormSwitch = ({
|
|
|
450
613
|
);
|
|
451
614
|
};
|
|
452
615
|
var ACTIVATION_KEYS = ["Enter", " ", "Spacebar"];
|
|
453
|
-
var
|
|
616
|
+
var HINT_FONT_SIZE2 = 12;
|
|
454
617
|
var BOX_SIZE = 20;
|
|
455
618
|
var BOX_RADIUS = 5;
|
|
456
619
|
var CHECK_FONT_SIZE = 14;
|
|
457
620
|
var DISABLED_OPACITY = 0.5;
|
|
458
|
-
var
|
|
621
|
+
var styles6 = StyleSheet.create({
|
|
459
622
|
container: {
|
|
460
623
|
flexDirection: "row",
|
|
461
624
|
alignItems: "flex-start",
|
|
@@ -484,7 +647,7 @@ var styles5 = StyleSheet.create({
|
|
|
484
647
|
fontWeight: "600"
|
|
485
648
|
},
|
|
486
649
|
hint: {
|
|
487
|
-
fontSize:
|
|
650
|
+
fontSize: HINT_FONT_SIZE2,
|
|
488
651
|
marginTop: 2
|
|
489
652
|
}
|
|
490
653
|
});
|
|
@@ -537,20 +700,419 @@ var FormCheckbox = ({
|
|
|
537
700
|
disabled,
|
|
538
701
|
onPress: toggle,
|
|
539
702
|
role: "checkbox",
|
|
540
|
-
style: [
|
|
703
|
+
style: [styles6.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
|
|
541
704
|
testID,
|
|
542
705
|
...keyboardProps,
|
|
543
706
|
children: [
|
|
544
|
-
/* @__PURE__ */ jsx(View, { style: [
|
|
545
|
-
/* @__PURE__ */ jsxs(View, { style:
|
|
546
|
-
/* @__PURE__ */ jsx(Text, { style: [
|
|
547
|
-
hasHint ? /* @__PURE__ */ jsx(Text, { style: [
|
|
707
|
+
/* @__PURE__ */ jsx(View, { style: [styles6.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsx(Text, { style: [styles6.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
|
|
708
|
+
/* @__PURE__ */ jsxs(View, { style: styles6.textColumn, children: [
|
|
709
|
+
/* @__PURE__ */ jsx(Text, { style: [styles6.label, themeStyles.label], children: label }),
|
|
710
|
+
hasHint ? /* @__PURE__ */ jsx(Text, { style: [styles6.hint, themeStyles.hint], children: hintText }) : null
|
|
548
711
|
] })
|
|
549
712
|
]
|
|
550
713
|
}
|
|
551
714
|
);
|
|
552
715
|
};
|
|
716
|
+
var FIELD_GAP = 4;
|
|
717
|
+
var LABEL_FONT = 11;
|
|
718
|
+
var LABEL_LETTER_SPACING = 0.4;
|
|
719
|
+
var INPUT_RADIUS = 8;
|
|
720
|
+
var INPUT_PAD_H = 12;
|
|
721
|
+
var INPUT_PAD_V = 10;
|
|
722
|
+
var INPUT_FONT = 14;
|
|
723
|
+
var INPUT_BORDER = 1;
|
|
724
|
+
var DATE_ROW_GAP = 8;
|
|
725
|
+
var CHEVRON_FONT = 10;
|
|
726
|
+
var MENU_TOP_GAP = 4;
|
|
727
|
+
var MENU_RADIUS = 8;
|
|
728
|
+
var MENU_PAD_V = 4;
|
|
729
|
+
var MENU_OPT_PAD_H = 12;
|
|
730
|
+
var MENU_OPT_PAD_V = 8;
|
|
731
|
+
var MENU_MIN_WIDTH = 160;
|
|
732
|
+
var MENU_MAX_HEIGHT = 260;
|
|
733
|
+
var MENU_Z = 1e3;
|
|
734
|
+
var MENU_ELEVATION = 8;
|
|
735
|
+
var MENU_BOX_SHADOW = "0px 2px 8px rgba(0, 0, 0, 0.15)";
|
|
736
|
+
var OPTION_FONT = 14;
|
|
737
|
+
var ERROR_FONT = 12;
|
|
738
|
+
var ERROR_GAP = 3;
|
|
739
|
+
var controlStyles = StyleSheet.create({
|
|
740
|
+
/** The shared bordered box every dense text control renders in. */
|
|
741
|
+
input: {
|
|
742
|
+
borderWidth: INPUT_BORDER,
|
|
743
|
+
borderRadius: INPUT_RADIUS,
|
|
744
|
+
paddingHorizontal: INPUT_PAD_H,
|
|
745
|
+
paddingVertical: INPUT_PAD_V,
|
|
746
|
+
fontSize: INPUT_FONT
|
|
747
|
+
},
|
|
748
|
+
/** The select trigger: a bordered field box matching the text inputs + a chevron. */
|
|
749
|
+
selectTrigger: {
|
|
750
|
+
flexDirection: "row",
|
|
751
|
+
alignItems: "center",
|
|
752
|
+
justifyContent: "space-between",
|
|
753
|
+
gap: DATE_ROW_GAP,
|
|
754
|
+
borderWidth: INPUT_BORDER,
|
|
755
|
+
borderRadius: INPUT_RADIUS,
|
|
756
|
+
paddingHorizontal: INPUT_PAD_H,
|
|
757
|
+
paddingVertical: INPUT_PAD_V
|
|
758
|
+
},
|
|
759
|
+
selectTriggerText: { fontSize: INPUT_FONT, flexShrink: 1 },
|
|
760
|
+
chevron: { fontSize: CHEVRON_FONT },
|
|
761
|
+
dateRow: { flexDirection: "row", gap: DATE_ROW_GAP },
|
|
762
|
+
dateCol: { gap: FIELD_GAP, flex: 1 },
|
|
763
|
+
/**
|
|
764
|
+
* The micro-caption above a sub-control (a date range's From/To) and beside a switch. Note it
|
|
765
|
+
* is deliberately NOT the `control` label voice: it carries no `700`/uppercase, because it
|
|
766
|
+
* names a HALF of a control that already has a `control`-voice label above it. Uppercasing it
|
|
767
|
+
* too would make one field shout twice.
|
|
768
|
+
*/
|
|
769
|
+
subLabel: { fontSize: LABEL_FONT, letterSpacing: LABEL_LETTER_SPACING },
|
|
770
|
+
/** Positioning context for a floating `AnchoredMenu` (`position:absolute; top:100%`). */
|
|
771
|
+
anchor: { position: "relative" },
|
|
772
|
+
menu: {
|
|
773
|
+
position: "absolute",
|
|
774
|
+
top: "100%",
|
|
775
|
+
left: 0,
|
|
776
|
+
marginTop: MENU_TOP_GAP,
|
|
777
|
+
minWidth: MENU_MIN_WIDTH,
|
|
778
|
+
maxHeight: MENU_MAX_HEIGHT,
|
|
779
|
+
borderWidth: INPUT_BORDER,
|
|
780
|
+
borderRadius: MENU_RADIUS,
|
|
781
|
+
paddingVertical: MENU_PAD_V,
|
|
782
|
+
zIndex: MENU_Z,
|
|
783
|
+
boxShadow: MENU_BOX_SHADOW,
|
|
784
|
+
elevation: MENU_ELEVATION,
|
|
785
|
+
overflow: "hidden"
|
|
786
|
+
},
|
|
787
|
+
option: { paddingHorizontal: MENU_OPT_PAD_H, paddingVertical: MENU_OPT_PAD_V },
|
|
788
|
+
optionText: { fontSize: OPTION_FONT },
|
|
789
|
+
error: { fontSize: ERROR_FONT, marginTop: ERROR_GAP }
|
|
790
|
+
});
|
|
791
|
+
var IS_WEB4 = typeof document !== "undefined";
|
|
792
|
+
var ESCAPE_KEY = "Escape";
|
|
793
|
+
var ACTIVE_WEIGHT = "700";
|
|
794
|
+
var IDLE_WEIGHT = "400";
|
|
795
|
+
function useDismiss(anchorRef, menuRef, onDismiss) {
|
|
796
|
+
useEffect(() => {
|
|
797
|
+
if (!IS_WEB4) return void 0;
|
|
798
|
+
const onMouseDown = (event) => {
|
|
799
|
+
const anchorNode = anchorRef.current;
|
|
800
|
+
const menuNode = menuRef.current;
|
|
801
|
+
const target = event.target instanceof Node ? event.target : null;
|
|
802
|
+
const insideAnchor = anchorNode !== null && target !== null && anchorNode.contains(target);
|
|
803
|
+
const insideMenu = menuNode !== null && target !== null && menuNode.contains(target);
|
|
804
|
+
if (!insideAnchor && !insideMenu) onDismiss();
|
|
805
|
+
};
|
|
806
|
+
const onKeyDown = (event) => {
|
|
807
|
+
if (event.key === ESCAPE_KEY) onDismiss();
|
|
808
|
+
};
|
|
809
|
+
document.addEventListener("mousedown", onMouseDown);
|
|
810
|
+
document.addEventListener("keydown", onKeyDown);
|
|
811
|
+
return () => {
|
|
812
|
+
document.removeEventListener("mousedown", onMouseDown);
|
|
813
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
814
|
+
};
|
|
815
|
+
}, [anchorRef, menuRef, onDismiss]);
|
|
816
|
+
}
|
|
817
|
+
function AnchoredMenu({
|
|
818
|
+
options,
|
|
819
|
+
selectedValue,
|
|
820
|
+
onSelect,
|
|
821
|
+
onDismiss,
|
|
822
|
+
colors,
|
|
823
|
+
optionHint,
|
|
824
|
+
testID,
|
|
825
|
+
anchorRef
|
|
826
|
+
}) {
|
|
827
|
+
const menuRef = React.useRef(null);
|
|
828
|
+
useDismiss(anchorRef, menuRef, onDismiss);
|
|
829
|
+
return /* @__PURE__ */ jsx(
|
|
830
|
+
View,
|
|
831
|
+
{
|
|
832
|
+
ref: menuRef,
|
|
833
|
+
accessibilityRole: "menu",
|
|
834
|
+
style: [controlStyles.menu, { borderColor: colors.border, backgroundColor: colors.surface }],
|
|
835
|
+
testID: `${testID}-menu`,
|
|
836
|
+
children: options.map((opt) => {
|
|
837
|
+
const active = opt.value === selectedValue;
|
|
838
|
+
return /* @__PURE__ */ jsx(
|
|
839
|
+
Pressable,
|
|
840
|
+
{
|
|
841
|
+
accessibilityRole: "menuitem",
|
|
842
|
+
accessibilityLabel: opt.label,
|
|
843
|
+
accessibilityHint: optionHint,
|
|
844
|
+
accessibilityState: { selected: active },
|
|
845
|
+
"aria-selected": active,
|
|
846
|
+
onPress: () => onSelect(opt.value),
|
|
847
|
+
style: controlStyles.option,
|
|
848
|
+
testID: `${testID}-option-${opt.value}`,
|
|
849
|
+
children: /* @__PURE__ */ jsx(Text, { style: [controlStyles.optionText, { color: active ? colors.brand : colors.text, fontWeight: active ? ACTIVE_WEIGHT : IDLE_WEIGHT }], children: opt.label })
|
|
850
|
+
},
|
|
851
|
+
opt.value
|
|
852
|
+
);
|
|
853
|
+
})
|
|
854
|
+
}
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
var CHEVRON_DOWN = "\u25BE";
|
|
858
|
+
var CHEVRON_UP = "\u25B4";
|
|
859
|
+
function SelectControl({
|
|
860
|
+
options,
|
|
861
|
+
value,
|
|
862
|
+
onChange,
|
|
863
|
+
placeholder = "",
|
|
864
|
+
accessibilityLabel,
|
|
865
|
+
accessibilityHint,
|
|
866
|
+
optionHint = "",
|
|
867
|
+
style,
|
|
868
|
+
testID
|
|
869
|
+
}) {
|
|
870
|
+
const { theme } = useUi();
|
|
871
|
+
const { colors, palette } = theme;
|
|
872
|
+
const anchorRef = useRef(null);
|
|
873
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
874
|
+
const selectedLabel = useMemo(() => {
|
|
875
|
+
const found = options.find((o) => o.value === value);
|
|
876
|
+
return found?.label ?? placeholder;
|
|
877
|
+
}, [options, placeholder, value]);
|
|
878
|
+
const close = useCallback(() => {
|
|
879
|
+
setIsOpen(false);
|
|
880
|
+
}, []);
|
|
881
|
+
const toggle = useCallback(() => {
|
|
882
|
+
setIsOpen((prev) => !prev);
|
|
883
|
+
}, []);
|
|
884
|
+
const select = useCallback(
|
|
885
|
+
(next) => {
|
|
886
|
+
onChange(next);
|
|
887
|
+
setIsOpen(false);
|
|
888
|
+
},
|
|
889
|
+
[onChange]
|
|
890
|
+
);
|
|
891
|
+
return /* @__PURE__ */ jsxs(View, { ref: anchorRef, style: [controlStyles.anchor, style], children: [
|
|
892
|
+
/* @__PURE__ */ jsxs(
|
|
893
|
+
Pressable,
|
|
894
|
+
{
|
|
895
|
+
accessibilityRole: "button",
|
|
896
|
+
accessibilityLabel,
|
|
897
|
+
accessibilityHint,
|
|
898
|
+
accessibilityState: { expanded: isOpen },
|
|
899
|
+
"aria-expanded": isOpen,
|
|
900
|
+
onPress: toggle,
|
|
901
|
+
style: [controlStyles.selectTrigger, { borderColor: colors.border, backgroundColor: colors.surface }],
|
|
902
|
+
testID: `${testID}-trigger`,
|
|
903
|
+
children: [
|
|
904
|
+
/* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: [controlStyles.selectTriggerText, { color: value === "" ? colors.textSecondary : colors.text }], children: selectedLabel }),
|
|
905
|
+
/* @__PURE__ */ jsx(Text, { "aria-hidden": true, style: [controlStyles.chevron, { color: colors.textSecondary }], children: isOpen ? CHEVRON_UP : CHEVRON_DOWN })
|
|
906
|
+
]
|
|
907
|
+
}
|
|
908
|
+
),
|
|
909
|
+
isOpen ? /* @__PURE__ */ jsx(
|
|
910
|
+
AnchoredMenu,
|
|
911
|
+
{
|
|
912
|
+
options,
|
|
913
|
+
selectedValue: value,
|
|
914
|
+
onSelect: select,
|
|
915
|
+
onDismiss: close,
|
|
916
|
+
colors: { text: colors.text, border: colors.border, surface: colors.surface, brand: palette.primary["500"] },
|
|
917
|
+
optionHint,
|
|
918
|
+
testID,
|
|
919
|
+
anchorRef
|
|
920
|
+
}
|
|
921
|
+
) : null
|
|
922
|
+
] });
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// src/controls/suggestOptions.ts
|
|
926
|
+
var RANK_VALUE_EXACT = 0;
|
|
927
|
+
var RANK_VALUE_PREFIX = 1;
|
|
928
|
+
var RANK_LABEL_PREFIX = 2;
|
|
929
|
+
var RANK_LABEL_CONTAINS = 3;
|
|
930
|
+
var NO_MATCH = -1;
|
|
931
|
+
function rankOf(option, needle) {
|
|
932
|
+
const value = option.value.toLowerCase();
|
|
933
|
+
const label = option.label.toLowerCase();
|
|
934
|
+
if (value === needle) return RANK_VALUE_EXACT;
|
|
935
|
+
if (value.startsWith(needle)) return RANK_VALUE_PREFIX;
|
|
936
|
+
if (label.startsWith(needle)) return RANK_LABEL_PREFIX;
|
|
937
|
+
if (label.includes(needle)) return RANK_LABEL_CONTAINS;
|
|
938
|
+
return NO_MATCH;
|
|
939
|
+
}
|
|
940
|
+
function suggestOptions(options, query, minChars, limit) {
|
|
941
|
+
const needle = query.trim().toLowerCase();
|
|
942
|
+
if (needle.length < minChars) return [];
|
|
943
|
+
const scored = [];
|
|
944
|
+
for (const option of options) {
|
|
945
|
+
const rank = rankOf(option, needle);
|
|
946
|
+
if (rank !== NO_MATCH) scored.push({ option, rank });
|
|
947
|
+
}
|
|
948
|
+
scored.sort((a, b) => a.rank !== b.rank ? a.rank - b.rank : a.option.label.localeCompare(b.option.label));
|
|
949
|
+
return scored.slice(0, limit).map((entry) => entry.option);
|
|
950
|
+
}
|
|
951
|
+
function isUnmatched(options, text) {
|
|
952
|
+
const needle = text.trim().toLowerCase();
|
|
953
|
+
if (needle === "") return false;
|
|
954
|
+
return !options.some((o) => o.value.toLowerCase() === needle || o.label.toLowerCase() === needle);
|
|
955
|
+
}
|
|
956
|
+
var CLOSE_DELAY_MS = 140;
|
|
957
|
+
var DEFAULT_TYPEAHEAD_MIN_CHARS = 1;
|
|
958
|
+
var DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS = 8;
|
|
959
|
+
function TypeaheadControl({
|
|
960
|
+
options,
|
|
961
|
+
value,
|
|
962
|
+
onChange,
|
|
963
|
+
onSubmit,
|
|
964
|
+
placeholder,
|
|
965
|
+
accessibilityLabel,
|
|
966
|
+
accessibilityHint,
|
|
967
|
+
optionHint = "",
|
|
968
|
+
error,
|
|
969
|
+
minChars = DEFAULT_TYPEAHEAD_MIN_CHARS,
|
|
970
|
+
maxSuggestions = DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS,
|
|
971
|
+
style,
|
|
972
|
+
testID
|
|
973
|
+
}) {
|
|
974
|
+
const { theme } = useUi();
|
|
975
|
+
const { colors, palette, semantic } = theme;
|
|
976
|
+
const anchorRef = useRef(null);
|
|
977
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
978
|
+
const exactPick = useMemo(() => options.some((o) => o.label === value), [options, value]);
|
|
979
|
+
const suggestions = useMemo(
|
|
980
|
+
() => exactPick ? [] : suggestOptions(options, value, minChars, maxSuggestions),
|
|
981
|
+
[exactPick, options, value, minChars, maxSuggestions]
|
|
982
|
+
);
|
|
983
|
+
const showMenu = isOpen && suggestions.length > 0;
|
|
984
|
+
const handleChange = useCallback(
|
|
985
|
+
(next) => {
|
|
986
|
+
onChange(next);
|
|
987
|
+
setIsOpen(true);
|
|
988
|
+
},
|
|
989
|
+
[onChange]
|
|
990
|
+
);
|
|
991
|
+
const pick = useCallback(
|
|
992
|
+
(optionValue) => {
|
|
993
|
+
const opt = options.find((o) => o.value === optionValue);
|
|
994
|
+
onChange(opt?.label ?? optionValue);
|
|
995
|
+
setIsOpen(false);
|
|
996
|
+
},
|
|
997
|
+
[options, onChange]
|
|
998
|
+
);
|
|
999
|
+
const handleBlur = useCallback(() => {
|
|
1000
|
+
setTimeout(() => setIsOpen(false), CLOSE_DELAY_MS);
|
|
1001
|
+
}, []);
|
|
1002
|
+
const handleSubmit = useCallback(() => {
|
|
1003
|
+
setIsOpen(false);
|
|
1004
|
+
onSubmit?.();
|
|
1005
|
+
}, [onSubmit]);
|
|
1006
|
+
const hasError = error !== void 0 && error !== "";
|
|
1007
|
+
return /* @__PURE__ */ jsxs(View, { ref: anchorRef, style: [controlStyles.anchor, style], children: [
|
|
1008
|
+
/* @__PURE__ */ jsx(
|
|
1009
|
+
TextInput,
|
|
1010
|
+
{
|
|
1011
|
+
accessibilityLabel,
|
|
1012
|
+
accessibilityHint,
|
|
1013
|
+
autoCapitalize: "none",
|
|
1014
|
+
autoCorrect: false,
|
|
1015
|
+
placeholder,
|
|
1016
|
+
placeholderTextColor: colors.textSecondary,
|
|
1017
|
+
spellCheck: false,
|
|
1018
|
+
value,
|
|
1019
|
+
onChangeText: handleChange,
|
|
1020
|
+
onFocus: () => setIsOpen(true),
|
|
1021
|
+
onBlur: handleBlur,
|
|
1022
|
+
onSubmitEditing: handleSubmit,
|
|
1023
|
+
style: [controlStyles.input, { borderColor: hasError ? semantic.error["500"] : colors.border, backgroundColor: colors.surface, color: colors.text }],
|
|
1024
|
+
testID: `${testID}-input`
|
|
1025
|
+
}
|
|
1026
|
+
),
|
|
1027
|
+
showMenu ? /* @__PURE__ */ jsx(
|
|
1028
|
+
AnchoredMenu,
|
|
1029
|
+
{
|
|
1030
|
+
options: suggestions,
|
|
1031
|
+
selectedValue: value,
|
|
1032
|
+
onSelect: pick,
|
|
1033
|
+
onDismiss: () => setIsOpen(false),
|
|
1034
|
+
colors: { text: colors.text, border: colors.border, surface: colors.surface, brand: palette.primary["500"] },
|
|
1035
|
+
optionHint,
|
|
1036
|
+
testID,
|
|
1037
|
+
anchorRef
|
|
1038
|
+
}
|
|
1039
|
+
) : null,
|
|
1040
|
+
hasError ? /* @__PURE__ */ jsx(Text, { role: "alert", style: [controlStyles.error, { color: semantic.error["500"] }], testID: `${testID}-error`, children: error }) : null
|
|
1041
|
+
] });
|
|
1042
|
+
}
|
|
1043
|
+
var WEB_DATE_PROPS = typeof document !== "undefined" ? { type: "date" } : {};
|
|
1044
|
+
function DateInput(p) {
|
|
1045
|
+
return /* @__PURE__ */ jsxs(View, { style: controlStyles.dateCol, children: [
|
|
1046
|
+
/* @__PURE__ */ jsx(Text, { style: [controlStyles.subLabel, { color: p.muted }], children: p.label }),
|
|
1047
|
+
/* @__PURE__ */ jsx(
|
|
1048
|
+
TextInput,
|
|
1049
|
+
{
|
|
1050
|
+
accessibilityLabel: p.label,
|
|
1051
|
+
accessibilityHint: p.hint,
|
|
1052
|
+
placeholder: p.placeholder,
|
|
1053
|
+
placeholderTextColor: p.muted,
|
|
1054
|
+
value: p.value,
|
|
1055
|
+
onChangeText: p.onChangeText,
|
|
1056
|
+
onSubmitEditing: p.onSubmit,
|
|
1057
|
+
style: [controlStyles.input, { borderColor: p.border, backgroundColor: p.surface, color: p.color }],
|
|
1058
|
+
testID: `${p.testID}-${p.side}`,
|
|
1059
|
+
...WEB_DATE_PROPS
|
|
1060
|
+
}
|
|
1061
|
+
)
|
|
1062
|
+
] });
|
|
1063
|
+
}
|
|
1064
|
+
function DateRangeControl({
|
|
1065
|
+
value,
|
|
1066
|
+
onChange,
|
|
1067
|
+
onSubmit,
|
|
1068
|
+
fromLabel,
|
|
1069
|
+
toLabel,
|
|
1070
|
+
fromPlaceholder,
|
|
1071
|
+
toPlaceholder,
|
|
1072
|
+
accessibilityHint,
|
|
1073
|
+
style,
|
|
1074
|
+
testID
|
|
1075
|
+
}) {
|
|
1076
|
+
const { theme } = useUi();
|
|
1077
|
+
const { colors } = theme;
|
|
1078
|
+
return /* @__PURE__ */ jsxs(View, { style: [controlStyles.dateRow, style], testID: `${testID}-range`, children: [
|
|
1079
|
+
/* @__PURE__ */ jsx(
|
|
1080
|
+
DateInput,
|
|
1081
|
+
{
|
|
1082
|
+
side: "from",
|
|
1083
|
+
label: fromLabel,
|
|
1084
|
+
placeholder: fromPlaceholder,
|
|
1085
|
+
value: value.from,
|
|
1086
|
+
color: colors.text,
|
|
1087
|
+
border: colors.border,
|
|
1088
|
+
surface: colors.surface,
|
|
1089
|
+
muted: colors.textSecondary,
|
|
1090
|
+
hint: accessibilityHint,
|
|
1091
|
+
testID,
|
|
1092
|
+
onChangeText: (from) => onChange({ from, to: value.to }),
|
|
1093
|
+
onSubmit
|
|
1094
|
+
}
|
|
1095
|
+
),
|
|
1096
|
+
/* @__PURE__ */ jsx(
|
|
1097
|
+
DateInput,
|
|
1098
|
+
{
|
|
1099
|
+
side: "to",
|
|
1100
|
+
label: toLabel,
|
|
1101
|
+
placeholder: toPlaceholder,
|
|
1102
|
+
value: value.to,
|
|
1103
|
+
color: colors.text,
|
|
1104
|
+
border: colors.border,
|
|
1105
|
+
surface: colors.surface,
|
|
1106
|
+
muted: colors.textSecondary,
|
|
1107
|
+
hint: accessibilityHint,
|
|
1108
|
+
testID,
|
|
1109
|
+
onChangeText: (to) => onChange({ from: value.from, to }),
|
|
1110
|
+
onSubmit
|
|
1111
|
+
}
|
|
1112
|
+
)
|
|
1113
|
+
] });
|
|
1114
|
+
}
|
|
553
1115
|
|
|
554
|
-
export { ChipSelector, Field, FormCheckbox, FormField, FormSwitch, ThemedTextInput, useThemedInput };
|
|
1116
|
+
export { AnchoredMenu, ChipSelector, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, Field, FormActions, FormCheckbox, FormField, FormSwitch, SelectControl, ThemedTextInput, TypeaheadControl, controlStyles, isUnmatched, suggestOptions, useThemedInput };
|
|
555
1117
|
//# sourceMappingURL=index.mjs.map
|
|
556
1118
|
//# sourceMappingURL=index.mjs.map
|