@kanso-labs/react-native-kanso-ui 0.4.0 → 0.5.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 (122) hide show
  1. package/dist/autocomplete/index.js +39 -0
  2. package/dist/autocomplete/index.js.map +1 -0
  3. package/dist/button/index.d.ts +2 -1
  4. package/dist/button/index.js +35 -2
  5. package/dist/button/index.js.map +1 -1
  6. package/dist/button/pending.js +1 -1
  7. package/dist/button/slots.d.ts +2 -1
  8. package/dist/button/slots.js.map +1 -1
  9. package/dist/chip/index.js +1 -1
  10. package/dist/components/autocomplete/index.d.ts +49 -0
  11. package/dist/components/autocomplete/index.js +87 -0
  12. package/dist/components/autocomplete/index.js.map +1 -0
  13. package/dist/components/avatar/index.d.ts +1 -1
  14. package/dist/components/button/index.d.ts +2 -2
  15. package/dist/components/button/index.js +196 -150
  16. package/dist/components/button/index.js.map +1 -1
  17. package/dist/components/card/index.d.ts +1 -1
  18. package/dist/components/card/index.js +1 -1
  19. package/dist/components/checkbox/index.d.ts +1 -1
  20. package/dist/components/checkbox/index.js +106 -193
  21. package/dist/components/checkbox/index.js.map +1 -1
  22. package/dist/components/chip/index.d.ts +1 -1
  23. package/dist/components/chip-group/index.d.ts +1 -1
  24. package/dist/components/chip-group/index.js +1 -1
  25. package/dist/components/copy-field/index.d.ts +1 -1
  26. package/dist/components/copy-field/index.js +1 -1
  27. package/dist/components/currency/index.d.ts +1 -1
  28. package/dist/components/currency/index.js +1 -1
  29. package/dist/components/dialog/index.d.ts +1 -1
  30. package/dist/components/dialog/index.js +3 -2
  31. package/dist/components/dialog/index.js.map +1 -1
  32. package/dist/components/icon-button/index.d.ts +2 -2
  33. package/dist/components/icon-button/index.js +205 -159
  34. package/dist/components/icon-button/index.js.map +1 -1
  35. package/dist/components/link/index.d.ts +1 -1
  36. package/dist/components/link/index.js +1 -1
  37. package/dist/components/list/index.d.ts +216 -0
  38. package/dist/components/list/index.js +1118 -0
  39. package/dist/components/list/index.js.map +1 -0
  40. package/dist/components/list-item/index.d.ts +63 -0
  41. package/dist/components/list-item/index.js +275 -0
  42. package/dist/components/list-item/index.js.map +1 -0
  43. package/dist/components/menu/index.d.ts +309 -0
  44. package/dist/components/menu/index.js +1283 -0
  45. package/dist/components/menu/index.js.map +1 -0
  46. package/dist/components/meter/index.d.ts +1 -1
  47. package/dist/components/meter/index.js +1 -1
  48. package/dist/components/number-field/index.d.ts +1 -1
  49. package/dist/components/number-field/index.js +2 -2
  50. package/dist/components/popover/index.d.ts +140 -0
  51. package/dist/components/popover/index.js +704 -0
  52. package/dist/components/popover/index.js.map +1 -0
  53. package/dist/components/product-icon/index.d.ts +1 -1
  54. package/dist/components/progress-indicator/index.d.ts +1 -1
  55. package/dist/components/progress-indicator/index.js +1 -1
  56. package/dist/components/radio-group/index.d.ts +1 -1
  57. package/dist/components/radio-group/index.js +1 -1
  58. package/dist/components/search-field/index.d.ts +12 -4
  59. package/dist/components/search-field/index.js +64 -58
  60. package/dist/components/search-field/index.js.map +1 -1
  61. package/dist/components/segmented-button/index.d.ts +1 -1
  62. package/dist/components/segmented-button/index.js +1 -1
  63. package/dist/components/separator/index.d.ts +1 -1
  64. package/dist/components/separator/index.js +1 -1
  65. package/dist/components/sheet/index.d.ts +1 -1
  66. package/dist/components/sheet/index.js +3 -2
  67. package/dist/components/sheet/index.js.map +1 -1
  68. package/dist/components/snackbar/index.d.ts +1 -1
  69. package/dist/components/snackbar/index.js +1 -1
  70. package/dist/components/stack/index.d.ts +1 -1
  71. package/dist/components/stack/index.js +1 -1
  72. package/dist/components/switch/index.d.ts +1 -1
  73. package/dist/components/switch/index.js +1 -1
  74. package/dist/components/text/index.d.ts +1 -1
  75. package/dist/components/text/index.js +1 -1
  76. package/dist/components/text-field/index.d.ts +1 -1
  77. package/dist/components/text-field/index.js +1 -1
  78. package/dist/control/checkbox-box.js +137 -0
  79. package/dist/control/checkbox-box.js.map +1 -0
  80. package/dist/control/index.js +1 -1
  81. package/dist/field/index.d.ts +1 -1
  82. package/dist/field/index.js +1 -1
  83. package/dist/form/index.d.ts +1 -1
  84. package/dist/glyphs/index.js +21 -2
  85. package/dist/glyphs/index.js.map +1 -1
  86. package/dist/i18n/index.js +16 -1
  87. package/dist/i18n/index.js.map +1 -1
  88. package/dist/i18n/messages.js +272 -34
  89. package/dist/i18n/messages.js.map +1 -1
  90. package/dist/index.d.ts +6 -1
  91. package/dist/index.js +7 -2
  92. package/dist/indicator/index.d.ts +1 -1
  93. package/dist/indicator/index.js +2 -2
  94. package/dist/mark/index.d.ts +1 -1
  95. package/dist/mark/index.js +1 -1
  96. package/dist/modal/index.d.ts +1 -1
  97. package/dist/modal/index.js +2 -9
  98. package/dist/modal/index.js.map +1 -1
  99. package/dist/overlay/anchored.d.ts +2 -0
  100. package/dist/overlay/anchored.js +182 -0
  101. package/dist/overlay/anchored.js.map +1 -0
  102. package/dist/overlay/host.d.ts +2 -0
  103. package/dist/overlay/host.js +1 -1
  104. package/dist/overlay/index.d.ts +4 -0
  105. package/dist/overlay/panel.js +215 -0
  106. package/dist/overlay/panel.js.map +1 -0
  107. package/dist/overlay/placement.d.ts +8 -0
  108. package/dist/overlay/placement.js +87 -0
  109. package/dist/overlay/placement.js.map +1 -0
  110. package/dist/press/index.js +1 -1
  111. package/dist/ref/index.js +15 -0
  112. package/dist/ref/index.js.map +1 -0
  113. package/dist/router/index.js +1 -1
  114. package/dist/row/index.js +158 -0
  115. package/dist/row/index.js.map +1 -0
  116. package/dist/row/styles.js +51 -0
  117. package/dist/row/styles.js.map +1 -0
  118. package/dist/theme/scope.js +1 -1
  119. package/dist/theme-scope.d.ts +1 -1
  120. package/dist/theme-scope.js +1 -1
  121. package/dist/typography/fonts.js +1 -1
  122. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.js","names":["RefObject","HostInstance","useEffect","useRef","AccessibilityInfo","BackHandler","Platform","Pressable","StyleSheet","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","OverlaySide","useTheme","SCALE_FROM","PANEL_ORIGINS","bottom","left","right","top","const","Record","styles","create","layer","position","DismissLayer","t0","$","_c","onPress","t1","useBackToClose","close","active","OS","subscription","addEventListener","remove","useFocusOnPlaced","ready","target","sendAccessibilityEvent","usePanelMotion","isOpen","placed","setPresent","motion","reduceMotion","shown","set","duration","durationShort3","easing","bezier","easingEmphasizedDecelerate","durationShort2","easingEmphasizedAccelerate","timer","setTimeout","clearTimeout","progress","get","opacity","transform","scale","useReturnFocus","present","quiet","trigger","current","gone","_c"],"sources":["../../src/overlay/panel.tsx"],"sourcesContent":["import type { RefObject } from 'react'\nimport type { HostInstance } from 'react-native'\n\nimport { useEffect, useRef } from 'react'\nimport {\n AccessibilityInfo,\n BackHandler,\n Platform,\n Pressable,\n StyleSheet,\n} from 'react-native'\nimport {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { OverlaySide } from './placement'\n\nimport { useTheme } from '../theme/scope'\n\n// What a non-modal panel anchored beside its trigger does besides being\n// placed, shared by Popover and Menu so the two cannot drift: the layer a\n// press outside lands on, Android's back gesture, the motion in and out, and\n// where a screen reader is sent as it comes and goes.\n\n// What the panel scales up from as it arrives, web's keyframes'.\nconst SCALE_FROM = 0.95\n\n/** The edge a panel grows from, the one facing its anchor. */\nconst PANEL_ORIGINS = {\n bottom: 'top',\n left: 'right',\n right: 'left',\n top: 'bottom',\n} as const satisfies Record<OverlaySide, string>\n\nconst styles = StyleSheet.create({\n // Over the whole screen and under the panel.\n layer: {\n bottom: 0,\n left: 0,\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n/**\n * The layer a press outside a non-modal panel lands on, drawn just before the\n * panel so it sits under it. The press goes no further, so a press on the\n * trigger closes the panel rather than opening it again, as on web. Nothing\n * but that press reaches it: React Native Web's Pressable is a tab stop\n * unless told otherwise, whatever `focusable` says.\n */\nfunction DismissLayer({ onPress }: { readonly onPress: () => void }) {\n return (\n <Pressable\n accessible={false}\n aria-hidden\n focusable={false}\n onPress={onPress}\n style={styles.layer}\n tabIndex={-1}\n />\n )\n}\n\n/**\n * Closes a non-modal panel from Android's back gesture, as a `Modal` closes a\n * modal one. React Native calls the latest listener first, so a panel opened\n * from another closes before the one it came from. React Native Web has no\n * back gesture, and its BackHandler reports an error when one is listened for,\n * so nothing listens there.\n */\nfunction useBackToClose(close: () => void, active: boolean) {\n useEffect(() => {\n if (!active || Platform.OS === 'web') {\n return undefined\n }\n\n const subscription = BackHandler.addEventListener(\n 'hardwareBackPress',\n () => {\n close()\n return true\n },\n )\n\n return () => {\n subscription.remove()\n }\n }, [active, close])\n}\n\n/**\n * Sends the screen reader to `target` once `ready`, which a panel passes once\n * it has been placed, and so can be seen. React Native Web has no screen\n * reader of its own to send.\n */\nfunction useFocusOnPlaced(ready: boolean, target: HostInstance | null) {\n useEffect(() => {\n if (ready && target !== null && Platform.OS !== 'web') {\n AccessibilityInfo.sendAccessibilityEvent(target, 'focus')\n }\n }, [ready, target])\n}\n\n/**\n * A panel's motion, and the timer that unmounts it once its exit has run: it\n * fades and scales up from 95% over the short3 duration on the emphasised\n * decelerating curve, and back over short2 on the accelerating one. The\n * entrance waits for the panel to be placed, so it is not spent while the\n * panel is still hidden being measured, and the panel stays transparent\n * until then. Under reduced motion it arrives and leaves at once.\n */\nfunction usePanelMotion(\n isOpen: boolean,\n placed: boolean,\n setPresent: (present: boolean) => void,\n) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const shown = useSharedValue(0)\n\n useEffect(() => {\n if (isOpen && !placed) {\n return undefined\n }\n\n if (isOpen) {\n shown.set(\n reduceMotion\n ? 1\n : withTiming(1, {\n duration: motion.durationShort3,\n easing: Easing.bezier(...motion.easingEmphasizedDecelerate),\n }),\n )\n return undefined\n }\n\n if (reduceMotion) {\n shown.set(0)\n setPresent(false)\n return undefined\n }\n\n shown.set(\n withTiming(0, {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasizedAccelerate),\n }),\n )\n const timer = setTimeout(() => {\n setPresent(false)\n }, motion.durationShort2)\n\n return () => {\n clearTimeout(timer)\n }\n }, [isOpen, motion, placed, reduceMotion, setPresent, shown])\n\n return useAnimatedStyle(() => {\n 'worklet'\n const progress = shown.get()\n\n return {\n opacity: progress,\n transform: [{ scale: SCALE_FROM + (1 - SCALE_FROM) * progress }],\n }\n })\n}\n\n/**\n * Sends the screen reader back to the trigger once a panel has gone, unless\n * `quiet` says nothing moved it there, as a pointer resting on a hover\n * trigger does not. React Native Web's `Modal` restores focus itself, and has\n * no screen reader to send otherwise.\n */\nfunction useReturnFocus(\n present: boolean,\n quiet: boolean,\n trigger: RefObject<HostInstance | null>,\n) {\n const shown = useRef(present)\n\n useEffect(() => {\n const gone = shown.current && !present\n const target = trigger.current\n\n shown.current = present\n if (gone && !quiet && target !== null && Platform.OS !== 'web') {\n AccessibilityInfo.sendAccessibilityEvent(target, 'focus')\n }\n }, [quiet, present, trigger])\n}\n\nexport {\n DismissLayer,\n PANEL_ORIGINS,\n useBackToClose,\n useFocusOnPlaced,\n usePanelMotion,\n useReturnFocus,\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ //#region src/overlay/placement.d.ts
2
+ /** Where along that side it lines up with the anchor. */
3
+ type OverlayAlign = 'center' | 'end' | 'start';
4
+ /** Which side of the anchor a surface opens on. */
5
+ type OverlaySide = 'bottom' | 'left' | 'right' | 'top';
6
+ //#endregion
7
+ export type { OverlayAlign, OverlaySide };
8
+ //# sourceMappingURL=placement.d.ts.map
@@ -0,0 +1,87 @@
1
+ //#region src/overlay/placement.ts
2
+ const OPPOSITE = {
3
+ bottom: "top",
4
+ left: "right",
5
+ right: "left",
6
+ top: "bottom"
7
+ };
8
+ /**
9
+ * Where a surface of a given length starts along the anchor, for an edge,
10
+ * moved by the offset. However far the offset moves it, it keeps overlapping
11
+ * the anchor, as React Aria's placement does on web.
12
+ */
13
+ function along(start, length, size, edge, offset) {
14
+ const lined = edge === "near" ? start : edge === "far" ? start + length - size : start + (length - size) / 2;
15
+ return Math.min(Math.max(lined + offset, start - size), start + length);
16
+ }
17
+ /**
18
+ * Bounds a surface stays inside: the window, less the safe area's insets and
19
+ * a padding on every side.
20
+ */
21
+ function boundsOf(window, insets, padding) {
22
+ return {
23
+ height: window.height - insets.top - insets.bottom - padding * 2,
24
+ width: window.width - insets.left - insets.right - padding * 2,
25
+ x: insets.left + padding,
26
+ y: insets.top + padding
27
+ };
28
+ }
29
+ function edgeOf(align, inline, rtl) {
30
+ if (align === "center") return "center";
31
+ return align === "start" === (inline && rtl) ? "far" : "near";
32
+ }
33
+ function isVertical(side) {
34
+ return side === "bottom" || side === "top";
35
+ }
36
+ /**
37
+ * Places a surface beside its anchor, as React Aria places a popover on web.
38
+ *
39
+ * It opens on `side`, lined up along it by `align`, and flips to the opposite
40
+ * side when it does not fit and that side has more room. Along the side it is
41
+ * shifted to stay inside the bounds; across it, on `top` and `bottom`, it
42
+ * stays inside them through `maxHeight`, and on `left` and `right`, where a
43
+ * surface wider than both sides has nowhere to go, it is shifted over the
44
+ * anchor rather than past the edge.
45
+ */
46
+ function place({ align, alignOffset, anchor, bounds, rtl, side: preferred, sideOffset, surface }) {
47
+ const room = (on) => spaceOn(on, anchor, bounds, sideOffset);
48
+ const opposite = OPPOSITE[preferred];
49
+ const side = (isVertical(preferred) ? surface.height : surface.width) > room(preferred) && room(opposite) > room(preferred) ? opposite : preferred;
50
+ if (isVertical(side)) {
51
+ const maxHeight = Math.max(0, room(side));
52
+ const height = Math.min(surface.height, maxHeight);
53
+ return {
54
+ maxHeight,
55
+ side,
56
+ x: within(along(anchor.x, anchor.width, surface.width, edgeOf(align, true, rtl), alignOffset), bounds.x, bounds.width, surface.width),
57
+ y: side === "bottom" ? anchor.y + anchor.height + sideOffset : anchor.y - sideOffset - height
58
+ };
59
+ }
60
+ const maxHeight = Math.max(0, bounds.height);
61
+ const height = Math.min(surface.height, maxHeight);
62
+ const x = side === "right" ? anchor.x + anchor.width + sideOffset : anchor.x - sideOffset - surface.width;
63
+ const y = along(anchor.y, anchor.height, height, edgeOf(align, false, rtl), alignOffset);
64
+ return {
65
+ maxHeight,
66
+ side,
67
+ x: within(x, bounds.x, bounds.width, surface.width),
68
+ y: within(y, bounds.y, bounds.height, height)
69
+ };
70
+ }
71
+ function spaceOn(side, anchor, bounds, sideOffset) {
72
+ return {
73
+ bottom: bounds.y + bounds.height - anchor.y - anchor.height,
74
+ left: anchor.x - bounds.x,
75
+ right: bounds.x + bounds.width - anchor.x - anchor.width,
76
+ top: anchor.y - bounds.y
77
+ }[side] - sideOffset;
78
+ }
79
+ function within(position, start, length, size) {
80
+ if (position < start) return start;
81
+ if (position + size > start + length) return Math.max(start + length - size, start);
82
+ return position;
83
+ }
84
+ //#endregion
85
+ export { boundsOf, place };
86
+
87
+ //# sourceMappingURL=placement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placement.js","names":["Edge","OverlayAlign","OverlaySide","Placement","maxHeight","side","x","y","PlacementInput","align","alignOffset","anchor","Rect","bounds","rtl","sideOffset","surface","Size","height","width","OPPOSITE","bottom","left","right","top","const","Record","along","start","length","size","edge","offset","lined","Math","min","max","boundsOf","window","insets","padding","edgeOf","inline","isVertical","place","preferred","room","on","spaceOn","opposite","within","position"],"sources":["../../src/overlay/placement.ts"],"sourcesContent":["// Which end of the anchor the surface lines up with, along the side.\ntype Edge = 'center' | 'far' | 'near'\n\n/** Where along that side it lines up with the anchor. */\ntype OverlayAlign = 'center' | 'end' | 'start'\n\n/** Which side of the anchor a surface opens on. */\ntype OverlaySide = 'bottom' | 'left' | 'right' | 'top'\n\n/** Where an anchored surface sits, in the coordinates its inputs were in. */\ntype Placement = {\n /**\n * The height the surface may take before it would pass the edge of the\n * bounds on the side it opened on. A surface taller than this scrolls.\n */\n readonly maxHeight: number\n /** The side it opened on, which is the opposite one after a flip. */\n readonly side: OverlaySide\n readonly x: number\n readonly y: number\n}\n\ntype PlacementInput = {\n readonly align: OverlayAlign\n /** Moves the surface along the side, in dp: right or down when positive. */\n readonly alignOffset: number\n readonly anchor: Rect\n /** The area the surface stays inside, padding already taken off. */\n readonly bounds: Rect\n /** Whether `start` and `end` read from the right, on `top` and `bottom`. */\n readonly rtl: boolean\n readonly side: OverlaySide\n /** The gap between the anchor and the surface, in dp. */\n readonly sideOffset: number\n readonly surface: Size\n}\n\ntype Rect = Size & { readonly x: number; readonly y: number }\n\ntype Size = { readonly height: number; readonly width: number }\n\nconst OPPOSITE = {\n bottom: 'top',\n left: 'right',\n right: 'left',\n top: 'bottom',\n} as const satisfies Record<OverlaySide, OverlaySide>\n\n/**\n * Where a surface of a given length starts along the anchor, for an edge,\n * moved by the offset. However far the offset moves it, it keeps overlapping\n * the anchor, as React Aria's placement does on web.\n */\nfunction along(\n start: number,\n length: number,\n size: number,\n edge: Edge,\n offset: number,\n) {\n const lined =\n edge === 'near'\n ? start\n : edge === 'far'\n ? start + length - size\n : start + (length - size) / 2\n\n return Math.min(Math.max(lined + offset, start - size), start + length)\n}\n\n/**\n * Bounds a surface stays inside: the window, less the safe area's insets and\n * a padding on every side.\n */\nfunction boundsOf(\n window: Size,\n insets: { bottom: number; left: number; right: number; top: number },\n padding: number,\n): Rect {\n return {\n height: window.height - insets.top - insets.bottom - padding * 2,\n width: window.width - insets.left - insets.right - padding * 2,\n x: insets.left + padding,\n y: insets.top + padding,\n }\n}\n\n// Which end of the anchor an alignment names. On `top` and `bottom` the\n// surface lines up along the inline axis, so `start` is the left edge, or the\n// right one in a right-to-left layout; on `left` and `right` it lines up along\n// the block axis, where `start` is the top edge in both.\nfunction edgeOf(align: OverlayAlign, inline: boolean, rtl: boolean): Edge {\n if (align === 'center') {\n return 'center'\n }\n\n return (align === 'start') === (inline && rtl) ? 'far' : 'near'\n}\n\nfunction isVertical(side: OverlaySide) {\n return side === 'bottom' || side === 'top'\n}\n\n/**\n * Places a surface beside its anchor, as React Aria places a popover on web.\n *\n * It opens on `side`, lined up along it by `align`, and flips to the opposite\n * side when it does not fit and that side has more room. Along the side it is\n * shifted to stay inside the bounds; across it, on `top` and `bottom`, it\n * stays inside them through `maxHeight`, and on `left` and `right`, where a\n * surface wider than both sides has nowhere to go, it is shifted over the\n * anchor rather than past the edge.\n */\nfunction place({\n align,\n alignOffset,\n anchor,\n bounds,\n rtl,\n side: preferred,\n sideOffset,\n surface,\n}: PlacementInput): Placement {\n const room = (on: OverlaySide) => spaceOn(on, anchor, bounds, sideOffset)\n const opposite = OPPOSITE[preferred]\n const length = isVertical(preferred) ? surface.height : surface.width\n const side =\n length > room(preferred) && room(opposite) > room(preferred)\n ? opposite\n : preferred\n\n if (isVertical(side)) {\n const maxHeight = Math.max(0, room(side))\n const height = Math.min(surface.height, maxHeight)\n const x = along(\n anchor.x,\n anchor.width,\n surface.width,\n edgeOf(align, true, rtl),\n alignOffset,\n )\n\n return {\n maxHeight,\n side,\n x: within(x, bounds.x, bounds.width, surface.width),\n y:\n side === 'bottom'\n ? anchor.y + anchor.height + sideOffset\n : anchor.y - sideOffset - height,\n }\n }\n\n const maxHeight = Math.max(0, bounds.height)\n const height = Math.min(surface.height, maxHeight)\n const x =\n side === 'right'\n ? anchor.x + anchor.width + sideOffset\n : anchor.x - sideOffset - surface.width\n const y = along(\n anchor.y,\n anchor.height,\n height,\n edgeOf(align, false, rtl),\n alignOffset,\n )\n\n return {\n maxHeight,\n side,\n x: within(x, bounds.x, bounds.width, surface.width),\n y: within(y, bounds.y, bounds.height, height),\n }\n}\n\n// The room between the anchor and the edge of the bounds on a side, less the\n// gap the surface keeps from the anchor.\nfunction spaceOn(\n side: OverlaySide,\n anchor: Rect,\n bounds: Rect,\n sideOffset: number,\n) {\n const room = {\n bottom: bounds.y + bounds.height - anchor.y - anchor.height,\n left: anchor.x - bounds.x,\n right: bounds.x + bounds.width - anchor.x - anchor.width,\n top: anchor.y - bounds.y,\n }\n\n return room[side] - sideOffset\n}\n\n// Shifts a surface along an axis until it is inside the bounds, or, when it is\n// longer than they are, until it starts where they start.\nfunction within(position: number, start: number, length: number, size: number) {\n if (position < start) {\n return start\n }\n\n if (position + size > start + length) {\n return Math.max(start + length - size, start)\n }\n\n return position\n}\n\nexport type { OverlayAlign, OverlaySide, Placement, PlacementInput, Rect, Size }\n\nexport { boundsOf, place }\n"],"mappings":";AAyCA,MAAMoB,WAAW;CACfC,QAAQ;CACRC,MAAM;CACNC,OAAO;CACPC,KAAK;AACP;;;;;;AAOA,SAASG,MACPC,OACAC,QACAC,MACAC,MACAC,QACA;CACA,MAAMC,QACJF,SAAS,SACLH,QACAG,SAAS,QACPH,QAAQC,SAASC,OACjBF,SAASC,SAASC,QAAQ;CAElC,OAAOI,KAAKC,IAAID,KAAKE,IAAIH,QAAQD,QAAQJ,QAAQE,IAAI,GAAGF,QAAQC,MAAM;AACxE;;;;;AAMA,SAASQ,SACPC,QACAC,QACAC,SACM;CACN,OAAO;EACLtB,QAAQoB,OAAOpB,SAASqB,OAAOf,MAAMe,OAAOlB,SAASmB,UAAU;EAC/DrB,OAAOmB,OAAOnB,QAAQoB,OAAOjB,OAAOiB,OAAOhB,QAAQiB,UAAU;EAC7DlC,GAAGiC,OAAOjB,OAAOkB;EACjBjC,GAAGgC,OAAOf,MAAMgB;CAClB;AACF;AAMA,SAASC,OAAOhC,OAAqBiC,QAAiB5B,KAAoB;CACxE,IAAIL,UAAU,UACZ,OAAO;CAGT,OAAQA,UAAU,aAAciC,UAAU5B,OAAO,QAAQ;AAC3D;AAEA,SAAS6B,WAAWtC,MAAmB;CACrC,OAAOA,SAAS,YAAYA,SAAS;AACvC;;;;;;;;;;;AAYA,SAASuC,MAAM,EACbnC,OACAC,aACAC,QACAE,QACAC,KACAT,MAAMwC,WACN9B,YACAC,WAC4B;CAC5B,MAAM8B,QAAQC,OAAoBC,QAAQD,IAAIpC,QAAQE,QAAQE,UAAU;CACxE,MAAMkC,WAAW7B,SAASyB;CAE1B,MAAMxC,QADSsC,WAAWE,SAAS,IAAI7B,QAAQE,SAASF,QAAQG,SAErD2B,KAAKD,SAAS,KAAKC,KAAKG,QAAQ,IAAIH,KAAKD,SAAS,IACvDI,WACAJ;CAEN,IAAIF,WAAWtC,IAAI,GAAG;EACpB,MAAMD,YAAY8B,KAAKE,IAAI,GAAGU,KAAKzC,IAAI,CAAC;EACxC,MAAMa,SAASgB,KAAKC,IAAInB,QAAQE,QAAQd,SAAS;EASjD,OAAO;GACLA;GACAC;GACAC,GAAG4C,OAXKvB,MACRhB,OAAOL,GACPK,OAAOQ,OACPH,QAAQG,OACRsB,OAAOhC,OAAO,MAAMK,GAAG,GACvBJ,WAMUJ,GAAGO,OAAOP,GAAGO,OAAOM,OAAOH,QAAQG,KAAK;GAClDZ,GACEF,SAAS,WACLM,OAAOJ,IAAII,OAAOO,SAASH,aAC3BJ,OAAOJ,IAAIQ,aAAaG;EAChC;CACF;CAEA,MAAMd,YAAY8B,KAAKE,IAAI,GAAGvB,OAAOK,MAAM;CAC3C,MAAMA,SAASgB,KAAKC,IAAInB,QAAQE,QAAQd,SAAS;CACjD,MAAME,IACJD,SAAS,UACLM,OAAOL,IAAIK,OAAOQ,QAAQJ,aAC1BJ,OAAOL,IAAIS,aAAaC,QAAQG;CACtC,MAAMZ,IAAIoB,MACRhB,OAAOJ,GACPI,OAAOO,QACPA,QACAuB,OAAOhC,OAAO,OAAOK,GAAG,GACxBJ,WACF;CAEA,OAAO;EACLN;EACAC;EACAC,GAAG4C,OAAO5C,GAAGO,OAAOP,GAAGO,OAAOM,OAAOH,QAAQG,KAAK;EAClDZ,GAAG2C,OAAO3C,GAAGM,OAAON,GAAGM,OAAOK,QAAQA,MAAM;CAC9C;AACF;AAIA,SAAS8B,QACP3C,MACAM,QACAE,QACAE,YACA;CAQA,OAAO+B;EANLzB,QAAQR,OAAON,IAAIM,OAAOK,SAASP,OAAOJ,IAAII,OAAOO;EACrDI,MAAMX,OAAOL,IAAIO,OAAOP;EACxBiB,OAAOV,OAAOP,IAAIO,OAAOM,QAAQR,OAAOL,IAAIK,OAAOQ;EACnDK,KAAKb,OAAOJ,IAAIM,OAAON;CAGlBuC,EAAKzC,QAAQU;AACtB;AAIA,SAASmC,OAAOC,UAAkBvB,OAAeC,QAAgBC,MAAc;CAC7E,IAAIqB,WAAWvB,OACb,OAAOA;CAGT,IAAIuB,WAAWrB,OAAOF,QAAQC,QAC5B,OAAOK,KAAKE,IAAIR,QAAQC,SAASC,MAAMF,KAAK;CAG9C,OAAOuB;AACT"}
@@ -2,9 +2,9 @@ import { useTheme } from "../theme/scope.js";
2
2
  import { composeStyles } from "../theme/styles.js";
3
3
  import { styles, stylesOf } from "./styles.js";
4
4
  import { c } from "react/compiler-runtime";
5
- import { View } from "react-native";
6
5
  import { useRef, useState } from "react";
7
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { View } from "react-native";
8
8
  import Animated, { Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withDelay, withTiming } from "react-native-reanimated";
9
9
  const INITIAL_ORIGIN_SCALE = .2;
10
10
  const FADE_IN = 105;
@@ -0,0 +1,15 @@
1
+ //#region src/ref/index.ts
2
+ /**
3
+ * A ref callback that hands its node to every ref given, a callback or an
4
+ * object alike, in the order given.
5
+ */
6
+ function refsOf(...refs) {
7
+ return (node) => {
8
+ for (const ref of refs) if (typeof ref === "function") ref(node);
9
+ else if (ref !== null && ref !== void 0) ref.current = node;
10
+ };
11
+ }
12
+ //#endregion
13
+ export { refsOf };
14
+
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["Ref","refsOf","refs","T","node","ref","undefined","current"],"sources":["../../src/ref/index.ts"],"sourcesContent":["import type { Ref } from 'react'\n\n// Hands one node to several refs, for a part that keeps its node for itself\n// and also lands a call site's `ref` on it. Internal: nothing here is\n// exported from the package.\n\n/**\n * A ref callback that hands its node to every ref given, a callback or an\n * object alike, in the order given.\n */\nfunction refsOf<T>(...refs: readonly (Ref<T> | undefined)[]) {\n return (node: null | T) => {\n for (const ref of refs) {\n if (typeof ref === 'function') {\n ref(node)\n } else if (ref !== null && ref !== undefined) {\n ref.current = node\n }\n }\n }\n}\n\nexport { refsOf }\n"],"mappings":";;;;;AAUA,SAASC,OAAU,GAAGC,MAAuC;CAC3D,QAAQE,SAAmB;EACzB,KAAK,MAAMC,OAAOH,MAChB,IAAI,OAAOG,QAAQ,YACjBA,IAAID,IAAI;OACH,IAAIC,QAAQ,QAAQA,QAAQC,KAAAA,GACjCD,IAAIE,UAAUH;CAGpB;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { c } from "react/compiler-runtime";
2
- import { Linking } from "react-native";
3
2
  import { createContext, use } from "react";
4
3
  import { jsx } from "react/jsx-runtime";
4
+ import { Linking } from "react-native";
5
5
  //#region src/router/index.tsx
6
6
  const SCHEME = /^[a-z][\d+.a-z-]*:/iu;
7
7
  const RouterContext = createContext(null);
@@ -0,0 +1,158 @@
1
+ import { IconStyleContext } from "../glyphs/icon-style.js";
2
+ import { useTheme } from "../theme/scope.js";
3
+ import { composeStyles } from "../theme/styles.js";
4
+ import { useTypeRole } from "../typography/roles.js";
5
+ import { rowContentColor, rowStyles, rowStylesOf } from "./styles.js";
6
+ import { c } from "react/compiler-runtime";
7
+ import "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { Text, View } from "react-native";
10
+ //#region src/row/index.tsx
11
+ const ICON_SIZE = 24;
12
+ /** Whether a slot was given something to draw. */
13
+ function hasContent(node) {
14
+ return node !== void 0 && node !== null && node !== false && node !== "";
15
+ }
16
+ /**
17
+ * The inside of a row: the leading slot, the headline with the lines beside
18
+ * it, and the trailing slot, each drawn only when given. The view around it
19
+ * is the caller's, ListItem's or a menu's item, and takes `rowStyleOf`; this
20
+ * is what each of them puts inside it, so a list and a menu draw the same
21
+ * row. Both slots hand an icon the row's content colour at 24dp, through
22
+ * `useIconStyle()`. Not a component of the library's own and not exported.
23
+ */
24
+ function RowContent(t0) {
25
+ const $ = c(29);
26
+ const { children, isDisabled: t1, isSelected: t2, leading, overline, supporting, trailing, variant: t3 } = t0;
27
+ const isDisabled = t1 === void 0 ? false : t1;
28
+ const isSelected = t2 === void 0 ? false : t2;
29
+ const variant = t3 === void 0 ? "list" : t3;
30
+ const theme = useTheme();
31
+ const own = rowStylesOf(theme);
32
+ const headlineRole = useTypeRole(variant === "menu" ? "labelLarge" : "bodyLarge");
33
+ const overlineRole = useTypeRole("labelSmall");
34
+ const supportingRole = useTypeRole("bodyMedium");
35
+ let t4;
36
+ if ($[0] !== isDisabled || $[1] !== isSelected || $[2] !== theme || $[3] !== variant) {
37
+ t4 = rowContentColor(theme, {
38
+ isDisabled,
39
+ isSelected,
40
+ variant
41
+ });
42
+ $[0] = isDisabled;
43
+ $[1] = isSelected;
44
+ $[2] = theme;
45
+ $[3] = variant;
46
+ $[4] = t4;
47
+ } else t4 = $[4];
48
+ const color = t4;
49
+ let t5;
50
+ if ($[5] !== color) {
51
+ t5 = {
52
+ color,
53
+ size: ICON_SIZE
54
+ };
55
+ $[5] = color;
56
+ $[6] = t5;
57
+ } else t5 = $[6];
58
+ const icons = t5;
59
+ const tone = isDisabled || isSelected ? { color } : own.muted;
60
+ const T0 = IconStyleContext;
61
+ let t6;
62
+ if ($[7] !== leading) {
63
+ t6 = hasContent(leading) ? /* @__PURE__ */ jsx(View, {
64
+ style: rowStyles.slot,
65
+ children: leading
66
+ }) : null;
67
+ $[7] = leading;
68
+ $[8] = t6;
69
+ } else t6 = $[8];
70
+ const T1 = View;
71
+ const t7 = own.main;
72
+ const t8 = hasContent(overline) ? /* @__PURE__ */ jsx(Text, {
73
+ style: composeStyles(overlineRole, tone),
74
+ children: overline
75
+ }) : null;
76
+ let t9;
77
+ if ($[9] !== color || $[10] !== headlineRole) {
78
+ t9 = composeStyles(headlineRole, { color });
79
+ $[9] = color;
80
+ $[10] = headlineRole;
81
+ $[11] = t9;
82
+ } else t9 = $[11];
83
+ let t10;
84
+ if ($[12] !== children || $[13] !== t9) {
85
+ t10 = /* @__PURE__ */ jsx(Text, {
86
+ style: t9,
87
+ children
88
+ });
89
+ $[12] = children;
90
+ $[13] = t9;
91
+ $[14] = t10;
92
+ } else t10 = $[14];
93
+ const t11 = hasContent(supporting) ? /* @__PURE__ */ jsx(Text, {
94
+ style: composeStyles(supportingRole, tone),
95
+ children: supporting
96
+ }) : null;
97
+ let t12;
98
+ if ($[15] !== T1 || $[16] !== own || $[17] !== t10 || $[18] !== t11 || $[19] !== t8) {
99
+ t12 = /* @__PURE__ */ jsxs(T1, {
100
+ style: t7,
101
+ children: [
102
+ t8,
103
+ t10,
104
+ t11
105
+ ]
106
+ });
107
+ $[15] = T1;
108
+ $[16] = own;
109
+ $[17] = t10;
110
+ $[18] = t11;
111
+ $[19] = t8;
112
+ $[20] = t12;
113
+ } else t12 = $[20];
114
+ let t13;
115
+ if ($[21] !== trailing) {
116
+ t13 = hasContent(trailing) ? /* @__PURE__ */ jsx(View, {
117
+ style: rowStyles.slot,
118
+ children: trailing
119
+ }) : null;
120
+ $[21] = trailing;
121
+ $[22] = t13;
122
+ } else t13 = $[22];
123
+ let t14;
124
+ if ($[23] !== T0 || $[24] !== icons || $[25] !== t12 || $[26] !== t13 || $[27] !== t6) {
125
+ t14 = /* @__PURE__ */ jsxs(T0, {
126
+ value: icons,
127
+ children: [
128
+ t6,
129
+ t12,
130
+ t13
131
+ ]
132
+ });
133
+ $[23] = T0;
134
+ $[24] = icons;
135
+ $[25] = t12;
136
+ $[26] = t13;
137
+ $[27] = t6;
138
+ $[28] = t14;
139
+ } else t14 = $[28];
140
+ return t14;
141
+ }
142
+ /**
143
+ * The style of the view around a row's content: the padding and gap, the
144
+ * variant's height, the taller item its lines make it, and the selected
145
+ * container. Which of the lists page's three items a row is comes from its
146
+ * content rather than a prop, so a row holding all three lines already says
147
+ * it is the three-line item. A line that wraps grows the shorter item and
148
+ * keeps its slots centred.
149
+ */
150
+ function rowStyleOf(theme, { isSelected = false, overline, supporting, variant = "list" }) {
151
+ const own = rowStylesOf(theme);
152
+ const lines = (hasContent(overline) ? 1 : 0) + (hasContent(supporting) ? 1 : 0);
153
+ return composeStyles(own.base, own[variant], lines === 1 ? own.twoLine : void 0, lines === 2 ? own.threeLine : void 0, isSelected ? own[variant === "menu" ? "selectedMenu" : "selectedList"] : void 0);
154
+ }
155
+ //#endregion
156
+ export { RowContent, hasContent, rowContentColor, rowStyleOf, rowStyles };
157
+
158
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["ReactNode","StyleProp","TextStyle","ViewStyle","useMemo","Text","NativeText","View","IconStyle","ResolvedTheme","RowVariant","IconStyleContext","useTheme","composeStyles","useTypeRole","rowContentColor","rowStyles","rowStylesOf","ICON_SIZE","RowContentProps","children","isDisabled","isSelected","leading","overline","supporting","trailing","variant","hasContent","node","undefined","RowContent","t0","$","_c","t1","t2","t3","theme","own","headlineRole","overlineRole","supportingRole","t4","color","t5","size","icons","tone","muted","T0","t6","slot","T1","t7","main","t8","t9","t10","t11","t12","t13","t14","rowStyleOf","Pick","lines","base","twoLine","threeLine","_c","NativeText"],"sources":["../../src/row/index.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native'\n\nimport { useMemo } from 'react'\nimport { Text as NativeText, View } from 'react-native'\n\nimport type { IconStyle } from '../glyphs/icon-style'\nimport type { ResolvedTheme } from '../theme/scope'\nimport type { RowVariant } from './styles'\n\nimport { IconStyleContext } from '../glyphs/icon-style'\nimport { useTheme } from '../theme/scope'\nimport { composeStyles } from '../theme/styles'\nimport { useTypeRole } from '../typography/roles'\nimport { rowContentColor, rowStyles, rowStylesOf } from './styles'\n\n// The size an icon in either slot is drawn at, the lists and menus pages'.\nconst ICON_SIZE = 24\n\ntype RowContentProps = {\n /** The row's headline, the one thing it is mostly about. */\n readonly children?: ReactNode\n /**\n * Whether the row is disabled. Its headline, slots and lines take the\n * faded on-surface colour, the supporting line included.\n * @default false\n */\n readonly isDisabled?: boolean\n /**\n * Whether the row is selected. Its headline, slots and lines take the\n * selected container's own on-colour, since the muted role is not\n * guaranteed to be readable over it.\n * @default false\n */\n readonly isSelected?: boolean\n /** Content before the headline: an avatar, an icon, a checkbox. */\n readonly leading?: ReactNode\n /** A line above the headline, in label small and the muted role. */\n readonly overline?: ReactNode\n /** A second line under the headline, in body medium and the muted role. */\n readonly supporting?: ReactNode\n /** Content after the headline, such as an amount or a control. */\n readonly trailing?: ReactNode\n /**\n * Which spec page's row this is. A list item's headline is body large and\n * a menu item's label large.\n * @default 'list'\n */\n readonly variant?: RowVariant\n}\n\n/** Whether a slot was given something to draw. */\nfunction hasContent(node: ReactNode): boolean {\n return node !== undefined && node !== null && node !== false && node !== ''\n}\n\n/**\n * The inside of a row: the leading slot, the headline with the lines beside\n * it, and the trailing slot, each drawn only when given. The view around it\n * is the caller's, ListItem's or a menu's item, and takes `rowStyleOf`; this\n * is what each of them puts inside it, so a list and a menu draw the same\n * row. Both slots hand an icon the row's content colour at 24dp, through\n * `useIconStyle()`. Not a component of the library's own and not exported.\n */\nfunction RowContent({\n children,\n isDisabled = false,\n isSelected = false,\n leading,\n overline,\n supporting,\n trailing,\n variant = 'list',\n}: RowContentProps) {\n const theme = useTheme()\n const own = rowStylesOf(theme)\n const headlineRole = useTypeRole(\n variant === 'menu' ? 'labelLarge' : 'bodyLarge',\n )\n const overlineRole = useTypeRole('labelSmall')\n const supportingRole = useTypeRole('bodyMedium')\n const color = rowContentColor(theme, { isDisabled, isSelected, variant })\n const icons = useMemo<IconStyle>(() => ({ color, size: ICON_SIZE }), [color])\n // The muted role on the surface; the row's own colour over a selected\n // container, and on a disabled row, which fades the lines with the rest.\n const tone = isDisabled || isSelected ? { color } : own.muted\n\n return (\n <IconStyleContext value={icons}>\n {hasContent(leading) ? (\n <View style={rowStyles.slot}>{leading}</View>\n ) : null}\n <View style={own.main}>\n {hasContent(overline) ? (\n <NativeText style={composeStyles<TextStyle>(overlineRole, tone)}>\n {overline}\n </NativeText>\n ) : null}\n <NativeText style={composeStyles<TextStyle>(headlineRole, { color })}>\n {children}\n </NativeText>\n {hasContent(supporting) ? (\n <NativeText style={composeStyles<TextStyle>(supportingRole, tone)}>\n {supporting}\n </NativeText>\n ) : null}\n </View>\n {hasContent(trailing) ? (\n <View style={rowStyles.slot}>{trailing}</View>\n ) : null}\n </IconStyleContext>\n )\n}\n\n/**\n * The style of the view around a row's content: the padding and gap, the\n * variant's height, the taller item its lines make it, and the selected\n * container. Which of the lists page's three items a row is comes from its\n * content rather than a prop, so a row holding all three lines already says\n * it is the three-line item. A line that wraps grows the shorter item and\n * keeps its slots centred.\n */\nfunction rowStyleOf(\n theme: ResolvedTheme,\n {\n isSelected = false,\n overline,\n supporting,\n variant = 'list',\n }: Pick<\n RowContentProps,\n 'isSelected' | 'overline' | 'supporting' | 'variant'\n >,\n): StyleProp<ViewStyle> {\n const own = rowStylesOf(theme)\n const lines =\n (hasContent(overline) ? 1 : 0) + (hasContent(supporting) ? 1 : 0)\n\n return composeStyles<ViewStyle>(\n own.base,\n own[variant],\n lines === 1 ? own.twoLine : undefined,\n lines === 2 ? own.threeLine : undefined,\n isSelected\n ? own[variant === 'menu' ? 'selectedMenu' : 'selectedList']\n : undefined,\n )\n}\n\nexport type { RowContentProps, RowVariant }\n\nexport { hasContent, RowContent, rowContentColor, rowStyleOf, rowStyles }\n"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { themedStyles } from "../theme/styles.js";
2
+ import { withOpacity } from "../theme/color.js";
3
+ import { StyleSheet } from "react-native";
4
+ //#region src/row/styles.ts
5
+ const rowStylesOf = themedStyles(({ colors, sizing, spacing }) => StyleSheet.create({
6
+ base: {
7
+ alignItems: "center",
8
+ flexDirection: "row",
9
+ gap: spacing.lg,
10
+ paddingHorizontal: spacing.lg,
11
+ paddingVertical: spacing.md
12
+ },
13
+ list: { minHeight: sizing.rowSm },
14
+ main: {
15
+ flexGrow: 1,
16
+ flexShrink: 1,
17
+ gap: spacing.xxs,
18
+ minWidth: 0
19
+ },
20
+ menu: { minHeight: sizing.rowXs },
21
+ muted: { color: colors.onSurfaceVariant },
22
+ selectedList: { backgroundColor: colors.primaryContainer },
23
+ selectedMenu: { backgroundColor: colors.tertiaryContainer },
24
+ threeLine: {
25
+ alignItems: "flex-start",
26
+ minHeight: sizing.rowLg
27
+ },
28
+ twoLine: { minHeight: sizing.rowMd }
29
+ }));
30
+ const rowStyles = StyleSheet.create({
31
+ ring: { outlineOffset: -2 },
32
+ slot: {
33
+ alignItems: "center",
34
+ flexShrink: 0,
35
+ justifyContent: "center"
36
+ }
37
+ });
38
+ /**
39
+ * The colour a row's headline, slots and feedback are drawn in: on surface,
40
+ * the selected container's own on-colour, or on surface faded to the
41
+ * disabled content opacity, which wins over the selected one.
42
+ */
43
+ function rowContentColor({ colors, stateLayerOpacity }, { isDisabled = false, isSelected = false, variant = "list" }) {
44
+ if (isDisabled) return withOpacity(colors.onSurface, stateLayerOpacity.disabledContent);
45
+ if (isSelected) return variant === "menu" ? colors.onTertiaryContainer : colors.onPrimaryContainer;
46
+ return colors.onSurface;
47
+ }
48
+ //#endregion
49
+ export { rowContentColor, rowStyles, rowStylesOf };
50
+
51
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","names":["StyleSheet","ResolvedTheme","withOpacity","themedStyles","RowVariant","rowStylesOf","colors","sizing","spacing","create","base","alignItems","flexDirection","gap","lg","paddingHorizontal","paddingVertical","md","list","minHeight","rowSm","main","flexGrow","flexShrink","xxs","minWidth","menu","rowXs","muted","color","onSurfaceVariant","selectedList","backgroundColor","primaryContainer","selectedMenu","tertiaryContainer","threeLine","rowLg","twoLine","rowMd","rowStyles","ring","outlineOffset","slot","justifyContent","rowContentColor","stateLayerOpacity","isDisabled","isSelected","variant","onSurface","disabledContent","onTertiaryContainer","onPrimaryContainer"],"sources":["../../src/row/styles.ts"],"sourcesContent":["import { StyleSheet } from 'react-native'\n\nimport type { ResolvedTheme } from '../theme/scope'\n\nimport { withOpacity } from '../theme/color'\nimport { themedStyles } from '../theme/styles'\n\n// The row every list and menu item draws, as web's src/row draws it: the\n// lists page's 16dp inline and 12dp block padding, 16dp between the slots\n// and the text, and a height per spec page and line count. Each height is a\n// `minHeight`, since the lines inside are set in type roles whose sizes are\n// tokens, and a larger OS text size grows the row rather than clipping it.\n\n/** Which spec page's row this is: the lists page's or the menus page's. */\ntype RowVariant = 'list' | 'menu'\n\nconst rowStylesOf = themedStyles(({ colors, sizing, spacing }: ResolvedTheme) =>\n StyleSheet.create({\n base: {\n alignItems: 'center',\n flexDirection: 'row',\n gap: spacing.lg,\n paddingHorizontal: spacing.lg,\n paddingVertical: spacing.md,\n },\n // The lists page's one-line item.\n list: {\n minHeight: sizing.rowSm,\n },\n // The headline and the lines beside it take what the slots leave and\n // wrap past it, so a long one never runs under the trailing slot.\n // `minWidth: 0` is for React Native Web, where a text in a row keeps\n // CSS's automatic minimum, the width of its longest word.\n main: {\n flexGrow: 1,\n flexShrink: 1,\n gap: spacing.xxs,\n minWidth: 0,\n },\n // The menus page's item.\n menu: {\n minHeight: sizing.rowXs,\n },\n muted: {\n color: colors.onSurfaceVariant,\n },\n selectedList: {\n backgroundColor: colors.primaryContainer,\n },\n selectedMenu: {\n backgroundColor: colors.tertiaryContainer,\n },\n // The three-line item, the only one whose slots move: they are held\n // level with the overline rather than centred on the three lines.\n threeLine: {\n alignItems: 'flex-start',\n minHeight: sizing.rowLg,\n },\n twoLine: {\n minHeight: sizing.rowMd,\n },\n }),\n)\n\nconst rowStyles = StyleSheet.create({\n // The focus ring, drawn inside the row as web's is, so a container that\n // clips its rows, such as a menu's surface, leaves the whole ring showing.\n ring: {\n outlineOffset: -2,\n },\n slot: {\n alignItems: 'center',\n flexShrink: 0,\n justifyContent: 'center',\n },\n})\n\n/**\n * The colour a row's headline, slots and feedback are drawn in: on surface,\n * the selected container's own on-colour, or on surface faded to the\n * disabled content opacity, which wins over the selected one.\n */\nfunction rowContentColor(\n { colors, stateLayerOpacity }: ResolvedTheme,\n {\n isDisabled = false,\n isSelected = false,\n variant = 'list',\n }: {\n readonly isDisabled?: boolean\n readonly isSelected?: boolean\n readonly variant?: RowVariant\n },\n): string {\n if (isDisabled) {\n return withOpacity(colors.onSurface, stateLayerOpacity.disabledContent)\n }\n if (isSelected) {\n return variant === 'menu'\n ? colors.onTertiaryContainer\n : colors.onPrimaryContainer\n }\n return colors.onSurface\n}\n\nexport type { RowVariant }\n\nexport { rowContentColor, rowStyles, rowStylesOf }\n"],"mappings":";;;;AAgBA,MAAMK,cAAcF,cAAc,EAAEG,QAAQC,QAAQC,cAClDR,WAAWS,OAAO;CAChBC,MAAM;EACJC,YAAY;EACZC,eAAe;EACfC,KAAKL,QAAQM;EACbC,mBAAmBP,QAAQM;EAC3BE,iBAAiBR,QAAQS;CAC3B;CAEAC,MAAM,EACJC,WAAWZ,OAAOa,MACpB;CAKAC,MAAM;EACJC,UAAU;EACVC,YAAY;EACZV,KAAKL,QAAQgB;EACbC,UAAU;CACZ;CAEAC,MAAM,EACJP,WAAWZ,OAAOoB,MACpB;CACAC,OAAO,EACLC,OAAOvB,OAAOwB,iBAChB;CACAC,cAAc,EACZC,iBAAiB1B,OAAO2B,iBAC1B;CACAC,cAAc,EACZF,iBAAiB1B,OAAO6B,kBAC1B;CAGAC,WAAW;EACTzB,YAAY;EACZQ,WAAWZ,OAAO8B;CACpB;CACAC,SAAS,EACPnB,WAAWZ,OAAOgC,MACpB;AACF,CAAC,CACH;AAEA,MAAMC,YAAYxC,WAAWS,OAAO;CAGlCgC,MAAM,EACJC,eAAe,GACjB;CACAC,MAAM;EACJhC,YAAY;EACZY,YAAY;EACZqB,gBAAgB;CAClB;AACF,CAAC;;;;;;AAOD,SAASC,gBACP,EAAEvC,QAAQwC,qBACV,EACEC,aAAa,OACbC,aAAa,OACbC,UAAU,UAMJ;CACR,IAAIF,YACF,OAAO7C,YAAYI,OAAO4C,WAAWJ,kBAAkBK,eAAe;CAExE,IAAIH,YACF,OAAOC,YAAY,SACf3C,OAAO8C,sBACP9C,OAAO+C;CAEb,OAAO/C,OAAO4C;AAChB"}
@@ -1,8 +1,8 @@
1
1
  import { dark, light } from "../tokens/defaults.js";
2
2
  import { resolveTheme } from "./resolve.js";
3
3
  import { c } from "react/compiler-runtime";
4
- import { useColorScheme } from "react-native";
5
4
  import { createContext, use } from "react";
5
+ import { useColorScheme } from "react-native";
6
6
  //#region src/theme/scope.ts
7
7
  const DEFAULTS = {
8
8
  dark,
@@ -1,6 +1,6 @@
1
1
  import { KuiTheme } from "./tokens/defaults.js";
2
- import { View, ViewProps } from "react-native";
3
2
  import { ComponentRef, Ref } from "react";
3
+ import { View, ViewProps } from "react-native";
4
4
  //#region src/theme-scope.d.ts
5
5
  type ThemeScopeProps = ViewProps & {
6
6
  readonly ref?: Ref<ComponentRef<typeof View>>;
@@ -2,9 +2,9 @@ import { ScopeContext, merged, scopeFor, useScope } from "./theme/scope.js";
2
2
  import { themedStyles } from "./theme/styles.js";
3
3
  import { OverlayHost, OverlayHostContext } from "./overlay/host.js";
4
4
  import { c } from "react/compiler-runtime";
5
- import { StyleSheet, View } from "react-native";
6
5
  import { use } from "react";
7
6
  import { jsx } from "react/jsx-runtime";
7
+ import { StyleSheet, View } from "react-native";
8
8
  //#region src/theme-scope.tsx
9
9
  const stylesOf = themedStyles(({ colors }) => StyleSheet.create({ pinned: { backgroundColor: colors.surface } }));
10
10
  /**
@@ -1,6 +1,6 @@
1
1
  import { c } from "react/compiler-runtime";
2
- import { Platform } from "react-native";
3
2
  import { useSyncExternalStore } from "react";
3
+ import { Platform } from "react-native";
4
4
  //#region src/typography/fonts.ts
5
5
  let registry;
6
6
  let loading;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanso-labs/react-native-kanso-ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Kanso UI's Material Design components for React Native, themed through the same --kui-* design tokens as the web library",
5
5
  "keywords": [
6
6
  "react-native",
@@ -61,8 +61,8 @@
61
61
  "@commitlint/config-conventional": "21.2.3",
62
62
  "@eslint/js": "10.0.1",
63
63
  "@jest/globals": "29.7.0",
64
- "@kanso-labs/kanso-ui": "0.43.4",
65
- "@kanso-labs/unplugin-style-dictionary": "0.10.32",
64
+ "@kanso-labs/kanso-ui": "0.43.5",
65
+ "@kanso-labs/unplugin-style-dictionary": "0.10.33",
66
66
  "@react-native/babel-preset": "0.88.0",
67
67
  "@react-native/jest-preset": "0.88.0",
68
68
  "@rolldown/plugin-babel": "0.2.4",