@dloizides/ui-forms 1.19.0 → 1.21.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/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import React2, { useState, useCallback, useMemo, useEffect, useRef, useLayoutEffect } from 'react';
2
- import { Platform, StyleSheet, TextInput, View, Text, ScrollView, Pressable, Switch, TouchableOpacity } from 'react-native';
2
+ import { Platform, StyleSheet, TextInput, View, Text, useWindowDimensions, ScrollView, Pressable, Switch, TouchableOpacity } from 'react-native';
3
3
  import { useUi } from '@dloizides/ui-feedback';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { Z_INDEX, focusRingOutline, readableScaleInk } from '@dloizides/design-tokens';
6
6
  import { themeFocusRingColor, Button } from '@dloizides/ui-buttons';
7
7
  import { createPortal } from 'react-dom';
@@ -247,6 +247,7 @@ var FORM_GRID_DEFAULT_MIN_WIDTH = 220;
247
247
  var FORM_GRID_DEFAULT_BASIS = 260;
248
248
  var styles2 = StyleSheet.create({
249
249
  grid: {
250
+ alignItems: "flex-start",
250
251
  flexDirection: "row",
251
252
  flexWrap: "wrap"
252
253
  }
@@ -262,6 +263,36 @@ var FormGrid = ({
262
263
  const metrics = React2.useMemo(() => ({ minWidth, basis }), [minWidth, basis]);
263
264
  return /* @__PURE__ */ jsx(FormGridProvider, { value: metrics, children: /* @__PURE__ */ jsx(GapOwnedProvider, { value: true, children: /* @__PURE__ */ jsx(View, { style: [styles2.grid, { gap }, style], testID, children }) }) });
264
265
  };
266
+ var LABEL_LINE_PLACEHOLDER = "\xA0";
267
+ var styles3 = StyleSheet.create({
268
+ labelLine: {
269
+ userSelect: "none"
270
+ }
271
+ });
272
+ var AlignToControl = ({
273
+ children,
274
+ labelVariant,
275
+ spacing,
276
+ style,
277
+ testID
278
+ }) => {
279
+ const spacingStyles = fieldSpacingStyleSet(resolveFieldSpacing(spacing, useIsGapOwned()));
280
+ const labelLineTestID = testID !== void 0 ? `${testID}-label-line` : void 0;
281
+ return /* @__PURE__ */ jsxs(View, { style: [spacingStyles.container, style], testID, children: [
282
+ /* @__PURE__ */ jsx(
283
+ Text,
284
+ {
285
+ accessibilityElementsHidden: true,
286
+ "aria-hidden": true,
287
+ importantForAccessibility: "no",
288
+ style: [spacingStyles.label, resolveLabelVariantStyle(labelVariant), styles3.labelLine],
289
+ testID: labelLineTestID,
290
+ children: LABEL_LINE_PLACEHOLDER
291
+ }
292
+ ),
293
+ children
294
+ ] });
295
+ };
265
296
  var GROW = 1;
266
297
  var NO_GROW = 0;
267
298
  var FormCell = ({
@@ -269,6 +300,7 @@ var FormCell = ({
269
300
  minWidth,
270
301
  basis,
271
302
  grow = true,
303
+ alignToControl = false,
272
304
  style,
273
305
  testID
274
306
  }) => {
@@ -280,11 +312,54 @@ var FormCell = ({
280
312
  flexBasis: resolvedBasis,
281
313
  flexGrow: grow ? GROW : NO_GROW
282
314
  };
283
- return /* @__PURE__ */ jsx(View, { style: [cellStyle, style], testID, children });
315
+ return /* @__PURE__ */ jsx(View, { style: [cellStyle, style], testID, children: alignToControl ? /* @__PURE__ */ jsx(AlignToControl, { children }) : children });
316
+ };
317
+
318
+ // src/InlineFieldRow/inlineFieldRowLayout.ts
319
+ var INLINE_FIELD_ROW_MIN_WIDTH = 1024;
320
+ function inlineFieldRowLayout(windowWidth, fieldMaxWidth, inlineMinWidth = INLINE_FIELD_ROW_MIN_WIDTH) {
321
+ const inline = windowWidth >= inlineMinWidth;
322
+ return { inline, fieldMaxWidth: inline ? fieldMaxWidth : void 0 };
323
+ }
324
+ var styles4 = StyleSheet.create({
325
+ action: {
326
+ flexShrink: 0
327
+ },
328
+ field: {
329
+ flexBasis: 0,
330
+ flexGrow: 1,
331
+ flexShrink: 1
332
+ },
333
+ row: {
334
+ alignItems: "flex-start",
335
+ flexDirection: "row"
336
+ }
337
+ });
338
+ var InlineFieldRow = ({
339
+ field,
340
+ action,
341
+ fieldMaxWidth,
342
+ inlineMinWidth,
343
+ gap = FORM_GRID_DEFAULT_GAP,
344
+ testID
345
+ }) => {
346
+ const { width } = useWindowDimensions();
347
+ const layout = inlineFieldRowLayout(width, fieldMaxWidth, inlineMinWidth);
348
+ const rowStyle = useMemo(() => [styles4.row, { gap }], [gap]);
349
+ const fieldStyle = useMemo(() => [styles4.field, { maxWidth: layout.fieldMaxWidth }], [layout.fieldMaxWidth]);
350
+ if (!layout.inline)
351
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
352
+ field,
353
+ action
354
+ ] });
355
+ return /* @__PURE__ */ jsxs(View, { style: rowStyle, testID, children: [
356
+ /* @__PURE__ */ jsx(View, { style: fieldStyle, children: field }),
357
+ /* @__PURE__ */ jsx(AlignToControl, { style: styles4.action, children: action })
358
+ ] });
284
359
  };
285
360
  var SECTION_GAP = 10;
286
361
  var DESCRIPTION_FONT_SIZE = 12;
287
- var styles3 = StyleSheet.create({
362
+ var styles5 = StyleSheet.create({
288
363
  section: {
289
364
  gap: SECTION_GAP
290
365
  },
@@ -319,7 +394,7 @@ var FormSection = ({
319
394
  // The section spaces its children with `gap`, so it owns their vertical rhythm exactly as a
320
395
  // `FormGrid` owns a row's — without this a `Field` placed directly in a section would stack its
321
396
  // 16px margin on the section's gap and recreate the F3 bug one level up.
322
- /* @__PURE__ */ jsx(GapOwnedProvider, { value: true, children: /* @__PURE__ */ jsxs(View, { role: "group", style: [styles3.section, style], testID, ...a11y, children: [
397
+ /* @__PURE__ */ jsx(GapOwnedProvider, { value: true, children: /* @__PURE__ */ jsxs(View, { role: "group", style: [styles5.section, style], testID, ...a11y, children: [
323
398
  hasTitle ? /* @__PURE__ */ jsx(
324
399
  Text,
325
400
  {
@@ -328,13 +403,13 @@ var FormSection = ({
328
403
  children: title
329
404
  }
330
405
  ) : null,
331
- hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles3.description, { color: colors.textSecondary }], children: description }) : null,
406
+ hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles5.description, { color: colors.textSecondary }], children: description }) : null,
332
407
  children
333
408
  ] }) })
334
409
  );
335
410
  };
336
411
  var ACTIONS_GAP = 12;
337
- var styles4 = StyleSheet.create({
412
+ var styles6 = StyleSheet.create({
338
413
  container: {
339
414
  flexDirection: "row",
340
415
  justifyContent: "flex-end",
@@ -354,7 +429,7 @@ var FormActions = ({
354
429
  saveTestID = "save-button",
355
430
  cancelTestID = "cancel-button",
356
431
  testID
357
- }) => /* @__PURE__ */ jsxs(View, { style: [styles4.container, containerStyle], testID, children: [
432
+ }) => /* @__PURE__ */ jsxs(View, { style: [styles6.container, containerStyle], testID, children: [
358
433
  typeof onCancel === "function" ? /* @__PURE__ */ jsx(
359
434
  Button,
360
435
  {
@@ -1174,7 +1249,7 @@ function chipIdentity(option, selected, config) {
1174
1249
  ariaChecked: isRadio ? selected : void 0
1175
1250
  };
1176
1251
  }
1177
- var styles5 = StyleSheet.create({
1252
+ var styles7 = StyleSheet.create({
1178
1253
  chipContainer: {
1179
1254
  flexDirection: "row",
1180
1255
  flexWrap: "wrap",
@@ -1260,7 +1335,7 @@ var ChipSelector = ({
1260
1335
  View,
1261
1336
  {
1262
1337
  accessibilityRole: isRadioGroup ? "radiogroup" : void 0,
1263
- style: styles5.chipContainer,
1338
+ style: styles7.chipContainer,
1264
1339
  ...webFieldA11y({ describedById, hasError }),
1265
1340
  ...radio.groupProps,
1266
1341
  children: options.map(renderChip)
@@ -1270,7 +1345,7 @@ var ChipSelector = ({
1270
1345
  );
1271
1346
  };
1272
1347
  var DESCRIPTION_FONT_SIZE2 = 12;
1273
- var styles6 = StyleSheet.create({
1348
+ var styles8 = StyleSheet.create({
1274
1349
  container: {
1275
1350
  flexDirection: "row",
1276
1351
  justifyContent: "space-between",
@@ -1331,15 +1406,15 @@ var FormSwitch = ({
1331
1406
  accessible: false,
1332
1407
  disabled,
1333
1408
  onPress: toggle,
1334
- style: [styles6.container, containerStyle],
1409
+ style: [styles8.container, containerStyle],
1335
1410
  tabIndex: -1,
1336
1411
  testID: `${testID}-row`,
1337
1412
  children: [
1338
- /* @__PURE__ */ jsxs(View, { style: styles6.textColumn, children: [
1339
- /* @__PURE__ */ jsx(Text, { style: [styles6.label, themeStyles.label], children: label }),
1340
- hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles6.description, themeStyles.description], children: description }) : null
1413
+ /* @__PURE__ */ jsxs(View, { style: styles8.textColumn, children: [
1414
+ /* @__PURE__ */ jsx(Text, { style: [styles8.label, themeStyles.label], children: label }),
1415
+ hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles8.description, themeStyles.description], children: description }) : null
1341
1416
  ] }),
1342
- /* @__PURE__ */ jsx(View, { pointerEvents: "none", style: styles6.switchSlot, testID: `${testID}-hitbox`, children: /* @__PURE__ */ jsx(
1417
+ /* @__PURE__ */ jsx(View, { pointerEvents: "none", style: styles8.switchSlot, testID: `${testID}-hitbox`, children: /* @__PURE__ */ jsx(
1343
1418
  Switch,
1344
1419
  {
1345
1420
  accessibilityHint: accessibilityHint ?? label,
@@ -1356,13 +1431,19 @@ var FormSwitch = ({
1356
1431
  )
1357
1432
  );
1358
1433
  };
1434
+
1435
+ // src/FormCheckbox/checkboxColors.ts
1436
+ var checkboxBoxColors = (colors, brand, checked) => checked ? { backgroundColor: brand, borderColor: brand } : { backgroundColor: "transparent", borderColor: colors.textSecondary };
1359
1437
  var ACTIVATION_KEYS = ["Enter", " ", "Spacebar"];
1360
1438
  var HINT_FONT_SIZE2 = 12;
1361
1439
  var BOX_SIZE = 20;
1362
1440
  var BOX_RADIUS = 5;
1363
1441
  var CHECK_FONT_SIZE = 14;
1364
1442
  var DISABLED_OPACITY = 0.5;
1365
- var styles7 = StyleSheet.create({
1443
+ var FOCUS_RING_WIDTH = 2;
1444
+ var WEB_NO_DEFAULT_OUTLINE = { outlineStyle: "none" };
1445
+ var FOCUS_RING_INSET = -6;
1446
+ var styles9 = StyleSheet.create({
1366
1447
  container: {
1367
1448
  flexDirection: "row",
1368
1449
  alignItems: "flex-start",
@@ -1386,6 +1467,15 @@ var styles7 = StyleSheet.create({
1386
1467
  // Nudge the box to sit on the label's first text line.
1387
1468
  marginTop: 1
1388
1469
  },
1470
+ focusRing: {
1471
+ position: "absolute",
1472
+ top: FOCUS_RING_INSET,
1473
+ left: FOCUS_RING_INSET,
1474
+ right: FOCUS_RING_INSET,
1475
+ bottom: FOCUS_RING_INSET,
1476
+ borderWidth: FOCUS_RING_WIDTH,
1477
+ borderRadius: BOX_RADIUS - FOCUS_RING_INSET
1478
+ },
1389
1479
  check: {
1390
1480
  fontSize: CHECK_FONT_SIZE,
1391
1481
  fontWeight: "700",
@@ -1421,13 +1511,15 @@ var FormCheckbox = ({
1421
1511
  const hintText = typeof hint === "string" && hint !== "" ? hint : description;
1422
1512
  const hasHint = typeof hintText === "string" && hintText !== "";
1423
1513
  const { hintId } = useFieldIds();
1514
+ const [focused, setFocused] = React2.useState(false);
1424
1515
  const themeStyles = React2.useMemo(() => {
1425
- const boxOn = { backgroundColor: brand, borderColor: brand };
1426
- const boxOff = { backgroundColor: "transparent", borderColor: colors.border };
1516
+ const boxOn = checkboxBoxColors(colors, brand, true);
1517
+ const boxOff = checkboxBoxColors(colors, brand, false);
1518
+ const ring = { borderColor: brand };
1427
1519
  const labelStyle = { color: colors.text };
1428
1520
  const hintStyle = { color: colors.textSecondary };
1429
- return { boxOn, boxOff, label: labelStyle, hint: hintStyle };
1430
- }, [brand, colors.border, colors.text, colors.textSecondary]);
1521
+ return { boxOn, boxOff, ring, label: labelStyle, hint: hintStyle };
1522
+ }, [brand, colors]);
1431
1523
  const toggle = () => {
1432
1524
  const next = !value;
1433
1525
  if (onValueChange) onValueChange(next);
@@ -1451,21 +1543,27 @@ var FormCheckbox = ({
1451
1543
  "aria-checked": value,
1452
1544
  "aria-describedby": hasHint ? hintId : void 0,
1453
1545
  disabled,
1546
+ onBlur: () => setFocused(false),
1547
+ onFocus: () => setFocused(true),
1454
1548
  onPress: toggle,
1455
1549
  role: "checkbox",
1456
1550
  style: [
1457
- styles7.container,
1458
- hasHint ? null : styles7.centered,
1551
+ styles9.container,
1552
+ hasHint ? null : styles9.centered,
1459
1553
  containerStyle,
1460
- disabled ? { opacity: DISABLED_OPACITY } : null
1554
+ disabled ? { opacity: DISABLED_OPACITY } : null,
1555
+ Platform.OS === "web" ? WEB_NO_DEFAULT_OUTLINE : null
1461
1556
  ],
1462
1557
  testID,
1463
1558
  ...keyboardProps,
1464
1559
  children: [
1465
- /* @__PURE__ */ jsx(View, { style: [styles7.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsx(Text, { style: [styles7.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
1466
- /* @__PURE__ */ jsxs(View, { style: styles7.textColumn, children: [
1467
- /* @__PURE__ */ jsx(Text, { style: [styles7.label, themeStyles.label], children: label }),
1468
- hasHint ? /* @__PURE__ */ jsx(Text, { nativeID: hintId, style: [styles7.hint, themeStyles.hint], children: hintText }) : null
1560
+ /* @__PURE__ */ jsxs(View, { style: [styles9.box, value ? themeStyles.boxOn : themeStyles.boxOff], testID: `${testID}-box`, children: [
1561
+ focused ? /* @__PURE__ */ jsx(View, { pointerEvents: "none", style: [styles9.focusRing, themeStyles.ring], testID: `${testID}-focus-ring` }) : null,
1562
+ value ? /* @__PURE__ */ jsx(Text, { style: [styles9.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null
1563
+ ] }),
1564
+ /* @__PURE__ */ jsxs(View, { style: styles9.textColumn, children: [
1565
+ /* @__PURE__ */ jsx(Text, { style: [styles9.label, themeStyles.label], children: label }),
1566
+ hasHint ? /* @__PURE__ */ jsx(Text, { nativeID: hintId, style: [styles9.hint, themeStyles.hint], children: hintText }) : null
1469
1567
  ] })
1470
1568
  ]
1471
1569
  }
@@ -1757,6 +1855,6 @@ function toggleGroupSelection(selected, groupKeys, order) {
1757
1855
  return order === void 0 ? merged : order.filter((key) => merged.includes(key));
1758
1856
  }
1759
1857
 
1760
- export { AnchoredMenu, ChipSelector, DEFAULT_MAX_RECENTS, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, Field, FormActions, FormCell, FormCheckbox, FormField, FormGrid, FormSection, FormSwitch, SelectControl, ThemedTextInput, TypeaheadControl, controlStyles, groupAriaChecked, isUnmatched, resolveGroupSelection, suggestOptions, toggleGroupSelection, useRecentValues, useThemedInput };
1858
+ export { AlignToControl, AnchoredMenu, ChipSelector, DEFAULT_MAX_RECENTS, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, Field, FormActions, FormCell, FormCheckbox, FormField, FormGrid, FormSection, FormSwitch, INLINE_FIELD_ROW_MIN_WIDTH, InlineFieldRow, SelectControl, ThemedTextInput, TypeaheadControl, controlStyles, groupAriaChecked, inlineFieldRowLayout, isUnmatched, resolveGroupSelection, suggestOptions, toggleGroupSelection, useRecentValues, useThemedInput };
1761
1859
  //# sourceMappingURL=index.mjs.map
1762
1860
  //# sourceMappingURL=index.mjs.map