@dloizides/ui-forms 1.20.0 → 1.22.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,9 +1,9 @@
1
1
  import React2, { useState, useCallback, useMemo, useEffect, useRef, useLayoutEffect } from 'react';
2
- import { Platform, StyleSheet, TextInput, View, Text, useWindowDimensions, ScrollView, Pressable, Switch, TouchableOpacity } from 'react-native';
2
+ import { Platform, StyleSheet, TextInput, View, Text, useWindowDimensions, ScrollView, Pressable, Switch, Image, TouchableOpacity } from 'react-native';
3
3
  import { useUi } from '@dloizides/ui-feedback';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { Z_INDEX, focusRingOutline, readableScaleInk } from '@dloizides/design-tokens';
6
- import { themeFocusRingColor, Button } from '@dloizides/ui-buttons';
6
+ import { themeFocusRingColor, Button, useFocusRing } from '@dloizides/ui-buttons';
7
7
  import { createPortal } from 'react-dom';
8
8
  import { MIN_TOUCH_TARGET_PX } from '@dloizides/a11y';
9
9
 
@@ -1431,12 +1431,18 @@ var FormSwitch = ({
1431
1431
  )
1432
1432
  );
1433
1433
  };
1434
+
1435
+ // src/FormCheckbox/checkboxColors.ts
1436
+ var checkboxBoxColors = (colors, brand, checked) => checked ? { backgroundColor: brand, borderColor: brand } : { backgroundColor: "transparent", borderColor: colors.textSecondary };
1434
1437
  var ACTIVATION_KEYS = ["Enter", " ", "Spacebar"];
1435
1438
  var HINT_FONT_SIZE2 = 12;
1436
1439
  var BOX_SIZE = 20;
1437
1440
  var BOX_RADIUS = 5;
1438
1441
  var CHECK_FONT_SIZE = 14;
1439
1442
  var DISABLED_OPACITY = 0.5;
1443
+ var FOCUS_RING_WIDTH = 2;
1444
+ var WEB_NO_DEFAULT_OUTLINE = { outlineStyle: "none" };
1445
+ var FOCUS_RING_INSET = -6;
1440
1446
  var styles9 = StyleSheet.create({
1441
1447
  container: {
1442
1448
  flexDirection: "row",
@@ -1461,6 +1467,15 @@ var styles9 = StyleSheet.create({
1461
1467
  // Nudge the box to sit on the label's first text line.
1462
1468
  marginTop: 1
1463
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
+ },
1464
1479
  check: {
1465
1480
  fontSize: CHECK_FONT_SIZE,
1466
1481
  fontWeight: "700",
@@ -1496,13 +1511,15 @@ var FormCheckbox = ({
1496
1511
  const hintText = typeof hint === "string" && hint !== "" ? hint : description;
1497
1512
  const hasHint = typeof hintText === "string" && hintText !== "";
1498
1513
  const { hintId } = useFieldIds();
1514
+ const [focused, setFocused] = React2.useState(false);
1499
1515
  const themeStyles = React2.useMemo(() => {
1500
- const boxOn = { backgroundColor: brand, borderColor: brand };
1501
- 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 };
1502
1519
  const labelStyle = { color: colors.text };
1503
1520
  const hintStyle = { color: colors.textSecondary };
1504
- return { boxOn, boxOff, label: labelStyle, hint: hintStyle };
1505
- }, [brand, colors.border, colors.text, colors.textSecondary]);
1521
+ return { boxOn, boxOff, ring, label: labelStyle, hint: hintStyle };
1522
+ }, [brand, colors]);
1506
1523
  const toggle = () => {
1507
1524
  const next = !value;
1508
1525
  if (onValueChange) onValueChange(next);
@@ -1526,18 +1543,24 @@ var FormCheckbox = ({
1526
1543
  "aria-checked": value,
1527
1544
  "aria-describedby": hasHint ? hintId : void 0,
1528
1545
  disabled,
1546
+ onBlur: () => setFocused(false),
1547
+ onFocus: () => setFocused(true),
1529
1548
  onPress: toggle,
1530
1549
  role: "checkbox",
1531
1550
  style: [
1532
1551
  styles9.container,
1533
1552
  hasHint ? null : styles9.centered,
1534
1553
  containerStyle,
1535
- disabled ? { opacity: DISABLED_OPACITY } : null
1554
+ disabled ? { opacity: DISABLED_OPACITY } : null,
1555
+ Platform.OS === "web" ? WEB_NO_DEFAULT_OUTLINE : null
1536
1556
  ],
1537
1557
  testID,
1538
1558
  ...keyboardProps,
1539
1559
  children: [
1540
- /* @__PURE__ */ jsx(View, { style: [styles9.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsx(Text, { style: [styles9.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : 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
+ ] }),
1541
1564
  /* @__PURE__ */ jsxs(View, { style: styles9.textColumn, children: [
1542
1565
  /* @__PURE__ */ jsx(Text, { style: [styles9.label, themeStyles.label], children: label }),
1543
1566
  hasHint ? /* @__PURE__ */ jsx(Text, { nativeID: hintId, style: [styles9.hint, themeStyles.hint], children: hintText }) : null
@@ -1831,7 +1854,193 @@ function toggleGroupSelection(selected, groupKeys, order) {
1831
1854
  });
1832
1855
  return order === void 0 ? merged : order.filter((key) => merged.includes(key));
1833
1856
  }
1857
+ var ROW_GAP = 4;
1858
+ var TAB_PADDING = 4;
1859
+ var INDICATOR_WIDTH = 2;
1860
+ var TAB_FONT_SIZE = 12;
1861
+ var styles10 = StyleSheet.create({
1862
+ row: { flexDirection: "row", flexWrap: "nowrap", gap: ROW_GAP },
1863
+ tab: {
1864
+ borderBottomWidth: INDICATOR_WIDTH,
1865
+ flexBasis: 0,
1866
+ flexGrow: 1,
1867
+ flexShrink: 1,
1868
+ justifyContent: "center",
1869
+ minHeight: MIN_TOUCH_TARGET_PX,
1870
+ minWidth: 0,
1871
+ paddingHorizontal: TAB_PADDING
1872
+ },
1873
+ text: { fontSize: TAB_FONT_SIZE, textAlign: "center" },
1874
+ textSelected: { fontWeight: "700" }
1875
+ });
1876
+ var StepperTab = ({
1877
+ step,
1878
+ selected,
1879
+ hint,
1880
+ onStep,
1881
+ testID
1882
+ }) => {
1883
+ const { theme } = useUi();
1884
+ const ring = useFocusRing();
1885
+ const indicator = selected ? theme.palette.primary["500"] : theme.colors.border;
1886
+ const webKeyProps = {
1887
+ onKeyDown: (event) => {
1888
+ if (event.key !== " ") return;
1889
+ event.preventDefault?.();
1890
+ onStep(step.key);
1891
+ }
1892
+ };
1893
+ return /* @__PURE__ */ jsx(
1894
+ Pressable,
1895
+ {
1896
+ accessibilityHint: hint,
1897
+ accessibilityLabel: step.label,
1898
+ accessibilityRole: "tab",
1899
+ accessibilityState: { selected },
1900
+ "aria-selected": selected,
1901
+ style: [styles10.tab, { borderBottomColor: indicator }, ring.ringStyle],
1902
+ testID,
1903
+ onBlur: ring.onBlur,
1904
+ onFocus: ring.onFocus,
1905
+ onPress: () => onStep(step.key),
1906
+ ...webKeyProps,
1907
+ children: /* @__PURE__ */ jsx(Text, { style: [styles10.text, { color: theme.colors.text }, selected ? styles10.textSelected : void 0], children: step.label })
1908
+ }
1909
+ );
1910
+ };
1911
+ var StepperTabs = ({
1912
+ steps,
1913
+ current,
1914
+ onStep,
1915
+ stepHint,
1916
+ testID
1917
+ }) => /* @__PURE__ */ jsx(View, { accessibilityRole: "tablist", style: styles10.row, testID, children: steps.map((step) => /* @__PURE__ */ jsx(
1918
+ StepperTab,
1919
+ {
1920
+ hint: stepHint,
1921
+ selected: step.key === current,
1922
+ step,
1923
+ testID: `${testID}-${step.key}`,
1924
+ onStep
1925
+ },
1926
+ step.key
1927
+ )) });
1928
+
1929
+ // src/Stepper/stepperModel.ts
1930
+ function resolveStepNeighbours(steps, current) {
1931
+ const index = steps.findIndex((step) => step.key === current);
1932
+ if (index < 0) return { previous: null, next: null };
1933
+ return { previous: steps[index - 1] ?? null, next: steps[index + 1] ?? null };
1934
+ }
1935
+ var ROW_GAP2 = 8;
1936
+ var styles11 = StyleSheet.create({
1937
+ cell: { flex: 1, minWidth: 0 },
1938
+ row: { flexDirection: "row", gap: ROW_GAP2 }
1939
+ });
1940
+ var StepperNav = ({
1941
+ steps,
1942
+ current,
1943
+ onStep,
1944
+ labels,
1945
+ testID
1946
+ }) => {
1947
+ const { previous, next } = resolveStepNeighbours(steps, current);
1948
+ return /* @__PURE__ */ jsxs(View, { style: styles11.row, testID, children: [
1949
+ /* @__PURE__ */ jsx(View, { style: styles11.cell, children: /* @__PURE__ */ jsx(
1950
+ Button,
1951
+ {
1952
+ fullWidth: true,
1953
+ accessibilityHint: labels.backHint,
1954
+ accessibilityLabel: labels.back,
1955
+ autoLoading: false,
1956
+ disabled: previous === null,
1957
+ label: labels.back,
1958
+ testID: `${testID}-back`,
1959
+ variant: "outline",
1960
+ onPress: () => {
1961
+ if (previous !== null) onStep(previous.key);
1962
+ }
1963
+ }
1964
+ ) }),
1965
+ next === null ? null : /* @__PURE__ */ jsx(View, { style: styles11.cell, children: /* @__PURE__ */ jsx(
1966
+ Button,
1967
+ {
1968
+ fullWidth: true,
1969
+ accessibilityHint: labels.nextHint,
1970
+ accessibilityLabel: labels.next(next),
1971
+ autoLoading: false,
1972
+ label: labels.next(next),
1973
+ testID: `${testID}-next`,
1974
+ onPress: () => onStep(next.key)
1975
+ }
1976
+ ) })
1977
+ ] });
1978
+ };
1979
+ var DEFAULT_PREVIEW_PX = 48;
1980
+ var PREVIEW_RADIUS = 8;
1981
+ var GAP = 8;
1982
+ var TITLE_FONT_SIZE = 14;
1983
+ var HELP_FONT_SIZE = 13;
1984
+ var styles12 = StyleSheet.create({
1985
+ help: { flex: 1, fontSize: HELP_FONT_SIZE },
1986
+ root: { gap: GAP },
1987
+ tile: { alignItems: "center", flexDirection: "row", gap: GAP },
1988
+ title: { fontSize: TITLE_FONT_SIZE, fontWeight: "700" },
1989
+ toggle: { alignSelf: "flex-start", justifyContent: "center", minHeight: MIN_TOUCH_TARGET_PX },
1990
+ toggleText: { fontSize: TITLE_FONT_SIZE, fontWeight: "700", textDecorationLine: "underline" }
1991
+ });
1992
+ var UploadWithLinkField = ({
1993
+ value,
1994
+ onChange,
1995
+ labels,
1996
+ uploadControl,
1997
+ testID,
1998
+ disabled = false,
1999
+ previewSize = DEFAULT_PREVIEW_PX
2000
+ }) => {
2001
+ const { theme } = useUi();
2002
+ const ring = useFocusRing();
2003
+ const url = value ?? "";
2004
+ const [pasting, setPasting] = useState(url !== "");
2005
+ const previewBox = { borderRadius: PREVIEW_RADIUS, height: previewSize, width: previewSize };
2006
+ return /* @__PURE__ */ jsxs(View, { style: styles12.root, children: [
2007
+ /* @__PURE__ */ jsx(Text, { style: [styles12.title, { color: theme.colors.text }], children: labels.title }),
2008
+ /* @__PURE__ */ jsxs(View, { style: styles12.tile, children: [
2009
+ url === "" ? /* @__PURE__ */ jsx(View, { style: [previewBox, { backgroundColor: theme.colors.border }] }) : /* @__PURE__ */ jsx(Image, { accessible: false, source: { uri: url }, style: previewBox, testID: `${testID}-preview` }),
2010
+ labels.help === void 0 ? null : /* @__PURE__ */ jsx(Text, { style: [styles12.help, { color: theme.colors.text }], children: labels.help })
2011
+ ] }),
2012
+ uploadControl,
2013
+ /* @__PURE__ */ jsx(
2014
+ Pressable,
2015
+ {
2016
+ accessibilityHint: labels.pasteToggleHint,
2017
+ accessibilityLabel: labels.pasteToggle,
2018
+ accessibilityRole: "button",
2019
+ accessibilityState: { expanded: pasting },
2020
+ "aria-expanded": pasting,
2021
+ style: [styles12.toggle, ring.ringStyle],
2022
+ testID: `${testID}-paste`,
2023
+ onBlur: ring.onBlur,
2024
+ onFocus: ring.onFocus,
2025
+ onPress: () => setPasting(!pasting),
2026
+ children: /* @__PURE__ */ jsx(Text, { style: [styles12.toggleText, { color: theme.colors.text }], children: labels.pasteToggle })
2027
+ }
2028
+ ),
2029
+ pasting ? /* @__PURE__ */ jsx(
2030
+ FormField,
2031
+ {
2032
+ accessibilityHint: labels.linkHint,
2033
+ editable: !disabled,
2034
+ label: labels.link,
2035
+ placeholder: labels.linkPlaceholder,
2036
+ testID,
2037
+ value: url,
2038
+ onChangeText: onChange
2039
+ }
2040
+ ) : null
2041
+ ] });
2042
+ };
1834
2043
 
1835
- 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 };
2044
+ 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, StepperNav, StepperTabs, ThemedTextInput, TypeaheadControl, UploadWithLinkField, controlStyles, groupAriaChecked, inlineFieldRowLayout, isUnmatched, resolveGroupSelection, resolveStepNeighbours, suggestOptions, toggleGroupSelection, useRecentValues, useThemedInput };
1836
2045
  //# sourceMappingURL=index.mjs.map
1837
2046
  //# sourceMappingURL=index.mjs.map