@kanso-labs/react-native-kanso-ui 0.1.1 → 0.3.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.
Files changed (88) hide show
  1. package/dist/button/index.d.ts +32 -2
  2. package/dist/button/index.js +38 -26
  3. package/dist/button/index.js.map +1 -1
  4. package/dist/button/pending.js +144 -0
  5. package/dist/button/pending.js.map +1 -0
  6. package/dist/button/slots.js +7 -0
  7. package/dist/button/slots.js.map +1 -0
  8. package/dist/components/avatar/index.d.ts +2 -2
  9. package/dist/components/avatar/index.js.map +1 -1
  10. package/dist/components/button/index.d.ts +3 -3
  11. package/dist/components/button/index.js +159 -111
  12. package/dist/components/button/index.js.map +1 -1
  13. package/dist/components/card/index.d.ts +2 -2
  14. package/dist/components/card/index.js.map +1 -1
  15. package/dist/components/checkbox/index.d.ts +2 -2
  16. package/dist/components/checkbox/index.js.map +1 -1
  17. package/dist/components/chip/index.d.ts +2 -2
  18. package/dist/components/chip/index.js.map +1 -1
  19. package/dist/components/chip-group/index.d.ts +3 -3
  20. package/dist/components/chip-group/index.js.map +1 -1
  21. package/dist/components/copy-field/index.d.ts +2 -2
  22. package/dist/components/copy-field/index.js.map +1 -1
  23. package/dist/components/dialog/index.d.ts +131 -0
  24. package/dist/components/dialog/index.js +861 -0
  25. package/dist/components/dialog/index.js.map +1 -0
  26. package/dist/components/icon-button/index.d.ts +3 -3
  27. package/dist/components/icon-button/index.js +165 -109
  28. package/dist/components/icon-button/index.js.map +1 -1
  29. package/dist/components/link/index.js +1 -1
  30. package/dist/components/link/index.js.map +1 -1
  31. package/dist/components/meter/index.d.ts +73 -0
  32. package/dist/components/meter/index.js +179 -0
  33. package/dist/components/meter/index.js.map +1 -0
  34. package/dist/components/number-field/index.d.ts +3 -3
  35. package/dist/components/number-field/index.js +1 -9
  36. package/dist/components/number-field/index.js.map +1 -1
  37. package/dist/components/product-icon/index.d.ts +2 -2
  38. package/dist/components/product-icon/index.js.map +1 -1
  39. package/dist/components/progress-indicator/index.d.ts +113 -0
  40. package/dist/components/progress-indicator/index.js +1024 -0
  41. package/dist/components/progress-indicator/index.js.map +1 -0
  42. package/dist/components/radio-group/index.d.ts +3 -3
  43. package/dist/components/radio-group/index.js.map +1 -1
  44. package/dist/components/search-field/index.d.ts +3 -3
  45. package/dist/components/search-field/index.js.map +1 -1
  46. package/dist/components/segmented-button/index.d.ts +3 -3
  47. package/dist/components/segmented-button/index.js.map +1 -1
  48. package/dist/components/snackbar/index.d.ts +120 -0
  49. package/dist/components/snackbar/index.js +540 -0
  50. package/dist/components/snackbar/index.js.map +1 -0
  51. package/dist/components/switch/index.d.ts +2 -2
  52. package/dist/components/switch/index.js.map +1 -1
  53. package/dist/components/text-field/index.d.ts +3 -3
  54. package/dist/components/text-field/index.js.map +1 -1
  55. package/dist/field/index.d.ts +1 -1
  56. package/dist/field/index.js.map +1 -1
  57. package/dist/form/index.d.ts +1 -1
  58. package/dist/form/index.js.map +1 -1
  59. package/dist/glyphs/hidden.js +13 -0
  60. package/dist/glyphs/hidden.js.map +1 -0
  61. package/dist/glyphs/index.js +2 -8
  62. package/dist/glyphs/index.js.map +1 -1
  63. package/dist/i18n/index.js +23 -0
  64. package/dist/i18n/index.js.map +1 -0
  65. package/dist/i18n/messages.js +41 -0
  66. package/dist/i18n/messages.js.map +1 -0
  67. package/dist/index.d.ts +5 -1
  68. package/dist/index.js +7 -3
  69. package/dist/indicator/index.d.ts +15 -0
  70. package/dist/indicator/index.js +256 -0
  71. package/dist/indicator/index.js.map +1 -0
  72. package/dist/indicator/motion.js +86 -0
  73. package/dist/indicator/motion.js.map +1 -0
  74. package/dist/indicator/paths.js +72 -0
  75. package/dist/indicator/paths.js.map +1 -0
  76. package/dist/indicator/styles.js +62 -0
  77. package/dist/indicator/styles.js.map +1 -0
  78. package/dist/mark/index.d.ts +1 -1
  79. package/dist/mark/index.js.map +1 -1
  80. package/dist/number/locale.js +14 -0
  81. package/dist/number/locale.js.map +1 -0
  82. package/dist/overlay/host.js +65 -2
  83. package/dist/overlay/host.js.map +1 -1
  84. package/dist/press/index.js +63 -63
  85. package/dist/press/index.js.map +1 -1
  86. package/dist/theme/resolve.js +1 -1
  87. package/dist/theme/resolve.js.map +1 -1
  88. package/package.json +11 -11
@@ -14,15 +14,44 @@ type ButtonLinkForm = {
14
14
  * with a scheme of its own, and the button is announced as a link.
15
15
  */
16
16
  readonly href: string;
17
+ readonly isPending?: never;
17
18
  readonly isSelected?: never;
18
19
  readonly onChange?: never;
20
+ readonly pendingLabel?: never;
21
+ readonly slot?: never;
19
22
  readonly type?: never;
20
23
  };
21
- type ButtonPressForm = {
24
+ /**
25
+ * Whether the button's action is under way, which the plain button and the
26
+ * toggle both take and the link does not.
27
+ */
28
+ type ButtonPending = {
29
+ /**
30
+ * Shows that the button's action is under way: a ring takes the place of
31
+ * the label and the icon, which keep their space, and the button keeps its
32
+ * focus and takes no press until it clears. A toggle may be pending, for a
33
+ * state saved somewhere slow.
34
+ * @default false
35
+ */
36
+ readonly isPending?: boolean;
37
+ /**
38
+ * The name of the ring shown while the button is pending, for a screen
39
+ * reader. The content it replaces is hidden while it shows. Left out, it is
40
+ * the word for it in the device's locale, "Loading" in English.
41
+ */
42
+ readonly pendingLabel?: string;
43
+ };
44
+ type ButtonPressForm = ButtonPending & {
22
45
  readonly defaultSelected?: never;
23
46
  readonly href?: never;
24
47
  readonly isSelected?: never;
25
48
  readonly onChange?: never;
49
+ /**
50
+ * `close` closes the overlay the button is in, as web's React Aria slot of
51
+ * that name does: a `Dialog` given one inside its content closes when it is
52
+ * pressed, after the button's own `onPress`.
53
+ */
54
+ readonly slot?: 'close';
26
55
  /**
27
56
  * What a press does past `onPress`. `submit` submits the `Form` the button
28
57
  * is in, as a submit button does on web; `button` does nothing more.
@@ -30,7 +59,7 @@ type ButtonPressForm = {
30
59
  */
31
60
  readonly type?: 'button' | 'submit';
32
61
  };
33
- type ButtonToggleForm = {
62
+ type ButtonToggleForm = ButtonPending & {
34
63
  /**
35
64
  * Whether a toggle starts selected, when it keeps its own state. Passing
36
65
  * this, `isSelected` or `onChange` is what makes the button a toggle.
@@ -49,6 +78,7 @@ type ButtonToggleForm = {
49
78
  * a toggle.
50
79
  */
51
80
  readonly onChange?: (isSelected: boolean) => void;
81
+ readonly slot?: never;
52
82
  readonly type?: never;
53
83
  };
54
84
  //#endregion
@@ -1,6 +1,7 @@
1
1
  import { useTheme } from "../theme/scope.js";
2
2
  import { FormContext } from "../form/index.js";
3
3
  import { useOpenLink } from "../router/index.js";
4
+ import { ButtonSlotContext } from "./slots.js";
4
5
  import { c } from "react/compiler-runtime";
5
6
  import { Platform, Pressable, StyleSheet } from "react-native";
6
7
  import { use, useState } from "react";
@@ -50,7 +51,7 @@ function measureThen(height, onLayout) {
50
51
  function pressThen(onPress, then) {
51
52
  return (event) => {
52
53
  onPress?.(event);
53
- then();
54
+ then(event);
54
55
  };
55
56
  }
56
57
  /**
@@ -60,50 +61,61 @@ function pressThen(onPress, then) {
60
61
  * in. A toggle keeps its own state from `defaultSelected` unless the call
61
62
  * site holds it in `isSelected`. A submit button outside a form submits
62
63
  * nothing, as on web.
64
+ *
65
+ * Given a `slot`, a button that is not a link then does what that slot of
66
+ * the overlay around it does, as src/button's slots.ts sets out: opens it
67
+ * from the trigger, or closes it from its content.
63
68
  */
64
69
  function useButtonAction(t0) {
65
- const $ = c(14);
66
- const { defaultSelected, href, isSelected, isToggle, onChange, onPress, type } = t0;
70
+ const $ = c(15);
71
+ const { defaultSelected, href, isSelected, isToggle, onChange, onPress, slot, type } = t0;
67
72
  const openLink = useOpenLink();
68
73
  const form = use(FormContext);
74
+ const slots = use(ButtonSlotContext);
69
75
  const [ownSelected, setOwnSelected] = useState(defaultSelected ?? false);
70
76
  const selected = isToggle && (isSelected ?? ownSelected);
77
+ const filled = slot === void 0 ? void 0 : slots?.[slot];
71
78
  let t1;
72
- if ($[0] !== form || $[1] !== href || $[2] !== isToggle || $[3] !== onChange || $[4] !== openLink || $[5] !== selected || $[6] !== type) {
73
- t1 = function follow() {
74
- if (href !== void 0) openLink(href);
75
- else if (isToggle) {
79
+ if ($[0] !== filled || $[1] !== form || $[2] !== href || $[3] !== isToggle || $[4] !== onChange || $[5] !== openLink || $[6] !== selected || $[7] !== type) {
80
+ t1 = function follow(event) {
81
+ if (href !== void 0) {
82
+ openLink(href);
83
+ return;
84
+ }
85
+ if (isToggle) {
76
86
  setOwnSelected(!selected);
77
87
  onChange?.(!selected);
78
88
  } else if (type === "submit") form?.requestSubmit();
89
+ filled?.(event);
79
90
  };
80
- $[0] = form;
81
- $[1] = href;
82
- $[2] = isToggle;
83
- $[3] = onChange;
84
- $[4] = openLink;
85
- $[5] = selected;
86
- $[6] = type;
87
- $[7] = t1;
88
- } else t1 = $[7];
91
+ $[0] = filled;
92
+ $[1] = form;
93
+ $[2] = href;
94
+ $[3] = isToggle;
95
+ $[4] = onChange;
96
+ $[5] = openLink;
97
+ $[6] = selected;
98
+ $[7] = type;
99
+ $[8] = t1;
100
+ } else t1 = $[8];
89
101
  const follow = t1;
90
102
  let t2;
91
- if ($[8] !== follow || $[9] !== onPress) {
103
+ if ($[9] !== follow || $[10] !== onPress) {
92
104
  t2 = pressThen(onPress, follow);
93
- $[8] = follow;
94
- $[9] = onPress;
95
- $[10] = t2;
96
- } else t2 = $[10];
105
+ $[9] = follow;
106
+ $[10] = onPress;
107
+ $[11] = t2;
108
+ } else t2 = $[11];
97
109
  let t3;
98
- if ($[11] !== selected || $[12] !== t2) {
110
+ if ($[12] !== selected || $[13] !== t2) {
99
111
  t3 = {
100
112
  onPress: t2,
101
113
  selected
102
114
  };
103
- $[11] = selected;
104
- $[12] = t2;
105
- $[13] = t3;
106
- } else t3 = $[13];
115
+ $[12] = selected;
116
+ $[13] = t2;
117
+ $[14] = t3;
118
+ } else t3 = $[14];
107
119
  return t3;
108
120
  }
109
121
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["GestureResponderEvent","LayoutChangeEvent","PressableProps","SharedValue","use","useState","Platform","Pressable","StyleSheet","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","ResolvedTheme","FormContext","useOpenLink","useTheme","ButtonLinkForm","defaultSelected","href","isSelected","onChange","type","ButtonPressForm","ButtonToggleForm","ControlSize","ToggleState","HEIGHTS","lg","md","xl","xs","xxl","const","Record","OUTLINE_WIDTHS","Readonly","PRESSED_CORNERS","AnimatedPressable","createAnimatedComponent","toggleState","select","selected","default","web","buttonStyles","create","clip","bottom","left","overflow","pointerEvents","position","right","top","measureThen","height","onLayout","event","set","nativeEvent","layout","pressThen","onPress","then","useButtonAction","t0","$","_c","isToggle","openLink","form","ownSelected","setOwnSelected","t1","follow","undefined","requestSubmit","t2","t3","useCorner","radius","motion","reduceMotion","durationShort2","easingEmphasized","bezier","duration","easing","timing","corner","measured","get","target","Math","min","borderRadius","t4","_c"],"sources":["../../src/button/index.ts"],"sourcesContent":["import type {\n GestureResponderEvent,\n LayoutChangeEvent,\n PressableProps,\n} from 'react-native'\nimport type { SharedValue } from 'react-native-reanimated'\n\nimport { use, useState } from 'react'\nimport { Platform, Pressable, StyleSheet } from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { ResolvedTheme } from '../theme/scope'\n\nimport { FormContext } from '../form'\nimport { useOpenLink } from '../router'\nimport { useTheme } from '../theme/scope'\n\n// What every button does past its own styles, shared by Button and\n// IconButton as web's src/button is: the three forms a button takes, the\n// toggle's state and how it is announced, what a press does after the call\n// site's own `onPress`, a submit button's included, the corner that eases\n// between shapes, and the metrics\n// the two components' pages give alike, since the two share their five\n// heights so that they line up in a row.\n\n/**\n * A plain button, a link, or a toggle, of which a call site writes exactly\n * one. Each form names the props of the other two as `never`, so a toggle\n * given `href` is a compile error rather than a prop that silently does\n * nothing.\n */\ntype ButtonLinkForm = {\n readonly defaultSelected?: never\n /**\n * Where the button leads. Given one, a press opens it through the\n * `RouterProvider` around the button, or through the platform for a URL\n * with a scheme of its own, and the button is announced as a link.\n */\n readonly href: string\n readonly isSelected?: never\n readonly onChange?: never\n readonly type?: never\n}\n\ntype ButtonPressForm = {\n readonly defaultSelected?: never\n readonly href?: never\n readonly isSelected?: never\n readonly onChange?: never\n /**\n * What a press does past `onPress`. `submit` submits the `Form` the button\n * is in, as a submit button does on web; `button` does nothing more.\n * @default 'button'\n */\n readonly type?: 'button' | 'submit'\n}\n\ntype ButtonToggleForm = {\n /**\n * Whether a toggle starts selected, when it keeps its own state. Passing\n * this, `isSelected` or `onChange` is what makes the button a toggle.\n */\n readonly defaultSelected?: boolean\n readonly href?: never\n /**\n * Whether a toggle is selected, when the call site holds the state. Pass\n * it with `onChange`; passing this, `defaultSelected` or `onChange` is what\n * makes the button a toggle.\n */\n readonly isSelected?: boolean\n /**\n * Called with the new state when a toggle is pressed, after `onPress`.\n * Passing this, `isSelected` or `defaultSelected` is what makes the button\n * a toggle.\n */\n readonly onChange?: (isSelected: boolean) => void\n readonly type?: never\n}\n\n/** The five control heights both pages give, by the names both props take. */\ntype ControlSize = 'lg' | 'md' | 'xl' | 'xs' | 'xxl'\n\n/** How a toggle reports its state to a screen reader. */\ntype ToggleState = {\n readonly 'aria-pressed'?: boolean\n readonly 'aria-selected'?: boolean\n}\n\n// The control height each size reads: the page's XS, S, M, L and XL, which\n// the theme's sizing scale names one step apart from this prop.\nconst HEIGHTS = {\n lg: 'controlLg',\n md: 'controlSm',\n xl: 'controlXl',\n xs: 'controlXs',\n xxl: 'controlXxl',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['sizing']>\n\n// The outlined rule thickens with the size, as the buttons page's size tokens\n// have it: 1dp up to M, 2dp at L, 3dp at XL. The icon buttons page keeps its\n// widths in a token set its widget will not open, so the icon button takes\n// these, which a 96dp icon button beside a 96dp outlined button needs anyway.\nconst OUTLINE_WIDTHS: Readonly<Record<ControlSize, number>> = {\n lg: 1,\n md: 1,\n xl: 2,\n xs: 1,\n xxl: 3,\n}\n\n// The corner each size presses to, which both pages give alike.\nconst PRESSED_CORNERS = {\n lg: 'md',\n md: 'sm',\n xl: 'lg',\n xs: 'sm',\n xxl: 'lg',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['radii']>\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable)\n\n// Web's toggle reports `aria-pressed`, which React Native Web writes onto the\n// element. React Native has no pressed state to report, and reports the\n// selected one instead, which VoiceOver and TalkBack both read out.\nconst toggleState = Platform.select<(selected: boolean) => ToggleState>({\n default: (selected) => ({ 'aria-selected': selected }),\n web: (selected) => ({ 'aria-pressed': selected }),\n})\n\nconst buttonStyles = StyleSheet.create({\n // Clips the state layer and the ripple to the corner as it morphs, and\n // takes no touches.\n clip: {\n bottom: 0,\n left: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n// Records the button's height for the corner, then calls the call site's own\n// `onLayout`. Built by a call rather than declared in a component, which is\n// what react-perf's jsx-no-new-function-as-prop is after; the React Compiler\n// memoises the result on its inputs.\nfunction measureThen(\n height: SharedValue<number>,\n onLayout: PressableProps['onLayout'],\n) {\n return (event: LayoutChangeEvent) => {\n height.set(event.nativeEvent.layout.height)\n onLayout?.(event)\n }\n}\n\n// The call site's own `onPress`, then what the button does with the press.\n// Built by a call for the reason `measureThen` is.\nfunction pressThen(onPress: PressableProps['onPress'], then: () => void) {\n return (event: GestureResponderEvent) => {\n onPress?.(event)\n then()\n }\n}\n\n/**\n * What a press does, and whether a toggle is selected. A press calls the call\n * site's own `onPress` first, then opens the link, changes the toggle's state\n * or submits the form the button is in, which is the order web reports them\n * in. A toggle keeps its own state from `defaultSelected` unless the call\n * site holds it in `isSelected`. A submit button outside a form submits\n * nothing, as on web.\n */\nfunction useButtonAction({\n defaultSelected,\n href,\n isSelected,\n isToggle,\n onChange,\n onPress,\n type,\n}: {\n readonly defaultSelected: boolean | undefined\n readonly href: string | undefined\n readonly isSelected: boolean | undefined\n readonly isToggle: boolean\n readonly onChange: ((isSelected: boolean) => void) | undefined\n readonly onPress: PressableProps['onPress']\n readonly type: 'button' | 'submit' | undefined\n}) {\n const openLink = useOpenLink()\n const form = use(FormContext)\n const [ownSelected, setOwnSelected] = useState(defaultSelected ?? false)\n const selected = isToggle && (isSelected ?? ownSelected)\n\n function follow() {\n if (href !== undefined) {\n openLink(href)\n } else if (isToggle) {\n setOwnSelected(!selected)\n onChange?.(!selected)\n } else if (type === 'submit') {\n form?.requestSubmit()\n }\n }\n\n return { onPress: pressThen(onPress, follow), selected }\n}\n\n/**\n * The corner a button eases to, over the motion tokens' short duration on the\n * emphasised curve, and at once for a reader who asks the device for reduced\n * motion. The pill is the radius scale's `pill`, far larger than any button,\n * so the ease runs from half the button's measured height rather than from\n * that: otherwise the corner would hold the pill for most of the ease and\n * jump at its end. The `onLayout` it hands back measures the button, then\n * calls the call site's own.\n */\nfunction useCorner(radius: number, onLayout: PressableProps['onLayout']) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const height = useSharedValue(0)\n const timing = {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasized),\n }\n const corner = useAnimatedStyle(() => {\n 'worklet'\n const measured = height.get()\n const target = measured > 0 ? Math.min(radius, measured / 2) : radius\n\n return {\n borderRadius: reduceMotion ? target : withTiming(target, timing),\n }\n })\n\n return { corner, onLayout: measureThen(height, onLayout) }\n}\n\nexport {\n AnimatedPressable,\n buttonStyles,\n HEIGHTS,\n OUTLINE_WIDTHS,\n PRESSED_CORNERS,\n toggleState,\n useButtonAction,\n useCorner,\n}\nexport type { ButtonLinkForm, ButtonPressForm, ButtonToggleForm, ControlSize }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["GestureResponderEvent","LayoutChangeEvent","PressableProps","SharedValue","use","useState","Platform","Pressable","StyleSheet","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","ResolvedTheme","FormContext","useOpenLink","useTheme","ButtonSlotContext","ButtonLinkForm","defaultSelected","href","isPending","isSelected","onChange","pendingLabel","slot","type","ButtonPending","ButtonPressForm","ButtonToggleForm","ControlSize","ToggleState","HEIGHTS","lg","md","xl","xs","xxl","const","Record","OUTLINE_WIDTHS","Readonly","PRESSED_CORNERS","AnimatedPressable","createAnimatedComponent","toggleState","select","selected","default","web","buttonStyles","create","clip","bottom","left","overflow","pointerEvents","position","right","top","measureThen","height","onLayout","event","set","nativeEvent","layout","pressThen","onPress","then","useButtonAction","t0","$","_c","isToggle","openLink","form","slots","ownSelected","setOwnSelected","filled","undefined","t1","follow","requestSubmit","t2","t3","useCorner","radius","motion","reduceMotion","durationShort2","easingEmphasized","bezier","duration","easing","timing","corner","measured","get","target","Math","min","borderRadius","t4","_c"],"sources":["../../src/button/index.ts"],"sourcesContent":["import type {\n GestureResponderEvent,\n LayoutChangeEvent,\n PressableProps,\n} from 'react-native'\nimport type { SharedValue } from 'react-native-reanimated'\n\nimport { use, useState } from 'react'\nimport { Platform, Pressable, StyleSheet } from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { ResolvedTheme } from '../theme/scope'\n\nimport { FormContext } from '../form'\nimport { useOpenLink } from '../router'\nimport { useTheme } from '../theme/scope'\nimport { ButtonSlotContext } from './slots'\n\n// What every button does past its own styles, shared by Button and\n// IconButton as web's src/button is: the three forms a button takes, the\n// toggle's state and how it is announced, what a press does after the call\n// site's own `onPress`, a submit button's included, and what the slot it\n// fills in an overlay adds to it, the corner that eases between shapes, and\n// the metrics\n// the two components' pages give alike, since the two share their five\n// heights so that they line up in a row. What a pending button draws and\n// says is ./pending.tsx.\n\n/**\n * A plain button, a link, or a toggle, of which a call site writes exactly\n * one. Each form names the props of the other two as `never`, so a toggle\n * given `href` is a compile error rather than a prop that silently does\n * nothing.\n */\ntype ButtonLinkForm = {\n readonly defaultSelected?: never\n /**\n * Where the button leads. Given one, a press opens it through the\n * `RouterProvider` around the button, or through the platform for a URL\n * with a scheme of its own, and the button is announced as a link.\n */\n readonly href: string\n readonly isPending?: never\n readonly isSelected?: never\n readonly onChange?: never\n readonly pendingLabel?: never\n readonly slot?: never\n readonly type?: never\n}\n\n/**\n * Whether the button's action is under way, which the plain button and the\n * toggle both take and the link does not.\n */\ntype ButtonPending = {\n /**\n * Shows that the button's action is under way: a ring takes the place of\n * the label and the icon, which keep their space, and the button keeps its\n * focus and takes no press until it clears. A toggle may be pending, for a\n * state saved somewhere slow.\n * @default false\n */\n readonly isPending?: boolean\n /**\n * The name of the ring shown while the button is pending, for a screen\n * reader. The content it replaces is hidden while it shows. Left out, it is\n * the word for it in the device's locale, \"Loading\" in English.\n */\n readonly pendingLabel?: string\n}\n\ntype ButtonPressForm = ButtonPending & {\n readonly defaultSelected?: never\n readonly href?: never\n readonly isSelected?: never\n readonly onChange?: never\n /**\n * `close` closes the overlay the button is in, as web's React Aria slot of\n * that name does: a `Dialog` given one inside its content closes when it is\n * pressed, after the button's own `onPress`.\n */\n readonly slot?: 'close'\n /**\n * What a press does past `onPress`. `submit` submits the `Form` the button\n * is in, as a submit button does on web; `button` does nothing more.\n * @default 'button'\n */\n readonly type?: 'button' | 'submit'\n}\n\ntype ButtonToggleForm = ButtonPending & {\n /**\n * Whether a toggle starts selected, when it keeps its own state. Passing\n * this, `isSelected` or `onChange` is what makes the button a toggle.\n */\n readonly defaultSelected?: boolean\n readonly href?: never\n /**\n * Whether a toggle is selected, when the call site holds the state. Pass\n * it with `onChange`; passing this, `defaultSelected` or `onChange` is what\n * makes the button a toggle.\n */\n readonly isSelected?: boolean\n /**\n * Called with the new state when a toggle is pressed, after `onPress`.\n * Passing this, `isSelected` or `defaultSelected` is what makes the button\n * a toggle.\n */\n readonly onChange?: (isSelected: boolean) => void\n readonly slot?: never\n readonly type?: never\n}\n\n/** The five control heights both pages give, by the names both props take. */\ntype ControlSize = 'lg' | 'md' | 'xl' | 'xs' | 'xxl'\n\n/** How a toggle reports its state to a screen reader. */\ntype ToggleState = {\n readonly 'aria-pressed'?: boolean\n readonly 'aria-selected'?: boolean\n}\n\n// The control height each size reads: the page's XS, S, M, L and XL, which\n// the theme's sizing scale names one step apart from this prop.\nconst HEIGHTS = {\n lg: 'controlLg',\n md: 'controlSm',\n xl: 'controlXl',\n xs: 'controlXs',\n xxl: 'controlXxl',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['sizing']>\n\n// The outlined rule thickens with the size, as the buttons page's size tokens\n// have it: 1dp up to M, 2dp at L, 3dp at XL. The icon buttons page keeps its\n// widths in a token set its widget will not open, so the icon button takes\n// these, which a 96dp icon button beside a 96dp outlined button needs anyway.\nconst OUTLINE_WIDTHS: Readonly<Record<ControlSize, number>> = {\n lg: 1,\n md: 1,\n xl: 2,\n xs: 1,\n xxl: 3,\n}\n\n// The corner each size presses to, which both pages give alike.\nconst PRESSED_CORNERS = {\n lg: 'md',\n md: 'sm',\n xl: 'lg',\n xs: 'sm',\n xxl: 'lg',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['radii']>\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable)\n\n// Web's toggle reports `aria-pressed`, which React Native Web writes onto the\n// element. React Native has no pressed state to report, and reports the\n// selected one instead, which VoiceOver and TalkBack both read out.\nconst toggleState = Platform.select<(selected: boolean) => ToggleState>({\n default: (selected) => ({ 'aria-selected': selected }),\n web: (selected) => ({ 'aria-pressed': selected }),\n})\n\nconst buttonStyles = StyleSheet.create({\n // Clips the state layer and the ripple to the corner as it morphs, and\n // takes no touches.\n clip: {\n bottom: 0,\n left: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n// Records the button's height for the corner, then calls the call site's own\n// `onLayout`. Built by a call rather than declared in a component, which is\n// what react-perf's jsx-no-new-function-as-prop is after; the React Compiler\n// memoises the result on its inputs.\nfunction measureThen(\n height: SharedValue<number>,\n onLayout: PressableProps['onLayout'],\n) {\n return (event: LayoutChangeEvent) => {\n height.set(event.nativeEvent.layout.height)\n onLayout?.(event)\n }\n}\n\n// The call site's own `onPress`, then what the button does with the press.\n// Built by a call for the reason `measureThen` is.\nfunction pressThen(\n onPress: PressableProps['onPress'],\n then: (event: GestureResponderEvent) => void,\n) {\n return (event: GestureResponderEvent) => {\n onPress?.(event)\n then(event)\n }\n}\n\n/**\n * What a press does, and whether a toggle is selected. A press calls the call\n * site's own `onPress` first, then opens the link, changes the toggle's state\n * or submits the form the button is in, which is the order web reports them\n * in. A toggle keeps its own state from `defaultSelected` unless the call\n * site holds it in `isSelected`. A submit button outside a form submits\n * nothing, as on web.\n *\n * Given a `slot`, a button that is not a link then does what that slot of\n * the overlay around it does, as src/button's slots.ts sets out: opens it\n * from the trigger, or closes it from its content.\n */\nfunction useButtonAction({\n defaultSelected,\n href,\n isSelected,\n isToggle,\n onChange,\n onPress,\n slot,\n type,\n}: {\n readonly defaultSelected: boolean | undefined\n readonly href: string | undefined\n readonly isSelected: boolean | undefined\n readonly isToggle: boolean\n readonly onChange: ((isSelected: boolean) => void) | undefined\n readonly onPress: PressableProps['onPress']\n readonly slot?: 'close' | 'default'\n readonly type: 'button' | 'submit' | undefined\n}) {\n const openLink = useOpenLink()\n const form = use(FormContext)\n const slots = use(ButtonSlotContext)\n const [ownSelected, setOwnSelected] = useState(defaultSelected ?? false)\n const selected = isToggle && (isSelected ?? ownSelected)\n const filled = slot === undefined ? undefined : slots?.[slot]\n\n function follow(event: GestureResponderEvent) {\n if (href !== undefined) {\n openLink(href)\n return\n }\n if (isToggle) {\n setOwnSelected(!selected)\n onChange?.(!selected)\n } else if (type === 'submit') {\n form?.requestSubmit()\n }\n filled?.(event)\n }\n\n return { onPress: pressThen(onPress, follow), selected }\n}\n\n/**\n * The corner a button eases to, over the motion tokens' short duration on the\n * emphasised curve, and at once for a reader who asks the device for reduced\n * motion. The pill is the radius scale's `pill`, far larger than any button,\n * so the ease runs from half the button's measured height rather than from\n * that: otherwise the corner would hold the pill for most of the ease and\n * jump at its end. The `onLayout` it hands back measures the button, then\n * calls the call site's own.\n */\nfunction useCorner(radius: number, onLayout: PressableProps['onLayout']) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const height = useSharedValue(0)\n const timing = {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasized),\n }\n const corner = useAnimatedStyle(() => {\n 'worklet'\n const measured = height.get()\n const target = measured > 0 ? Math.min(radius, measured / 2) : radius\n\n return {\n borderRadius: reduceMotion ? target : withTiming(target, timing),\n }\n })\n\n return { corner, onLayout: measureThen(height, onLayout) }\n}\n\nexport {\n AnimatedPressable,\n buttonStyles,\n HEIGHTS,\n OUTLINE_WIDTHS,\n PRESSED_CORNERS,\n toggleState,\n useButtonAction,\n useCorner,\n}\nexport type { ButtonLinkForm, ButtonPressForm, ButtonToggleForm, ControlSize }\n"],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ import { IconStyleContext } from "../glyphs/icon-style.js";
2
+ import { ProgressIndicator } from "../components/progress-indicator/index.js";
3
+ import { messagesFor } from "../i18n/index.js";
4
+ import { c } from "react/compiler-runtime";
5
+ import { AccessibilityInfo, Platform, StyleSheet, View } from "react-native";
6
+ import { useEffect, useRef } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/button/pending.tsx
9
+ const pendingStyles = StyleSheet.create({
10
+ hidden: { opacity: 0 },
11
+ ring: {
12
+ alignItems: "center",
13
+ bottom: 0,
14
+ justifyContent: "center",
15
+ left: 0,
16
+ pointerEvents: "none",
17
+ position: "absolute",
18
+ right: 0,
19
+ top: 0
20
+ }
21
+ });
22
+ const BUSY = { "aria-busy": true };
23
+ const CONCEALED = { "aria-hidden": true };
24
+ /**
25
+ * The icon a button draws, in a view that hides it in place while the button
26
+ * is pending. The view is there whether or not the button is, so an icon
27
+ * keeps its state when the button goes pending and comes back.
28
+ */
29
+ function PendingContent(t0) {
30
+ const $ = c(4);
31
+ const { children, isPending } = t0;
32
+ const t1 = isPending ? CONCEALED : void 0;
33
+ const t2 = isPending ? pendingStyles.hidden : void 0;
34
+ let t3;
35
+ if ($[0] !== children || $[1] !== t1 || $[2] !== t2) {
36
+ t3 = /* @__PURE__ */ jsx(View, {
37
+ ...t1,
38
+ style: t2,
39
+ children
40
+ });
41
+ $[0] = children;
42
+ $[1] = t1;
43
+ $[2] = t2;
44
+ $[3] = t3;
45
+ } else t3 = $[3];
46
+ return t3;
47
+ }
48
+ /**
49
+ * The indeterminate ring a pending button draws over its content, in the
50
+ * button's content colour rather than the progress page's primary, which on a
51
+ * filled button is the fill itself. `diameter` is what the content it
52
+ * replaces measures, the label's type size on Button and the icon on
53
+ * IconButton, which is what web's `1em` reads in each.
54
+ */
55
+ function PendingRing(t0) {
56
+ const $ = c(9);
57
+ const { color, diameter, label } = t0;
58
+ let t1;
59
+ if ($[0] !== color || $[1] !== diameter) {
60
+ t1 = slotOf(color, diameter);
61
+ $[0] = color;
62
+ $[1] = diameter;
63
+ $[2] = t1;
64
+ } else t1 = $[2];
65
+ const t2 = `${diameter}px`;
66
+ let t3;
67
+ if ($[3] !== label || $[4] !== t2) {
68
+ t3 = /* @__PURE__ */ jsx(ProgressIndicator, {
69
+ "aria-label": label,
70
+ diameter: t2,
71
+ isIndeterminate: true,
72
+ tone: "inherit",
73
+ variant: "circular"
74
+ });
75
+ $[3] = label;
76
+ $[4] = t2;
77
+ $[5] = t3;
78
+ } else t3 = $[5];
79
+ let t4;
80
+ if ($[6] !== t1 || $[7] !== t3) {
81
+ t4 = /* @__PURE__ */ jsx(View, {
82
+ style: pendingStyles.ring,
83
+ children: /* @__PURE__ */ jsx(IconStyleContext, {
84
+ value: t1,
85
+ children: t3
86
+ })
87
+ });
88
+ $[6] = t1;
89
+ $[7] = t3;
90
+ $[8] = t4;
91
+ } else t4 = $[8];
92
+ return t4;
93
+ }
94
+ function slotOf(color, size) {
95
+ return {
96
+ color,
97
+ size
98
+ };
99
+ }
100
+ /**
101
+ * The ring's name, and the announcement a button makes as it goes pending.
102
+ * The name is `pendingLabel`, or the word for loading in the device's locale.
103
+ *
104
+ * On iOS and Android the change is said through VoiceOver and TalkBack, as
105
+ * web's is said through React Aria's live announcer, since a ring appearing
106
+ * under them is not reliably read. Web says it only to a reader whose focus
107
+ * is on the button; a screen reader's focus is not something a component
108
+ * here can read, so it is said whatever has focus. A button pending as it
109
+ * mounts says nothing, as on web, which announces a change. React Native Web
110
+ * has no announcer, so there the ring is read when the button next is.
111
+ */
112
+ function usePending(isPending, pendingLabel) {
113
+ const $ = c(6);
114
+ let t0;
115
+ if ($[0] !== pendingLabel) {
116
+ t0 = pendingLabel ?? messagesFor().loading;
117
+ $[0] = pendingLabel;
118
+ $[1] = t0;
119
+ } else t0 = $[1];
120
+ const label = t0;
121
+ const was = useRef(isPending);
122
+ let t1;
123
+ let t2;
124
+ if ($[2] !== isPending || $[3] !== label) {
125
+ t1 = () => {
126
+ if (isPending && !was.current && (Platform.OS === "ios" || Platform.OS === "android")) AccessibilityInfo.announceForAccessibility(label);
127
+ was.current = isPending;
128
+ };
129
+ t2 = [isPending, label];
130
+ $[2] = isPending;
131
+ $[3] = label;
132
+ $[4] = t1;
133
+ $[5] = t2;
134
+ } else {
135
+ t1 = $[4];
136
+ t2 = $[5];
137
+ }
138
+ useEffect(t1, t2);
139
+ return label;
140
+ }
141
+ //#endregion
142
+ export { BUSY, CONCEALED, PendingContent, PendingRing, pendingStyles, usePending };
143
+
144
+ //# sourceMappingURL=pending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending.js","names":["ReactNode","ColorValue","useEffect","useRef","AccessibilityInfo","Platform","StyleSheet","View","IconStyle","ProgressIndicator","IconStyleContext","messagesFor","pendingStyles","create","hidden","opacity","ring","alignItems","bottom","justifyContent","left","pointerEvents","position","right","top","BUSY","const","CONCEALED","PendingContent","t0","$","_c","children","isPending","t1","undefined","t2","t3","PendingRing","color","diameter","label","slotOf","t4","size","usePending","pendingLabel","loading","was","current","OS","announceForAccessibility","_c"],"sources":["../../src/button/pending.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { ColorValue } from 'react-native'\n\nimport { useEffect, useRef } from 'react'\nimport { AccessibilityInfo, Platform, StyleSheet, View } from 'react-native'\n\nimport type { IconStyle } from '../glyphs/icon-style'\n\nimport { ProgressIndicator } from '../components/progress-indicator'\nimport { IconStyleContext } from '../glyphs/icon-style'\nimport { messagesFor } from '../i18n'\n\n// What a pending button draws and says, shared by Button and IconButton as\n// web's ButtonBase draws it for both: the content hidden in place, the ring\n// over it, and the announcement that the button has gone pending.\n\nconst pendingStyles = StyleSheet.create({\n // The label and the icon while the ring shows: still laid out, so the\n // button keeps its width, which is what stops a form jumping the moment it\n // is submitted, and not drawn.\n hidden: {\n opacity: 0,\n },\n // The ring sits over the hidden content, centred in the button, and takes\n // no touches.\n ring: {\n alignItems: 'center',\n bottom: 0,\n justifyContent: 'center',\n left: 0,\n pointerEvents: 'none',\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n// What a pending button reports to a screen reader, spread before the call\n// site's own props, so an `aria-busy` of its own still wins, as on web.\nconst BUSY = { 'aria-busy': true } as const\n\n// What takes the hidden content out of what a screen reader reads, so the\n// button is named by the ring while it shows.\nconst CONCEALED = { 'aria-hidden': true } as const\n\n/**\n * The icon a button draws, in a view that hides it in place while the button\n * is pending. The view is there whether or not the button is, so an icon\n * keeps its state when the button goes pending and comes back.\n */\nfunction PendingContent({\n children,\n isPending,\n}: {\n readonly children: ReactNode\n readonly isPending: boolean\n}) {\n return (\n <View\n {...(isPending ? CONCEALED : undefined)}\n style={isPending ? pendingStyles.hidden : undefined}\n >\n {children}\n </View>\n )\n}\n\n/**\n * The indeterminate ring a pending button draws over its content, in the\n * button's content colour rather than the progress page's primary, which on a\n * filled button is the fill itself. `diameter` is what the content it\n * replaces measures, the label's type size on Button and the icon on\n * IconButton, which is what web's `1em` reads in each.\n */\nfunction PendingRing({\n color,\n diameter,\n label,\n}: {\n readonly color: ColorValue\n readonly diameter: number\n readonly label: string\n}) {\n return (\n <View style={pendingStyles.ring}>\n <IconStyleContext value={slotOf(color, diameter)}>\n <ProgressIndicator\n aria-label={label}\n diameter={`${diameter}px`}\n isIndeterminate\n tone=\"inherit\"\n variant=\"circular\"\n />\n </IconStyleContext>\n </View>\n )\n}\n\nfunction slotOf(color: ColorValue, size: number): IconStyle {\n return { color, size }\n}\n\n/**\n * The ring's name, and the announcement a button makes as it goes pending.\n * The name is `pendingLabel`, or the word for loading in the device's locale.\n *\n * On iOS and Android the change is said through VoiceOver and TalkBack, as\n * web's is said through React Aria's live announcer, since a ring appearing\n * under them is not reliably read. Web says it only to a reader whose focus\n * is on the button; a screen reader's focus is not something a component\n * here can read, so it is said whatever has focus. A button pending as it\n * mounts says nothing, as on web, which announces a change. React Native Web\n * has no announcer, so there the ring is read when the button next is.\n */\nfunction usePending(isPending: boolean, pendingLabel: string | undefined) {\n const label = pendingLabel ?? messagesFor().loading\n const was = useRef(isPending)\n\n useEffect(() => {\n if (\n isPending &&\n !was.current &&\n (Platform.OS === 'ios' || Platform.OS === 'android')\n ) {\n AccessibilityInfo.announceForAccessibility(label)\n }\n was.current = isPending\n }, [isPending, label])\n\n return label\n}\n\nexport {\n BUSY,\n CONCEALED,\n PendingContent,\n PendingRing,\n pendingStyles,\n usePending,\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { createContext } from "react";
2
+ //#region src/button/slots.ts
3
+ const ButtonSlotContext = createContext(null);
4
+ //#endregion
5
+ export { ButtonSlotContext };
6
+
7
+ //# sourceMappingURL=slots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.js","names":["GestureResponderEvent","createContext","ButtonSlots","close","event","default","ButtonSlotContext"],"sources":["../../src/button/slots.ts"],"sourcesContent":["import type { GestureResponderEvent } from 'react-native'\n\nimport { createContext } from 'react'\n\n/**\n * What a button does past its own press when it sits in an overlay's trigger\n * or content, as web's React Aria hands the same through a button's `slot`:\n * the default slot opens the overlay, which a button inside `Dialog` and\n * outside its content takes, and `close` closes it, which one given\n * `slot=\"close\"` inside the content takes. The overlay provides the slots it\n * fills, and resets the default one inside its content, so a button there\n * opens nothing.\n */\ntype ButtonSlots = {\n readonly close?: (event: GestureResponderEvent) => void\n readonly default?: (event: GestureResponderEvent) => void\n}\n\nconst ButtonSlotContext = createContext<ButtonSlots | null>(null)\n\nexport { ButtonSlotContext }\nexport type { ButtonSlots }\n"],"mappings":";;AAkBA,MAAMM,oBAAoBL,cAAkC,IAAI"}
@@ -1,6 +1,6 @@
1
1
  import { MarkImageProps } from "../../mark/index.js";
2
2
  import { StyleProp, View, ViewProps, ViewStyle } from "react-native";
3
- import { Ref } from "react";
3
+ import { ComponentRef, Ref } from "react";
4
4
  //#region src/components/avatar/index.d.ts
5
5
  type AvatarProps = Omit<ViewProps, 'children' | 'style'> & {
6
6
  /**
@@ -30,7 +30,7 @@ type AvatarProps = Omit<ViewProps, 'children' | 'style'> & {
30
30
  * names are, is one word, and takes one initial.
31
31
  */
32
32
  readonly name: string;
33
- readonly ref?: Ref<View>;
33
+ readonly ref?: Ref<ComponentRef<typeof View>>;
34
34
  /**
35
35
  * Whether an avatar with no initials to take, for a blank `name`, draws
36
36
  * Material Design's anonymous person. `false` leaves the tinted disc
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Ref","StyleProp","View","ViewProps","ViewStyle","MarkImageProps","Mark","AvatarProps","Omit","decorative","imgProps","name","ref","showPersonIcon","size","src","style","tone","Avatar","t0","$","_c","props","t1","t2","undefined","t3","_c"],"sources":["../../../src/components/avatar/index.tsx"],"sourcesContent":["import type { Ref } from 'react'\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native'\n\nimport type { MarkImageProps } from '../../mark'\n\nimport { Mark } from '../../mark'\n\n// A person, drawn as a disc with the photo cropped to fill it, as kanso-ui's\n// Avatar draws one. The box, the sizes, the tones, the photo's loading and\n// the labelling are the mark's in `src/mark`, which ProductIcon draws a thing\n// through as well, so a person and a thing fill the same slot.\n//\n// Which tone a given person gets is the consuming app's decision, since the\n// cycle of colours belongs where the list of people is.\n\ntype AvatarProps = Omit<ViewProps, 'children' | 'style'> & {\n /**\n * Hides the avatar from screen readers, for one beside the person's name,\n * such as a row whose headline is the name or a byline, where it would\n * otherwise announce the name a second time. `name` still supplies the\n * initials, and is not announced.\n * @default false\n */\n readonly decorative?: boolean\n /**\n * Props for the photo's own `Image`: `crossOrigin` and `referrerPolicy`\n * for where it is served from, and `resizeMethod`, `fadeDuration` and\n * `progressiveRenderingEnabled` for how Android decodes and shows it.\n */\n readonly imgProps?: MarkImageProps\n /**\n * The person this represents. Supplies both the initials and the\n * accessible name, so a screen reader announces who it is rather than\n * reading two letters aloud.\n *\n * A display name, as a reader would see it written: the initials are its\n * first and last words. A title before the name (\"Dr.\"), a generational\n * suffix after it (\"Jr.\", \"III\") and an aside in brackets at its end\n * (\"(she/her)\") are passed over, as is punctuation a word opens with. A\n * name written without spaces between its words, as Japanese and Chinese\n * names are, is one word, and takes one initial.\n */\n readonly name: string\n readonly ref?: Ref<View>\n /**\n * Whether an avatar with no initials to take, for a blank `name`, draws\n * Material Design's anonymous person. `false` leaves the tinted disc\n * empty, for a loading skeleton.\n * @default true\n */\n readonly showPersonIcon?: boolean\n /**\n * Diameter: `sm` 36dp, `md` 40dp, `lg` 56dp.\n * @default 'md'\n */\n readonly size?: 'lg' | 'md' | 'sm'\n /**\n * A photo to show in place of the initials. The initials stay until it has\n * loaded, and come back if it fails.\n */\n readonly src?: string\n readonly style?: StyleProp<ViewStyle>\n /**\n * Which container and on-container colour pair to tint with.\n * @default 'primary'\n */\n readonly tone?: 'negative' | 'positive' | 'primary' | 'secondary' | 'tertiary'\n}\n\n/**\n * A person, as a disc holding their photo or, until it loads or when there\n * is none, their initials. `name` is both where the initials come from and\n * what a screen reader announces, so it is required even with a photo.\n *\n * ```tsx\n * <Avatar name=\"Ada Lovelace\" size=\"lg\" src={photo} />\n * ```\n *\n * It is announced as an image named by `name`. When the person's name is\n * already written next to the avatar, mark it `decorative`, so a screen\n * reader reads the name once. A blank name names nothing, so the avatar is\n * then a plain view, which a screen reader passes over rather than reading\n * as an image with no name.\n *\n * Reach for ProductIcon when what is drawn is not a person; the two share\n * their sizes, so either fills the same slot in a row or a card.\n *\n * The initials are taken in the device's locale. `style`, `ref` and `testID`\n * land on the disc.\n */\nfunction Avatar({ size = 'md', tone = 'primary', ...props }: AvatarProps) {\n return <Mark {...props} kind=\"person\" size={size} tone={tone} />\n}\n\nexport type { AvatarProps }\n\nexport { Avatar }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,SAAAkB,OAAAC,IAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAJ,EAAA,OAAAD,IAAA;EAAgB,CAAA,CAAAL,MAAAS,IAAAN,MAAAO,IAAA,GAAAF,SAAAH;EAAwDC,EAAA,KAAAD;EAAAC,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAA;EAAAF,QAAAF,EAAA;EAAAG,KAAAH,EAAA;EAAAI,KAAAJ,EAAA;CAAA;CAAtD,MAAAN,OAAAS,OAAAE,KAAAA,IAAA,OAAAF;CAAa,MAAAN,OAAAO,OAAAC,KAAAA,IAAA,YAAAD;CAAgB,IAAAE;CAAA,IAAAN,EAAA,OAAAE,SAAAF,EAAA,OAAAN,QAAAM,EAAA,OAAAH,MAAA;EACtCS,KAAA,oBAAC,MAAD;GAAK,GAAKJ;GAAY,MAAA;GAAeR;GAAYG;EAAI,CAAA;EAAIG,EAAA,KAAAE;EAAAF,EAAA,KAAAN;EAAAM,EAAA,KAAAH;EAAAG,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CAAA,OAAzDM;AAAyD"}
1
+ {"version":3,"file":"index.js","names":["ComponentRef","Ref","StyleProp","View","ViewProps","ViewStyle","MarkImageProps","Mark","AvatarProps","Omit","decorative","imgProps","name","ref","showPersonIcon","size","src","style","tone","Avatar","t0","$","_c","props","t1","t2","undefined","t3","_c"],"sources":["../../../src/components/avatar/index.tsx"],"sourcesContent":["import type { ComponentRef, Ref } from 'react'\nimport type { StyleProp, View, ViewProps, ViewStyle } from 'react-native'\n\nimport type { MarkImageProps } from '../../mark'\n\nimport { Mark } from '../../mark'\n\n// A person, drawn as a disc with the photo cropped to fill it, as kanso-ui's\n// Avatar draws one. The box, the sizes, the tones, the photo's loading and\n// the labelling are the mark's in `src/mark`, which ProductIcon draws a thing\n// through as well, so a person and a thing fill the same slot.\n//\n// Which tone a given person gets is the consuming app's decision, since the\n// cycle of colours belongs where the list of people is.\n\ntype AvatarProps = Omit<ViewProps, 'children' | 'style'> & {\n /**\n * Hides the avatar from screen readers, for one beside the person's name,\n * such as a row whose headline is the name or a byline, where it would\n * otherwise announce the name a second time. `name` still supplies the\n * initials, and is not announced.\n * @default false\n */\n readonly decorative?: boolean\n /**\n * Props for the photo's own `Image`: `crossOrigin` and `referrerPolicy`\n * for where it is served from, and `resizeMethod`, `fadeDuration` and\n * `progressiveRenderingEnabled` for how Android decodes and shows it.\n */\n readonly imgProps?: MarkImageProps\n /**\n * The person this represents. Supplies both the initials and the\n * accessible name, so a screen reader announces who it is rather than\n * reading two letters aloud.\n *\n * A display name, as a reader would see it written: the initials are its\n * first and last words. A title before the name (\"Dr.\"), a generational\n * suffix after it (\"Jr.\", \"III\") and an aside in brackets at its end\n * (\"(she/her)\") are passed over, as is punctuation a word opens with. A\n * name written without spaces between its words, as Japanese and Chinese\n * names are, is one word, and takes one initial.\n */\n readonly name: string\n readonly ref?: Ref<ComponentRef<typeof View>>\n /**\n * Whether an avatar with no initials to take, for a blank `name`, draws\n * Material Design's anonymous person. `false` leaves the tinted disc\n * empty, for a loading skeleton.\n * @default true\n */\n readonly showPersonIcon?: boolean\n /**\n * Diameter: `sm` 36dp, `md` 40dp, `lg` 56dp.\n * @default 'md'\n */\n readonly size?: 'lg' | 'md' | 'sm'\n /**\n * A photo to show in place of the initials. The initials stay until it has\n * loaded, and come back if it fails.\n */\n readonly src?: string\n readonly style?: StyleProp<ViewStyle>\n /**\n * Which container and on-container colour pair to tint with.\n * @default 'primary'\n */\n readonly tone?: 'negative' | 'positive' | 'primary' | 'secondary' | 'tertiary'\n}\n\n/**\n * A person, as a disc holding their photo or, until it loads or when there\n * is none, their initials. `name` is both where the initials come from and\n * what a screen reader announces, so it is required even with a photo.\n *\n * ```tsx\n * <Avatar name=\"Ada Lovelace\" size=\"lg\" src={photo} />\n * ```\n *\n * It is announced as an image named by `name`. When the person's name is\n * already written next to the avatar, mark it `decorative`, so a screen\n * reader reads the name once. A blank name names nothing, so the avatar is\n * then a plain view, which a screen reader passes over rather than reading\n * as an image with no name.\n *\n * Reach for ProductIcon when what is drawn is not a person; the two share\n * their sizes, so either fills the same slot in a row or a card.\n *\n * The initials are taken in the device's locale. `style`, `ref` and `testID`\n * land on the disc.\n */\nfunction Avatar({ size = 'md', tone = 'primary', ...props }: AvatarProps) {\n return <Mark {...props} kind=\"person\" size={size} tone={tone} />\n}\n\nexport type { AvatarProps }\n\nexport { Avatar }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,SAAAmB,OAAAC,IAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAJ,EAAA,OAAAD,IAAA;EAAgB,CAAA,CAAAL,MAAAS,IAAAN,MAAAO,IAAA,GAAAF,SAAAH;EAAwDC,EAAA,KAAAD;EAAAC,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAA;EAAAF,QAAAF,EAAA;EAAAG,KAAAH,EAAA;EAAAI,KAAAJ,EAAA;CAAA;CAAtD,MAAAN,OAAAS,OAAAE,KAAAA,IAAA,OAAAF;CAAa,MAAAN,OAAAO,OAAAC,KAAAA,IAAA,YAAAD;CAAgB,IAAAE;CAAA,IAAAN,EAAA,OAAAE,SAAAF,EAAA,OAAAN,QAAAM,EAAA,OAAAH,MAAA;EACtCS,KAAA,oBAAC,MAAD;GAAK,GAAKJ;GAAY,MAAA;GAAeR;GAAYG;EAAI,CAAA;EAAIG,EAAA,KAAAE;EAAAF,EAAA,KAAAN;EAAAM,EAAA,KAAAH;EAAAG,EAAA,KAAAM;CAAA,OAAAA,KAAAN,EAAA;CAAA,OAAzDM;AAAyD"}
@@ -1,6 +1,6 @@
1
1
  import { ButtonLinkForm, ButtonPressForm, ButtonToggleForm } from "../../button/index.js";
2
2
  import { PressableProps, StyleProp, View, ViewStyle } from "react-native";
3
- import { ReactNode, Ref } from "react";
3
+ import { ComponentRef, ReactNode, Ref } from "react";
4
4
  //#region src/components/button/index.d.ts
5
5
  type ButtonForm = ButtonLinkForm | ButtonPressForm | (ButtonToggleForm & {
6
6
  /**
@@ -29,7 +29,7 @@ type ButtonProps = ButtonForm & Omit<PressableProps, 'children' | 'disabled' | '
29
29
  * @default false
30
30
  */
31
31
  readonly isDisabled?: boolean;
32
- readonly ref?: Ref<View>;
32
+ readonly ref?: Ref<ComponentRef<typeof View>>;
33
33
  /**
34
34
  * The corner the button rests at: `round` is the pill, `square` the
35
35
  * page's rounded rectangle, 12dp up to `md`, 16dp at `lg` and 28dp above.
@@ -81,7 +81,7 @@ type ButtonVariant = 'elevated' | 'filled' | 'outlined' | 'text' | 'tonal';
81
81
  * target through `hitSlop`, which a `hitSlop` of the call site's own replaces.
82
82
  * `style`, `ref` and `testID` land on the `Pressable`.
83
83
  */
84
- declare function Button({ children, defaultSelected, disableRipple, hitSlop: ownHitSlop, href, icon, isDisabled, isSelected, onBlur, onChange, onFocus, onHoverIn, onHoverOut, onLayout, onPress, onPressIn, onPressOut, shape, size, style, type, variant, ...props }: ButtonProps): import("react").JSX.Element;
84
+ declare function Button({ children, defaultSelected, disableRipple, hitSlop: ownHitSlop, href, icon, isDisabled, isPending, isSelected, onBlur, onChange, onFocus, onHoverIn, onHoverOut, onLayout, onLongPress, onPress, onPressIn, onPressOut, pendingLabel, shape, size, slot, style, type, variant, ...props }: ButtonProps): import("react").JSX.Element;
85
85
  //#endregion
86
86
  export { Button, type ButtonProps, type ButtonShape, type ButtonSize, type ButtonVariant };
87
87
  //# sourceMappingURL=index.d.ts.map