@dloizides/ui-forms 1.5.0 → 1.7.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 +88 -0
- package/README.md +107 -75
- package/dist/index.d.mts +182 -10
- package/dist/index.d.ts +182 -10
- package/dist/index.js +260 -103
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +249 -94
- package/dist/index.mjs.map +1 -1
- package/package.json +110 -108
package/dist/index.js
CHANGED
|
@@ -1,51 +1,122 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React = 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
|
+
var uiButtons = require('@dloizides/ui-buttons');
|
|
7
8
|
|
|
8
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
10
|
|
|
10
|
-
var
|
|
11
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
11
12
|
|
|
12
13
|
// src/Field/Field.tsx
|
|
14
|
+
function FieldLabel({
|
|
15
|
+
label,
|
|
16
|
+
marker,
|
|
17
|
+
optionalLabel,
|
|
18
|
+
style,
|
|
19
|
+
requiredMarkStyle,
|
|
20
|
+
optionalMarkStyle
|
|
21
|
+
}) {
|
|
22
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style, children: [
|
|
23
|
+
label,
|
|
24
|
+
" ",
|
|
25
|
+
marker === "required" ? (
|
|
26
|
+
// The asterisk is decorative — the control's `aria-required` conveys "required" to
|
|
27
|
+
// assistive tech, so a screen reader says "required" instead of "star".
|
|
28
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: requiredMarkStyle, children: "*" })
|
|
29
|
+
) : null,
|
|
30
|
+
marker === "optional" && optionalLabel !== "" ? (
|
|
31
|
+
// Also decorative: "optional" is the ABSENCE of `aria-required`, which assistive tech
|
|
32
|
+
// already conveys. Rendering it to a screen reader would double-announce the same fact.
|
|
33
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: optionalMarkStyle, children: optionalLabel })
|
|
34
|
+
) : null
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/Field/fieldMarkers.ts
|
|
39
|
+
var DEFAULT_OPTIONAL_LABEL = "(optional)";
|
|
40
|
+
function resolveFieldMarker(required, optional) {
|
|
41
|
+
if (required) return "required";
|
|
42
|
+
if (optional) return "optional";
|
|
43
|
+
return "none";
|
|
44
|
+
}
|
|
45
|
+
var FIELD_FONT_SIZE = 13;
|
|
46
|
+
var FIELD_FONT_WEIGHT = "600";
|
|
47
|
+
var CONTROL_FONT_SIZE = 11;
|
|
48
|
+
var CONTROL_FONT_WEIGHT = "700";
|
|
49
|
+
var CONTROL_LETTER_SPACING = 0.4;
|
|
50
|
+
var labelVariantStyles = reactNative.StyleSheet.create({
|
|
51
|
+
field: {
|
|
52
|
+
fontSize: FIELD_FONT_SIZE,
|
|
53
|
+
fontWeight: FIELD_FONT_WEIGHT
|
|
54
|
+
},
|
|
55
|
+
control: {
|
|
56
|
+
fontSize: CONTROL_FONT_SIZE,
|
|
57
|
+
fontWeight: CONTROL_FONT_WEIGHT,
|
|
58
|
+
letterSpacing: CONTROL_LETTER_SPACING,
|
|
59
|
+
textTransform: "uppercase"
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
function resolveLabelVariantStyle(variant) {
|
|
63
|
+
return variant === "control" ? labelVariantStyles.control : labelVariantStyles.field;
|
|
64
|
+
}
|
|
13
65
|
var CONTAINER_MARGIN_BOTTOM = 16;
|
|
14
|
-
var LABEL_FONT_SIZE = 13;
|
|
15
|
-
var LABEL_FONT_WEIGHT = "600";
|
|
16
66
|
var LABEL_MARGIN_BOTTOM = 4;
|
|
17
67
|
var ERROR_FONT_SIZE = 12;
|
|
18
68
|
var ERROR_MARGIN_TOP = 4;
|
|
69
|
+
var HINT_FONT_SIZE = 12;
|
|
70
|
+
var HINT_MARGIN_TOP = 4;
|
|
71
|
+
var OPTIONAL_MARK_FONT_WEIGHT = "400";
|
|
19
72
|
var styles = reactNative.StyleSheet.create({
|
|
20
73
|
container: {
|
|
21
74
|
marginBottom: CONTAINER_MARGIN_BOTTOM
|
|
22
75
|
},
|
|
23
76
|
label: {
|
|
24
|
-
marginBottom: LABEL_MARGIN_BOTTOM
|
|
25
|
-
|
|
26
|
-
|
|
77
|
+
marginBottom: LABEL_MARGIN_BOTTOM
|
|
78
|
+
},
|
|
79
|
+
hintText: {
|
|
80
|
+
fontSize: HINT_FONT_SIZE,
|
|
81
|
+
marginTop: HINT_MARGIN_TOP
|
|
27
82
|
},
|
|
28
83
|
errorText: {
|
|
29
84
|
fontSize: ERROR_FONT_SIZE,
|
|
30
85
|
marginTop: ERROR_MARGIN_TOP
|
|
86
|
+
},
|
|
87
|
+
optionalMark: {
|
|
88
|
+
fontWeight: OPTIONAL_MARK_FONT_WEIGHT
|
|
31
89
|
}
|
|
32
90
|
});
|
|
33
91
|
var fieldSeq = 0;
|
|
34
|
-
function
|
|
35
|
-
const [
|
|
92
|
+
function useFieldIds() {
|
|
93
|
+
const [ids] = React__default.default.useState(() => {
|
|
36
94
|
fieldSeq += 1;
|
|
37
|
-
|
|
95
|
+
const seq = String(fieldSeq);
|
|
96
|
+
return { hintId: `field-hint-${seq}`, errorId: `field-error-${seq}` };
|
|
38
97
|
});
|
|
39
|
-
return
|
|
98
|
+
return ids;
|
|
99
|
+
}
|
|
100
|
+
function isPresent(value) {
|
|
101
|
+
return typeof value === "string" && value !== "";
|
|
40
102
|
}
|
|
41
103
|
function hasFieldError(error) {
|
|
42
|
-
return
|
|
104
|
+
return isPresent(error);
|
|
105
|
+
}
|
|
106
|
+
function joinDescribedBy(hintId, errorId) {
|
|
107
|
+
const ids = [hintId, errorId].filter((id) => isPresent(id));
|
|
108
|
+
return ids.length > 0 ? ids.join(" ") : void 0;
|
|
43
109
|
}
|
|
44
110
|
var Field = ({
|
|
45
111
|
label,
|
|
46
112
|
children,
|
|
47
113
|
required = false,
|
|
114
|
+
optional = false,
|
|
115
|
+
optionalLabel = DEFAULT_OPTIONAL_LABEL,
|
|
116
|
+
hint,
|
|
48
117
|
error,
|
|
118
|
+
labelVariant,
|
|
119
|
+
labelStyle,
|
|
49
120
|
containerStyle,
|
|
50
121
|
testID
|
|
51
122
|
}) => {
|
|
@@ -53,30 +124,89 @@ var Field = ({
|
|
|
53
124
|
const { colors, semantic } = theme;
|
|
54
125
|
const errorColor = semantic.error["500"];
|
|
55
126
|
const hasError = hasFieldError(error);
|
|
56
|
-
const
|
|
57
|
-
const
|
|
127
|
+
const hasHint = isPresent(hint);
|
|
128
|
+
const labelText = label ?? "";
|
|
129
|
+
const hasLabel = labelText !== "";
|
|
130
|
+
const marker = resolveFieldMarker(required, optional);
|
|
131
|
+
const { hintId, errorId } = useFieldIds();
|
|
132
|
+
const themeStyles = React__default.default.useMemo(
|
|
58
133
|
() => ({
|
|
59
134
|
label: { color: colors.textSecondary },
|
|
60
135
|
requiredMark: { color: errorColor },
|
|
136
|
+
// The optional marker is an aside, so it sits a step back from the label's own ink.
|
|
137
|
+
optionalMark: { color: colors.textSecondary },
|
|
138
|
+
hintText: { color: colors.textSecondary },
|
|
61
139
|
errorText: { color: errorColor }
|
|
62
140
|
}),
|
|
63
141
|
[colors.textSecondary, errorColor]
|
|
64
142
|
);
|
|
65
|
-
const
|
|
143
|
+
const describedById = joinDescribedBy(hasHint ? hintId : void 0, hasError ? errorId : void 0);
|
|
144
|
+
const control = typeof children === "function" ? children({ describedById, hasError }) : children;
|
|
66
145
|
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.container, containerStyle], testID, children: [
|
|
67
|
-
/* @__PURE__ */ jsxRuntime.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
146
|
+
hasLabel ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
147
|
+
FieldLabel,
|
|
148
|
+
{
|
|
149
|
+
label: labelText,
|
|
150
|
+
marker,
|
|
151
|
+
optionalLabel,
|
|
152
|
+
optionalMarkStyle: [styles.optionalMark, themeStyles.optionalMark],
|
|
153
|
+
requiredMarkStyle: themeStyles.requiredMark,
|
|
154
|
+
style: [styles.label, resolveLabelVariantStyle(labelVariant), themeStyles.label, labelStyle]
|
|
155
|
+
}
|
|
156
|
+
) : null,
|
|
76
157
|
control,
|
|
158
|
+
hasHint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { nativeID: hintId, style: [styles.hintText, themeStyles.hintText], children: hint }) : null,
|
|
77
159
|
hasError ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { nativeID: errorId, role: "alert", style: [styles.errorText, themeStyles.errorText], children: error }) : null
|
|
78
160
|
] });
|
|
79
161
|
};
|
|
162
|
+
var ACTIONS_GAP = 12;
|
|
163
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
164
|
+
container: {
|
|
165
|
+
flexDirection: "row",
|
|
166
|
+
justifyContent: "flex-end",
|
|
167
|
+
gap: ACTIONS_GAP
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
var FormActions = ({
|
|
171
|
+
onSave,
|
|
172
|
+
onCancel,
|
|
173
|
+
saveLabel,
|
|
174
|
+
cancelLabel = "",
|
|
175
|
+
saveHint,
|
|
176
|
+
cancelHint,
|
|
177
|
+
saving = false,
|
|
178
|
+
saveDisabled = false,
|
|
179
|
+
containerStyle,
|
|
180
|
+
saveTestID = "save-button",
|
|
181
|
+
cancelTestID = "cancel-button",
|
|
182
|
+
testID
|
|
183
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles2.container, containerStyle], testID, children: [
|
|
184
|
+
typeof onCancel === "function" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
185
|
+
uiButtons.Button,
|
|
186
|
+
{
|
|
187
|
+
accessibilityHint: cancelHint ?? cancelLabel,
|
|
188
|
+
accessibilityLabel: cancelLabel,
|
|
189
|
+
disabled: saving,
|
|
190
|
+
label: cancelLabel,
|
|
191
|
+
testID: cancelTestID,
|
|
192
|
+
variant: "outline",
|
|
193
|
+
onPress: onCancel
|
|
194
|
+
}
|
|
195
|
+
) : null,
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
197
|
+
uiButtons.Button,
|
|
198
|
+
{
|
|
199
|
+
accessibilityHint: saveHint ?? saveLabel,
|
|
200
|
+
accessibilityLabel: saveLabel,
|
|
201
|
+
disabled: saveDisabled,
|
|
202
|
+
label: saveLabel,
|
|
203
|
+
loading: saving,
|
|
204
|
+
testID: saveTestID,
|
|
205
|
+
variant: "primary",
|
|
206
|
+
onPress: onSave
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
] });
|
|
80
210
|
var IS_WEB = reactNative.Platform.OS === "web";
|
|
81
211
|
var TRANSITION_PROPERTY = "border-color, background-color, box-shadow";
|
|
82
212
|
var TRANSITION_DURATION = "0.14s";
|
|
@@ -109,34 +239,35 @@ function useThemedInput(options = {}) {
|
|
|
109
239
|
const { hasError = false, onFocus, onBlur } = options;
|
|
110
240
|
const { theme } = uiFeedback.useUi();
|
|
111
241
|
const { colors, palette, semantic } = theme;
|
|
112
|
-
const [isFocused, setIsFocused] =
|
|
113
|
-
const [isHovered, setIsHovered] =
|
|
114
|
-
const handleFocus =
|
|
242
|
+
const [isFocused, setIsFocused] = React.useState(false);
|
|
243
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
244
|
+
const handleFocus = React.useCallback(
|
|
115
245
|
(e) => {
|
|
116
246
|
setIsFocused(true);
|
|
117
247
|
onFocus?.(e);
|
|
118
248
|
},
|
|
119
249
|
[onFocus]
|
|
120
250
|
);
|
|
121
|
-
const handleBlur =
|
|
251
|
+
const handleBlur = React.useCallback(
|
|
122
252
|
(e) => {
|
|
123
253
|
setIsFocused(false);
|
|
124
254
|
onBlur?.(e);
|
|
125
255
|
},
|
|
126
256
|
[onBlur]
|
|
127
257
|
);
|
|
128
|
-
const handleHoverIn =
|
|
129
|
-
const handleHoverOut =
|
|
258
|
+
const handleHoverIn = React.useCallback(() => setIsHovered(true), []);
|
|
259
|
+
const handleHoverOut = React.useCallback(() => setIsHovered(false), []);
|
|
130
260
|
const primary = palette.primary["500"];
|
|
131
261
|
const errorColor = semantic.error["500"];
|
|
132
|
-
const style =
|
|
262
|
+
const style = React.useMemo(() => {
|
|
133
263
|
const restBorder = hasError ? errorColor : colors.border;
|
|
134
264
|
const focusBorder = hasError ? errorColor : primary;
|
|
135
265
|
const borderColor = isFocused ? focusBorder : isHovered ? withAlpha(colors.text, HOVER_BORDER_ALPHA) : restBorder;
|
|
136
266
|
const backgroundColor = isFocused ? colors.surface : colors.surfaceElevated;
|
|
137
267
|
const base = { color: colors.text, borderColor, backgroundColor };
|
|
138
268
|
if (!IS_WEB) return base;
|
|
139
|
-
const
|
|
269
|
+
const ringColor = hasError ? errorColor : primary;
|
|
270
|
+
const ring = isFocused ? `0 0 0 ${FOCUS_RING_WIDTH}px ${withAlpha(ringColor, FOCUS_RING_ALPHA)}` : "none";
|
|
140
271
|
const webStyle = {
|
|
141
272
|
transitionProperty: TRANSITION_PROPERTY,
|
|
142
273
|
// Collapse the eased transition to instant when the user prefers reduced motion.
|
|
@@ -157,8 +288,8 @@ function useThemedInput(options = {}) {
|
|
|
157
288
|
primary,
|
|
158
289
|
errorColor
|
|
159
290
|
]);
|
|
160
|
-
const focusBind =
|
|
161
|
-
const hoverBind =
|
|
291
|
+
const focusBind = React.useMemo(() => ({ onFocus: handleFocus, onBlur: handleBlur }), [handleFocus, handleBlur]);
|
|
292
|
+
const hoverBind = React.useMemo(
|
|
162
293
|
() => IS_WEB ? { onMouseEnter: handleHoverIn, onMouseLeave: handleHoverOut } : {},
|
|
163
294
|
[handleHoverIn, handleHoverOut]
|
|
164
295
|
);
|
|
@@ -172,7 +303,7 @@ function useThemedInput(options = {}) {
|
|
|
172
303
|
};
|
|
173
304
|
}
|
|
174
305
|
var IS_WEB2 = reactNative.Platform.OS === "web";
|
|
175
|
-
var ThemedTextInput =
|
|
306
|
+
var ThemedTextInput = React__default.default.forwardRef(function ThemedTextInput2({ hasError = false, describedById, requiredField = false, onFocus, onBlur, style, placeholderTextColor, ...rest }, ref) {
|
|
176
307
|
const {
|
|
177
308
|
style: themedStyle,
|
|
178
309
|
placeholderTextColor: themedPlaceholder,
|
|
@@ -202,7 +333,7 @@ var INPUT_PADDING_V = 10;
|
|
|
202
333
|
var INPUT_PADDING_H = 12;
|
|
203
334
|
var INPUT_BORDER_RADIUS = 8;
|
|
204
335
|
var INPUT_BORDER_WIDTH = 1;
|
|
205
|
-
var
|
|
336
|
+
var styles3 = reactNative.StyleSheet.create({
|
|
206
337
|
input: {
|
|
207
338
|
paddingVertical: INPUT_PADDING_V,
|
|
208
339
|
paddingHorizontal: INPUT_PADDING_H,
|
|
@@ -224,7 +355,7 @@ var FormField = ({
|
|
|
224
355
|
describedById,
|
|
225
356
|
hasError,
|
|
226
357
|
requiredField: required,
|
|
227
|
-
style:
|
|
358
|
+
style: styles3.input,
|
|
228
359
|
testID: "form-field-input",
|
|
229
360
|
...textInputProps
|
|
230
361
|
}
|
|
@@ -244,6 +375,14 @@ var RGB_R_END = 2;
|
|
|
244
375
|
var RGB_G_END = 4;
|
|
245
376
|
var RGB_B_END = 6;
|
|
246
377
|
var CHIP_HIT_SLOP = { top: 10, bottom: 10, left: 4, right: 4 };
|
|
378
|
+
var CHIP_GUTTER = 8;
|
|
379
|
+
var DEFAULT_CHIP_TESTID_PREFIX = "chip-selector-chip";
|
|
380
|
+
var ON_BRAND_SCALE = "onBrand";
|
|
381
|
+
var MAIN_STEP = "500";
|
|
382
|
+
function onBrandInk(palette) {
|
|
383
|
+
const scales = palette;
|
|
384
|
+
return scales[ON_BRAND_SCALE]?.[MAIN_STEP] ?? WHITE_COLOR;
|
|
385
|
+
}
|
|
247
386
|
function withAlpha2(hex, alpha) {
|
|
248
387
|
const normalized = hex.replace("#", "");
|
|
249
388
|
const isShort = normalized.length === HEX_SHORT_LENGTH2;
|
|
@@ -254,18 +393,18 @@ function withAlpha2(hex, alpha) {
|
|
|
254
393
|
const isValid = !Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b);
|
|
255
394
|
return isValid ? `rgba(${r}, ${g}, ${b}, ${alpha})` : hex;
|
|
256
395
|
}
|
|
257
|
-
var
|
|
258
|
-
label: {
|
|
259
|
-
marginBottom: 8,
|
|
260
|
-
fontWeight: "600"
|
|
261
|
-
},
|
|
396
|
+
var styles4 = reactNative.StyleSheet.create({
|
|
262
397
|
chipContainer: {
|
|
263
398
|
flexDirection: "row",
|
|
264
|
-
flexWrap: "wrap"
|
|
399
|
+
flexWrap: "wrap",
|
|
400
|
+
// Each chip's bottom gutter also hangs off the LAST row, stacking on `Field`'s container
|
|
401
|
+
// margin (8 + 16 = 24) and dropping the block below the `FormField` beside it. Cancelling it
|
|
402
|
+
// is standard gutter compensation: inner rows keep their 8px, the block ends at Field's 16.
|
|
403
|
+
marginBottom: -CHIP_GUTTER
|
|
265
404
|
},
|
|
266
405
|
chipWrapper: {
|
|
267
|
-
marginRight:
|
|
268
|
-
marginBottom:
|
|
406
|
+
marginRight: CHIP_GUTTER,
|
|
407
|
+
marginBottom: CHIP_GUTTER
|
|
269
408
|
},
|
|
270
409
|
chip: {
|
|
271
410
|
paddingHorizontal: 12,
|
|
@@ -283,14 +422,26 @@ var styles3 = reactNative.StyleSheet.create({
|
|
|
283
422
|
chipText: {},
|
|
284
423
|
chipTextOutline: { fontSize: OUTLINE_FONT_SIZE }
|
|
285
424
|
});
|
|
425
|
+
function chipIdentity(option, selected, config) {
|
|
426
|
+
return {
|
|
427
|
+
testID: `${config.prefix}-${String(option.value)}`,
|
|
428
|
+
// The English default is retained verbatim: erevna/katalogos assert the exact string
|
|
429
|
+
// `Selects Red`. Consumers that localize pass `optionAccessibilityHint`.
|
|
430
|
+
accessibilityHint: config.hint ?? `Selects ${option.label}`,
|
|
431
|
+
ariaPressed: config.multiple ? selected : void 0
|
|
432
|
+
};
|
|
433
|
+
}
|
|
286
434
|
function OutlineChip({
|
|
287
435
|
option,
|
|
288
436
|
selected,
|
|
289
437
|
disabled,
|
|
290
438
|
colors,
|
|
439
|
+
testID,
|
|
440
|
+
accessibilityHint,
|
|
441
|
+
ariaPressed,
|
|
291
442
|
onPress
|
|
292
443
|
}) {
|
|
293
|
-
const [hovered, setHovered] =
|
|
444
|
+
const [hovered, setHovered] = React__default.default.useState(false);
|
|
294
445
|
const active = selected || hovered;
|
|
295
446
|
const borderColor = active ? colors.primary : colors.border;
|
|
296
447
|
const backgroundColor = selected ? colors.tint : colors.muted;
|
|
@@ -298,18 +449,19 @@ function OutlineChip({
|
|
|
298
449
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
299
450
|
reactNative.Pressable,
|
|
300
451
|
{
|
|
301
|
-
accessibilityHint
|
|
452
|
+
accessibilityHint,
|
|
302
453
|
accessibilityLabel: option.label,
|
|
303
454
|
accessibilityRole: "button",
|
|
304
455
|
accessibilityState: { selected, disabled },
|
|
456
|
+
"aria-pressed": ariaPressed,
|
|
305
457
|
disabled,
|
|
306
458
|
hitSlop: CHIP_HIT_SLOP,
|
|
307
|
-
style:
|
|
308
|
-
testID
|
|
459
|
+
style: styles4.chipWrapper,
|
|
460
|
+
testID,
|
|
309
461
|
onHoverIn: () => setHovered(true),
|
|
310
462
|
onHoverOut: () => setHovered(false),
|
|
311
463
|
onPress,
|
|
312
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
464
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.chipText, styles4.chipTextOutline, { color: textColor }], children: option.label }) })
|
|
313
465
|
}
|
|
314
466
|
);
|
|
315
467
|
}
|
|
@@ -318,26 +470,35 @@ function SolidChip({
|
|
|
318
470
|
selected,
|
|
319
471
|
disabled,
|
|
320
472
|
colors,
|
|
473
|
+
testID,
|
|
474
|
+
accessibilityHint,
|
|
475
|
+
ariaPressed,
|
|
321
476
|
onPress
|
|
322
477
|
}) {
|
|
323
478
|
const backgroundColor = selected ? colors.primary : TRANSPARENT_COLOR;
|
|
324
|
-
const textColor = selected ?
|
|
479
|
+
const textColor = selected ? colors.onPrimary : colors.textStrong;
|
|
325
480
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
326
481
|
reactNative.TouchableOpacity,
|
|
327
482
|
{
|
|
328
|
-
accessibilityHint
|
|
483
|
+
accessibilityHint,
|
|
329
484
|
accessibilityLabel: option.label,
|
|
330
485
|
accessibilityRole: "button",
|
|
331
486
|
accessibilityState: { selected, disabled },
|
|
487
|
+
"aria-pressed": ariaPressed,
|
|
332
488
|
disabled,
|
|
333
489
|
hitSlop: CHIP_HIT_SLOP,
|
|
334
|
-
style:
|
|
335
|
-
testID
|
|
490
|
+
style: styles4.chipWrapper,
|
|
491
|
+
testID,
|
|
336
492
|
onPress,
|
|
337
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
493
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.chipText, { color: textColor }], children: option.label }) })
|
|
338
494
|
}
|
|
339
495
|
);
|
|
340
496
|
}
|
|
497
|
+
var IS_WEB3 = reactNative.Platform.OS === "web";
|
|
498
|
+
function groupA11yProps(describedById, hasError) {
|
|
499
|
+
if (!IS_WEB3) return {};
|
|
500
|
+
return { "aria-invalid": hasError ? true : void 0, "aria-describedby": describedById };
|
|
501
|
+
}
|
|
341
502
|
var ChipSelector = ({
|
|
342
503
|
label,
|
|
343
504
|
options,
|
|
@@ -346,59 +507,53 @@ var ChipSelector = ({
|
|
|
346
507
|
multiple = false,
|
|
347
508
|
disabled = false,
|
|
348
509
|
variant = "solid",
|
|
349
|
-
containerStyle
|
|
510
|
+
containerStyle,
|
|
511
|
+
required = false,
|
|
512
|
+
error,
|
|
513
|
+
testID,
|
|
514
|
+
chipTestIDPrefix = DEFAULT_CHIP_TESTID_PREFIX,
|
|
515
|
+
optionAccessibilityHint
|
|
350
516
|
}) => {
|
|
351
517
|
const { theme } = uiFeedback.useUi();
|
|
352
518
|
const { colors, palette } = theme;
|
|
353
|
-
const
|
|
354
|
-
const chipColors = React3__default.default.useMemo(() => {
|
|
519
|
+
const chipColors = React__default.default.useMemo(() => {
|
|
355
520
|
const primary = palette.primary["500"];
|
|
356
521
|
return {
|
|
357
522
|
border: colors.border,
|
|
358
523
|
textStrong: colors.text,
|
|
359
524
|
textMuted: colors.textSecondary,
|
|
360
525
|
primary,
|
|
526
|
+
onPrimary: onBrandInk(palette),
|
|
361
527
|
muted: colors.background,
|
|
362
528
|
tint: withAlpha2(primary, OUTLINE_TINT_ALPHA)
|
|
363
529
|
};
|
|
364
|
-
}, [colors.border, colors.text, colors.textSecondary, colors.background, palette
|
|
365
|
-
const labelStyle = React3__default.default.useMemo(() => ({ color: colors.text }), [colors.text]);
|
|
530
|
+
}, [colors.border, colors.text, colors.textSecondary, colors.background, palette]);
|
|
366
531
|
function isSelected(optionValue) {
|
|
367
532
|
if (multiple && Array.isArray(value))
|
|
368
533
|
return value.includes(optionValue);
|
|
369
534
|
return value === optionValue;
|
|
370
535
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
colors: chipColors,
|
|
390
|
-
disabled,
|
|
391
|
-
option,
|
|
392
|
-
selected,
|
|
393
|
-
onPress
|
|
394
|
-
},
|
|
395
|
-
String(option.value)
|
|
396
|
-
);
|
|
397
|
-
}) })
|
|
398
|
-
] });
|
|
536
|
+
function renderChip(option) {
|
|
537
|
+
const selected = isSelected(option.value);
|
|
538
|
+
const chipProps = {
|
|
539
|
+
colors: chipColors,
|
|
540
|
+
disabled,
|
|
541
|
+
option,
|
|
542
|
+
selected,
|
|
543
|
+
...chipIdentity(option, selected, {
|
|
544
|
+
prefix: chipTestIDPrefix,
|
|
545
|
+
hint: optionAccessibilityHint,
|
|
546
|
+
multiple
|
|
547
|
+
}),
|
|
548
|
+
onPress: () => onChange(option.value)
|
|
549
|
+
};
|
|
550
|
+
const key = String(option.value);
|
|
551
|
+
return variant === "outline" ? /* @__PURE__ */ jsxRuntime.jsx(OutlineChip, { ...chipProps }, key) : /* @__PURE__ */ jsxRuntime.jsx(SolidChip, { ...chipProps }, key);
|
|
552
|
+
}
|
|
553
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Field, { containerStyle, error, label, required, testID, children: ({ describedById, hasError }) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.chipContainer, ...groupA11yProps(describedById, hasError), children: options.map(renderChip) }) });
|
|
399
554
|
};
|
|
400
555
|
var DESCRIPTION_FONT_SIZE = 12;
|
|
401
|
-
var
|
|
556
|
+
var styles5 = reactNative.StyleSheet.create({
|
|
402
557
|
container: {
|
|
403
558
|
flexDirection: "row",
|
|
404
559
|
justifyContent: "space-between",
|
|
@@ -429,7 +584,7 @@ var FormSwitch = ({
|
|
|
429
584
|
const { theme } = uiFeedback.useUi();
|
|
430
585
|
const { colors } = theme;
|
|
431
586
|
const hasDescription = typeof description === "string" && description !== "";
|
|
432
|
-
const themeStyles =
|
|
587
|
+
const themeStyles = React__default.default.useMemo(() => {
|
|
433
588
|
const labelStyle = { color: colors.text };
|
|
434
589
|
const descriptionStyle = { color: colors.textSecondary };
|
|
435
590
|
return { label: labelStyle, description: descriptionStyle };
|
|
@@ -438,13 +593,13 @@ var FormSwitch = ({
|
|
|
438
593
|
reactNative.View,
|
|
439
594
|
{
|
|
440
595
|
style: [
|
|
441
|
-
|
|
596
|
+
styles5.container,
|
|
442
597
|
containerStyle
|
|
443
598
|
],
|
|
444
599
|
children: [
|
|
445
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
446
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
447
|
-
hasDescription ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
600
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.textColumn, children: [
|
|
601
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.label, themeStyles.label], children: label }),
|
|
602
|
+
hasDescription ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.description, themeStyles.description], children: description }) : null
|
|
448
603
|
] }),
|
|
449
604
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
450
605
|
reactNative.Switch,
|
|
@@ -464,12 +619,12 @@ var FormSwitch = ({
|
|
|
464
619
|
);
|
|
465
620
|
};
|
|
466
621
|
var ACTIVATION_KEYS = ["Enter", " ", "Spacebar"];
|
|
467
|
-
var
|
|
622
|
+
var HINT_FONT_SIZE2 = 12;
|
|
468
623
|
var BOX_SIZE = 20;
|
|
469
624
|
var BOX_RADIUS = 5;
|
|
470
625
|
var CHECK_FONT_SIZE = 14;
|
|
471
626
|
var DISABLED_OPACITY = 0.5;
|
|
472
|
-
var
|
|
627
|
+
var styles6 = reactNative.StyleSheet.create({
|
|
473
628
|
container: {
|
|
474
629
|
flexDirection: "row",
|
|
475
630
|
alignItems: "flex-start",
|
|
@@ -498,7 +653,7 @@ var styles5 = reactNative.StyleSheet.create({
|
|
|
498
653
|
fontWeight: "600"
|
|
499
654
|
},
|
|
500
655
|
hint: {
|
|
501
|
-
fontSize:
|
|
656
|
+
fontSize: HINT_FONT_SIZE2,
|
|
502
657
|
marginTop: 2
|
|
503
658
|
}
|
|
504
659
|
});
|
|
@@ -520,7 +675,7 @@ var FormCheckbox = ({
|
|
|
520
675
|
const brand = palette.primary["500"];
|
|
521
676
|
const hintText = typeof hint === "string" && hint !== "" ? hint : description;
|
|
522
677
|
const hasHint = typeof hintText === "string" && hintText !== "";
|
|
523
|
-
const themeStyles =
|
|
678
|
+
const themeStyles = React__default.default.useMemo(() => {
|
|
524
679
|
const boxOn = { backgroundColor: brand, borderColor: brand };
|
|
525
680
|
const boxOff = { backgroundColor: "transparent", borderColor: colors.border };
|
|
526
681
|
const labelStyle = { color: colors.text };
|
|
@@ -551,14 +706,14 @@ var FormCheckbox = ({
|
|
|
551
706
|
disabled,
|
|
552
707
|
onPress: toggle,
|
|
553
708
|
role: "checkbox",
|
|
554
|
-
style: [
|
|
709
|
+
style: [styles6.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
|
|
555
710
|
testID,
|
|
556
711
|
...keyboardProps,
|
|
557
712
|
children: [
|
|
558
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
559
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
560
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
561
|
-
hasHint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
713
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
|
|
714
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.textColumn, children: [
|
|
715
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.label, themeStyles.label], children: label }),
|
|
716
|
+
hasHint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.hint, themeStyles.hint], children: hintText }) : null
|
|
562
717
|
] })
|
|
563
718
|
]
|
|
564
719
|
}
|
|
@@ -566,7 +721,9 @@ var FormCheckbox = ({
|
|
|
566
721
|
};
|
|
567
722
|
|
|
568
723
|
exports.ChipSelector = ChipSelector;
|
|
724
|
+
exports.DEFAULT_OPTIONAL_LABEL = DEFAULT_OPTIONAL_LABEL;
|
|
569
725
|
exports.Field = Field;
|
|
726
|
+
exports.FormActions = FormActions;
|
|
570
727
|
exports.FormCheckbox = FormCheckbox;
|
|
571
728
|
exports.FormField = FormField;
|
|
572
729
|
exports.FormSwitch = FormSwitch;
|