@kanso-labs/react-native-kanso-ui 0.5.0 → 0.6.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 (112) hide show
  1. package/README.md +4 -1
  2. package/dist/badge/index.js +74 -0
  3. package/dist/badge/index.js.map +1 -0
  4. package/dist/badge/styles.js +51 -0
  5. package/dist/badge/styles.js.map +1 -0
  6. package/dist/button/index.d.ts +1 -1
  7. package/dist/button/index.js +1 -1
  8. package/dist/button/pending.js +1 -1
  9. package/dist/button/slots.d.ts +2 -2
  10. package/dist/chip/index.js +2 -2
  11. package/dist/components/app-bar/index.d.ts +111 -0
  12. package/dist/components/app-bar/index.js +341 -0
  13. package/dist/components/app-bar/index.js.map +1 -0
  14. package/dist/components/avatar/index.d.ts +1 -1
  15. package/dist/components/button/index.d.ts +1 -1
  16. package/dist/components/button/index.js +2 -2
  17. package/dist/components/card/index.d.ts +1 -1
  18. package/dist/components/card/index.js +3 -3
  19. package/dist/components/checkbox/index.d.ts +1 -1
  20. package/dist/components/checkbox/index.js +1 -1
  21. package/dist/components/chip/index.d.ts +1 -1
  22. package/dist/components/chip/index.js +1 -1
  23. package/dist/components/chip-group/index.d.ts +1 -1
  24. package/dist/components/chip-group/index.js +4 -4
  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 +2 -2
  31. package/dist/components/icon-button/index.d.ts +1 -1
  32. package/dist/components/icon-button/index.js +2 -2
  33. package/dist/components/link/index.d.ts +1 -1
  34. package/dist/components/link/index.js +3 -3
  35. package/dist/components/list/index.d.ts +1 -1
  36. package/dist/components/list/index.js +5 -5
  37. package/dist/components/list-detail/index.d.ts +56 -0
  38. package/dist/components/list-detail/index.js +169 -0
  39. package/dist/components/list-detail/index.js.map +1 -0
  40. package/dist/components/list-item/index.d.ts +1 -1
  41. package/dist/components/list-item/index.js +1 -1
  42. package/dist/components/menu/index.d.ts +1 -1
  43. package/dist/components/menu/index.js +6 -6
  44. package/dist/components/meter/index.d.ts +1 -1
  45. package/dist/components/meter/index.js +1 -1
  46. package/dist/components/navigation-bar/index.d.ts +170 -0
  47. package/dist/components/navigation-bar/index.js +494 -0
  48. package/dist/components/navigation-bar/index.js.map +1 -0
  49. package/dist/components/number-field/index.d.ts +1 -1
  50. package/dist/components/number-field/index.js +5 -5
  51. package/dist/components/popover/index.d.ts +1 -1
  52. package/dist/components/popover/index.js +2 -2
  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 +3 -3
  56. package/dist/components/radio-group/index.d.ts +1 -1
  57. package/dist/components/radio-group/index.js +3 -3
  58. package/dist/components/search-field/index.d.ts +1 -1
  59. package/dist/components/search-field/index.js +5 -5
  60. package/dist/components/segmented-button/index.d.ts +1 -1
  61. package/dist/components/segmented-button/index.js +5 -5
  62. package/dist/components/separator/index.d.ts +1 -1
  63. package/dist/components/separator/index.js +1 -1
  64. package/dist/components/sheet/index.d.ts +1 -1
  65. package/dist/components/sheet/index.js +3 -3
  66. package/dist/components/snackbar/index.d.ts +1 -1
  67. package/dist/components/snackbar/index.js +4 -4
  68. package/dist/components/stack/index.d.ts +1 -1
  69. package/dist/components/stack/index.js +1 -1
  70. package/dist/components/switch/index.d.ts +1 -1
  71. package/dist/components/switch/index.js +4 -4
  72. package/dist/components/text/index.d.ts +1 -1
  73. package/dist/components/text/index.js +1 -1
  74. package/dist/components/text-field/index.d.ts +1 -1
  75. package/dist/components/text-field/index.js +1 -1
  76. package/dist/control/checkbox-box.js +2 -2
  77. package/dist/control/index.js +1 -1
  78. package/dist/field/index.d.ts +1 -1
  79. package/dist/field/index.js +4 -4
  80. package/dist/form/index.d.ts +1 -1
  81. package/dist/glyphs/index.js +2 -2
  82. package/dist/hooks/useAppBarScroll.d.ts +56 -0
  83. package/dist/hooks/useAppBarScroll.js +87 -0
  84. package/dist/hooks/useAppBarScroll.js.map +1 -0
  85. package/dist/index.d.ts +7 -3
  86. package/dist/index.js +7 -3
  87. package/dist/indicator/index.d.ts +2 -2
  88. package/dist/indicator/index.js +3 -3
  89. package/dist/indicator/motion.js +1 -1
  90. package/dist/mark/index.d.ts +1 -1
  91. package/dist/mark/index.js +2 -2
  92. package/dist/modal/index.d.ts +1 -1
  93. package/dist/modal/index.js +2 -2
  94. package/dist/navigation/index.d.ts +29 -0
  95. package/dist/navigation/index.js +405 -0
  96. package/dist/navigation/index.js.map +1 -0
  97. package/dist/navigation/styles.js +61 -0
  98. package/dist/navigation/styles.js.map +1 -0
  99. package/dist/overlay/anchored.d.ts +2 -2
  100. package/dist/overlay/anchored.js +1 -1
  101. package/dist/overlay/host.d.ts +2 -2
  102. package/dist/overlay/host.js +1 -1
  103. package/dist/overlay/panel.js +2 -2
  104. package/dist/press/index.js +75 -69
  105. package/dist/press/index.js.map +1 -1
  106. package/dist/router/index.js +1 -1
  107. package/dist/row/index.js +1 -1
  108. package/dist/theme/scope.js +1 -1
  109. package/dist/theme-scope.d.ts +1 -1
  110. package/dist/theme-scope.js +1 -1
  111. package/dist/typography/fonts.js +1 -1
  112. package/package.json +2 -2
package/README.md CHANGED
@@ -94,7 +94,10 @@ same.
94
94
 
95
95
  The insets come from safe-area-context's `SafeAreaProvider`, which Expo Router
96
96
  puts around an app already; elsewhere, wrap the app in one. Without it, the
97
- library keeps to the window's edge.
97
+ library keeps to the window's edge. `AppBar` and the panes of a `ListDetail` are
98
+ the exception: each measures its own insets through safe-area-context's
99
+ `SafeAreaView`, so it clears only the edges it actually meets, and on a device
100
+ it needs no provider. Under React Native Web that view reads the provider too.
98
101
 
99
102
  Gesture Handler 2 and 3 both satisfy the peer. A `Switch` draws Gesture
100
103
  Handler's root view around its own track, so an app need not wrap itself in
@@ -0,0 +1,74 @@
1
+ import { useTheme } from "../theme/scope.js";
2
+ import { composeStyles } from "../theme/styles.js";
3
+ import { useTypeRole } from "../typography/roles.js";
4
+ import { styles, stylesOf } from "./styles.js";
5
+ import { c } from "react/compiler-runtime";
6
+ import { Text, View } from "react-native";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/badge/index.tsx
9
+ let countFormat;
10
+ /**
11
+ * A badge on the top trailing corner of the icon it wraps: the small dot, or
12
+ * with `count` the large badge holding the number.
13
+ */
14
+ function Badge(t0) {
15
+ const $ = c(12);
16
+ const { children, count, inFlow: t1, max: t2 } = t0;
17
+ const inFlow = t1 === void 0 ? false : t1;
18
+ const max = t2 === void 0 ? 999 : t2;
19
+ const own = stylesOf(useTheme());
20
+ const role = useTypeRole("labelSmall");
21
+ let t3;
22
+ if ($[0] !== count || $[1] !== max) {
23
+ t3 = count === void 0 ? void 0 : countOf(count, max);
24
+ $[0] = count;
25
+ $[1] = max;
26
+ $[2] = t3;
27
+ } else t3 = $[2];
28
+ const label = t3;
29
+ const T0 = View;
30
+ const t4 = styles;
31
+ const T1 = View;
32
+ const t5 = true;
33
+ const t6 = composeStyles(own.mark, label === void 0 ? styles.small : [own.large, inFlow ? styles.inFlow : styles.over]);
34
+ const t7 = label === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, {
35
+ style: styles.space,
36
+ children: " "
37
+ }), /* @__PURE__ */ jsx(Text, {
38
+ style: composeStyles(role, own.count),
39
+ children: label
40
+ })] });
41
+ let t8;
42
+ if ($[3] !== T1 || $[4] !== t6 || $[5] !== t7) {
43
+ t8 = /* @__PURE__ */ jsx(T1, {
44
+ "aria-hidden": t5,
45
+ style: t6,
46
+ children: t7
47
+ });
48
+ $[3] = T1;
49
+ $[4] = t6;
50
+ $[5] = t7;
51
+ $[6] = t8;
52
+ } else t8 = $[6];
53
+ let t9;
54
+ if ($[7] !== T0 || $[8] !== children || $[9] !== t4.anchor || $[10] !== t8) {
55
+ t9 = /* @__PURE__ */ jsxs(T0, {
56
+ style: t4.anchor,
57
+ children: [children, t8]
58
+ });
59
+ $[7] = T0;
60
+ $[8] = children;
61
+ $[9] = t4.anchor;
62
+ $[10] = t8;
63
+ $[11] = t9;
64
+ } else t9 = $[11];
65
+ return t9;
66
+ }
67
+ function countOf(count, max) {
68
+ countFormat ??= new Intl.NumberFormat();
69
+ return count > max ? `${countFormat.format(max)}+` : countFormat.format(count);
70
+ }
71
+ //#endregion
72
+ export { Badge };
73
+
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["ReactNode","TextStyle","ViewStyle","Text","NativeText","View","useTheme","composeStyles","useTypeRole","styles","stylesOf","BadgeProps","children","count","inFlow","max","countFormat","Intl","NumberFormat","Badge","t0","$","_c","t1","t2","undefined","own","role","t3","countOf","label","T0","T1","t5","t6","mark","small","large","over","t7","space","t8","t9","t4","anchor","format","_c","NativeText"],"sources":["../../src/badge/index.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { TextStyle, ViewStyle } from 'react-native'\n\nimport { Text as NativeText, View } from 'react-native'\n\nimport { useTheme } from '../theme/scope'\nimport { composeStyles } from '../theme/styles'\nimport { useTypeRole } from '../typography/roles'\nimport { styles, stylesOf } from './styles'\n\n// The badges page's badge, as kanso-ui's Badge draws it on a navigation\n// destination: a mark on the corner of an icon that flags something new\n// there, either as the 6dp small badge or as the 16dp large badge holding a\n// count. Both are the error pair, the error role with the count in on error,\n// on a full corner, and the count is label small, capped with a plus, as\n// \"999+\".\n//\n// The badge wraps the icon and places the mark against the icon's box,\n// which is what the page measures from. The small badge fills the icon's top\n// trailing 6dp square. The large badge's bottom leading corner sits 14dp\n// down and 12dp in from the icon's top trailing corner, so it stands 2dp\n// above the icon and grows toward the end as its count lengthens. Both are\n// placed by `start` and `end`, so the mark stays on the trailing corner under\n// right-to-left.\n//\n// Where a label stands beside the icon rather than under it, the large badge\n// joins the flow after the icon instead, so the label starts after it rather\n// than under it: `inFlow`. The small badge stays inside the icon's box, so\n// nothing has to make room for it.\n//\n// The mark is hidden from a screen reader. A count read on its own is a\n// number with no subject; it belongs in the name of the control it sits on,\n// where a reader meets it with what it counts, \"First item, 3 new\".\n//\n// The count is set apart from the text before it by a space drawn out of the\n// flow. Under React Native Web the count is still text inside the control,\n// and with nothing between them a label and its count ran together as\n// \"Second item3\", a word in no name: what axe's label-in-name check fails,\n// and not what a speech-input user would say. Web draws the count as\n// generated content instead, which a style here has no counterpart for.\n\ntype BadgeProps = {\n /** What the badge is drawn on: an icon, at 24dp. */\n readonly children?: ReactNode\n /**\n * The number to show, which draws the large badge. Left out, the badge is\n * the small dot, which flags without counting.\n */\n readonly count?: number | undefined\n /**\n * Draws the large badge in the flow after the icon rather than over its\n * corner, for a label beside the icon.\n * @default false\n */\n readonly inFlow?: boolean\n /**\n * The largest count drawn as itself. Above it the badge reads `max` and a\n * plus, which is the page's 999+ at the default.\n * @default 999\n */\n readonly max?: number\n}\n\nlet countFormat: Intl.NumberFormat | undefined\n\n/**\n * A badge on the top trailing corner of the icon it wraps: the small dot, or\n * with `count` the large badge holding the number.\n */\nfunction Badge({ children, count, inFlow = false, max = 999 }: BadgeProps) {\n const own = stylesOf(useTheme())\n const role = useTypeRole('labelSmall')\n const label = count === undefined ? undefined : countOf(count, max)\n\n return (\n <View style={styles.anchor}>\n {children}\n <View\n aria-hidden\n style={composeStyles<ViewStyle>(\n own.mark,\n label === undefined\n ? styles.small\n : [own.large, inFlow ? styles.inFlow : styles.over],\n )}\n >\n {label === undefined ? null : (\n <>\n <NativeText style={styles.space}> </NativeText>\n <NativeText style={composeStyles<TextStyle>(role, own.count)}>\n {label}\n </NativeText>\n </>\n )}\n </View>\n </View>\n )\n}\n\n// The count in the device's digits and grouping, as web writes it in the\n// reader's locale. One formatter, since a badge's count changes often.\nfunction countOf(count: number, max: number) {\n countFormat ??= new Intl.NumberFormat()\n\n return count > max ? `${countFormat.format(max)}+` : countFormat.format(count)\n}\n\nexport type { BadgeProps }\n\nexport { Badge }\n"],"mappings":";;;;;;;;AA+DA,IAAIgB;;;;;AAMJ,SAAAG,MAAAC,IAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAe,MAAA,EAAAV,UAAAC,OAAAC,QAAAS,IAAAR,KAAAS,OAAAJ;CAAmB,MAAAN,SAAAS,OAAAE,KAAAA,IAAA,QAAAF;CAAgB,MAAAR,MAAAS,OAAAC,KAAAA,IAAA,MAAAD;CAChD,MAAAE,MAAYhB,SAASJ,SAAS,CAAC;CAC/B,MAAAqB,OAAanB,YAAY,YAAY;CAAC,IAAAoB;CAAA,IAAAP,EAAA,OAAAR,SAAAQ,EAAA,OAAAN,KAAA;EACxBa,KAAAf,UAAUY,KAAAA,IAAVA,KAAAA,IAAkCI,QAAQhB,OAAOE,GAAG;EAACM,EAAA,KAAAR;EAAAQ,EAAA,KAAAN;EAAAM,EAAA,KAAAO;CAAA,OAAAA,KAAAP,EAAA;CAAnE,MAAAS,QAAcF;CAGX,MAAAG,KAAA1B;CAAYI,MAAAA,KAAAA;CAEV,MAAAuB,KAAA3B;CACC,MAAA4B,KAAA;CACO,MAAAC,KAAA3B,cACLmB,IAAGS,MACHL,UAAUL,KAAAA,IACNhB,OAAM2B,QADV,CAEKV,IAAGW,OAAQvB,SAASL,OAAMK,SAAUL,OAAM6B,IAAK,CACtD;CAEC,MAAAC,KAAAT,UAAUL,KAAAA,IAAV,OAAA,qBAAA,UAAA,EAAA,UAAA,CAEG,oBAACsB,MAAD;EAAmB,OAAAtC,OAAM+B;EAAQ,UAAA;CAAtB,CAAA,GACX,oBAACO,MAAD;EAAmB,OAAAxC,cAAyBoB,MAAMD,IAAGb,KAAM;EACxDiB,UAAAA;CADQ,CAAA,CAEE,EAAA,CAAA;CAEhB,IAAAW;CAAA,IAAApB,EAAA,OAAAW,MAAAX,EAAA,OAAAa,MAAAb,EAAA,OAAAkB,IAAA;EAhBHE,KAAA,oBAAC,IAAD;GACE,eAAAR;GACO,OAAAC;GAONK,UAAAA;EATE,CAAA;EAiBElB,EAAA,KAAAW;EAAAX,EAAA,KAAAa;EAAAb,EAAA,KAAAkB;EAAAlB,EAAA,KAAAoB;CAAA,OAAAA,KAAApB,EAAA;CAAA,IAAAqB;CAAA,IAAArB,EAAA,OAAAU,MAAAV,EAAA,OAAAT,YAAAS,EAAA,OAAAsB,GAAAC,UAAAvB,EAAA,QAAAoB,IAAA;EAnBTC,KAAA,qBAAC,IAAD;GAAa,OAAAjC,GAAMmC;GAAnB,UAAA,CACGhC,UACD6B,EAFG;;EAoBEpB,EAAA,KAAAU;EAAAV,EAAA,KAAAT;EAAAS,EAAA,KAAAsB,GAAAC;EAAAvB,EAAA,MAAAoB;EAAApB,EAAA,MAAAqB;CAAA,OAAAA,KAAArB,EAAA;CAAA,OApBPqB;AAoBO;AAMX,SAASb,QAAQhB,OAAeE,KAAa;CAC3CC,gBAAgB,IAAIC,KAAKC,aAAa;CAEtC,OAAOL,QAAQE,MAAM,GAAGC,YAAY6B,OAAO9B,GAAG,EAAC,KAAMC,YAAY6B,OAAOhC,KAAK;AAC/E"}
@@ -0,0 +1,51 @@
1
+ import { themedStyles } from "../theme/styles.js";
2
+ import { StyleSheet } from "react-native";
3
+ //#region src/badge/styles.ts
4
+ const SMALL_SIZE = 6;
5
+ const LARGE_SIZE = 16;
6
+ const styles = StyleSheet.create({
7
+ anchor: {
8
+ alignItems: "flex-start",
9
+ flexDirection: "row"
10
+ },
11
+ inFlow: {
12
+ marginStart: -12,
13
+ position: "relative",
14
+ top: -2
15
+ },
16
+ over: {
17
+ position: "absolute",
18
+ start: 12,
19
+ top: -2
20
+ },
21
+ small: {
22
+ end: 0,
23
+ height: SMALL_SIZE,
24
+ position: "absolute",
25
+ top: 0,
26
+ width: SMALL_SIZE
27
+ },
28
+ space: { position: "absolute" }
29
+ });
30
+ const stylesOf = themedStyles(({ colors, radii, spacing }) => StyleSheet.create({
31
+ count: {
32
+ color: colors.onError,
33
+ fontVariant: ["tabular-nums"]
34
+ },
35
+ large: {
36
+ alignItems: "center",
37
+ justifyContent: "center",
38
+ minHeight: LARGE_SIZE,
39
+ minWidth: LARGE_SIZE,
40
+ paddingHorizontal: spacing.xs
41
+ },
42
+ mark: {
43
+ backgroundColor: colors.error,
44
+ borderRadius: radii.pill,
45
+ pointerEvents: "none"
46
+ }
47
+ }));
48
+ //#endregion
49
+ export { styles, stylesOf };
50
+
51
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","names":["StyleSheet","themedStyles","ICON_SIZE","OVERLAP","RISE","SMALL_SIZE","LARGE_SIZE","styles","create","anchor","alignItems","flexDirection","inFlow","marginStart","position","top","over","start","small","end","height","width","space","stylesOf","colors","radii","spacing","count","color","onError","fontVariant","large","justifyContent","minHeight","minWidth","paddingHorizontal","xs","mark","backgroundColor","error","borderRadius","pill","pointerEvents"],"sources":["../../src/badge/styles.ts"],"sourcesContent":["import { StyleSheet } from 'react-native'\n\nimport { themedStyles } from '../theme/styles'\n\n// The icon a badge is drawn on, which the large badge's corner is measured\n// from: the 24dp every destination draws.\nconst ICON_SIZE = 24\n\n// How far back over the icon the large badge reaches, from the icon's\n// trailing edge, and how far above the icon it stands. The page's own\n// figures: its bottom leading corner 14dp down and 12dp in from the icon's\n// top trailing corner, on a 16dp badge.\nconst OVERLAP = 12\nconst RISE = 2\n\n// The small badge's side, and the large badge's least height and width.\nconst SMALL_SIZE = 6\nconst LARGE_SIZE = 16\n\nconst styles = StyleSheet.create({\n // What the badge is drawn on. It shrinks to what it holds, so its box is\n // the icon's and the mark lands where the page puts it, and lays a mark\n // that joins the flow after the icon.\n anchor: {\n alignItems: 'flex-start',\n flexDirection: 'row',\n },\n // The large badge in the flow after the icon, 12dp back over it and 2dp\n // above it, which is where its own placement puts it, so what follows the\n // icon starts after the badge rather than under it.\n inFlow: {\n marginStart: -OVERLAP,\n position: 'relative',\n top: -RISE,\n },\n // The large badge over the icon's top trailing corner.\n over: {\n position: 'absolute',\n start: ICON_SIZE - OVERLAP,\n top: -RISE,\n },\n // The small badge, filling the icon's top trailing 6dp square.\n small: {\n end: 0,\n height: SMALL_SIZE,\n position: 'absolute',\n top: 0,\n width: SMALL_SIZE,\n },\n // The space before the count, out of the flow, so it sets the count apart\n // from the text before it without moving anything.\n space: {\n position: 'absolute',\n },\n})\n\nconst stylesOf = themedStyles(({ colors, radii, spacing }) =>\n StyleSheet.create({\n // The count, in label small with tabular figures, so a count that\n // changes does not change the badge's width digit by digit.\n count: {\n color: colors.onError,\n fontVariant: ['tabular-nums'],\n },\n // The page's large badge: at least 16dp tall and as wide, 4dp either\n // side of its count. A minimum rather than a size, so a larger text size\n // the device asks for grows the badge rather than cutting the count.\n large: {\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: LARGE_SIZE,\n minWidth: LARGE_SIZE,\n paddingHorizontal: spacing.xs,\n },\n // The fill and corner both badges share. It takes no touches, so a press\n // on it lands on what it is drawn on.\n mark: {\n backgroundColor: colors.error,\n borderRadius: radii.pill,\n pointerEvents: 'none',\n },\n }),\n)\n\nexport { styles, stylesOf }\n"],"mappings":";;;AAgBA,MAAMK,aAAa;AACnB,MAAMC,aAAa;AAEnB,MAAMC,SAASP,WAAWQ,OAAO;CAI/BC,QAAQ;EACNC,YAAY;EACZC,eAAe;CACjB;CAIAC,QAAQ;EACNC,aAAa;EACbC,UAAU;EACVC,KAAK;CACP;CAEAC,MAAM;EACJF,UAAU;EACVG,OAAOf;EACPa,KAAK;CACP;CAEAG,OAAO;EACLC,KAAK;EACLC,QAAQf;EACRS,UAAU;EACVC,KAAK;EACLM,OAAOhB;CACT;CAGAiB,OAAO,EACLR,UAAU,WACZ;AACF,CAAC;AAED,MAAMS,WAAWtB,cAAc,EAAEuB,QAAQC,OAAOC,cAC9C1B,WAAWQ,OAAO;CAGhBmB,OAAO;EACLC,OAAOJ,OAAOK;EACdC,aAAa,CAAC,cAAc;CAC9B;CAIAC,OAAO;EACLrB,YAAY;EACZsB,gBAAgB;EAChBC,WAAW3B;EACX4B,UAAU5B;EACV6B,mBAAmBT,QAAQU;CAC7B;CAGAC,MAAM;EACJC,iBAAiBd,OAAOe;EACxBC,cAAcf,MAAMgB;EACpBC,eAAe;CACjB;AACF,CAAC,CACH"}
@@ -1,5 +1,5 @@
1
+ import { HostInstance, LayoutChangeEvent, PressableProps } from "react-native";
1
2
  import { Ref } from "react";
2
- import { HostInstance, PressableProps } from "react-native";
3
3
  //#region src/button/index.d.ts
4
4
  /**
5
5
  * A plain button, a link, or a toggle, of which a call site writes exactly
@@ -4,9 +4,9 @@ import { refsOf } from "../ref/index.js";
4
4
  import { useOpenLink } from "../router/index.js";
5
5
  import { ButtonSlotContext } from "./slots.js";
6
6
  import { c } from "react/compiler-runtime";
7
- import { use, useState } from "react";
8
7
  import { Platform, Pressable, StyleSheet } from "react-native";
9
8
  import Animated, { Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from "react-native-reanimated";
9
+ import { use, useState } from "react";
10
10
  //#region src/button/index.ts
11
11
  const HEIGHTS = {
12
12
  lg: "controlLg",
@@ -2,9 +2,9 @@ import { IconStyleContext } from "../glyphs/icon-style.js";
2
2
  import { ProgressIndicator } from "../components/progress-indicator/index.js";
3
3
  import { messagesFor } from "../i18n/index.js";
4
4
  import { c } from "react/compiler-runtime";
5
+ import { AccessibilityInfo, Platform, StyleSheet, View } from "react-native";
5
6
  import { useEffect, useRef } from "react";
6
7
  import { jsx } from "react/jsx-runtime";
7
- import { AccessibilityInfo, Platform, StyleSheet, View } from "react-native";
8
8
  //#region src/button/pending.tsx
9
9
  const pendingStyles = StyleSheet.create({
10
10
  hidden: { opacity: 0 },
@@ -1,2 +1,2 @@
1
- import { RefObject } from "react";
2
- import { HostInstance } from "react-native";
1
+ import { HostInstance } from "react-native";
2
+ import { RefObject } from "react";
@@ -1,14 +1,14 @@
1
1
  import { IconStyleContext } from "../glyphs/icon-style.js";
2
- import { CheckGlyph } from "../glyphs/index.js";
3
2
  import { useTheme } from "../theme/scope.js";
4
3
  import { composeStyles } from "../theme/styles.js";
5
4
  import { useTypeRole } from "../typography/roles.js";
6
5
  import { withOpacity } from "../theme/color.js";
6
+ import { CheckGlyph } from "../glyphs/index.js";
7
7
  import { styles, stylesOf } from "./styles.js";
8
8
  import { c } from "react/compiler-runtime";
9
- import { jsx } from "react/jsx-runtime";
10
9
  import { View } from "react-native";
11
10
  import Animated, { Easing, useAnimatedStyle, useReducedMotion, withTiming } from "react-native-reanimated";
11
+ import { jsx } from "react/jsx-runtime";
12
12
  //#region src/chip/index.tsx
13
13
  const CHECK = /* @__PURE__ */ jsx(CheckGlyph, {});
14
14
  /**
@@ -0,0 +1,111 @@
1
+ import { View, ViewProps } from "react-native";
2
+ import { ComponentRef, ReactNode, Ref } from "react";
3
+ //#region src/components/app-bar/index.d.ts
4
+ type AppBarProps = Omit<ViewProps, 'children'> & {
5
+ /**
6
+ * Which of Material Design's alignments the text takes. `center` is the
7
+ * configuration that replaced the old center-aligned variant.
8
+ * @default 'start'
9
+ */
10
+ readonly align?: 'center' | 'start';
11
+ /**
12
+ * Collapses a flexible bar to the height and headline of the small one,
13
+ * for a `pinned` bar over content that has been scrolled. Without it a
14
+ * pinned `lg` keeps 152dp of the screen for as long as the screen is open.
15
+ *
16
+ * Controlled, as `scrolled` is: only the app knows which view scrolls and
17
+ * how far into it the bar should have collapsed. `useAppBarScroll` derives
18
+ * both from that view's scroll, and is the way to set them unless the app
19
+ * needs a rule of its own. Ignored on `sm`, which is already the height
20
+ * this collapses to, and the subtitle goes with the height.
21
+ * @default false
22
+ */
23
+ readonly collapsed?: boolean;
24
+ /**
25
+ * Where the bar's content starts on each edge, in dp: the headline where
26
+ * nothing comes before it, and the leading icon's glyph where something
27
+ * does. The trailing edge is the same mirrored. `0` draws a bar flush.
28
+ *
29
+ * The default is Material Design's margin for a top app bar, the spacing
30
+ * scale's `lg` step, which is also the margin it gives body content, so a
31
+ * bar and the screen beneath line up without either being told about the
32
+ * other.
33
+ * @default spacing.lg
34
+ */
35
+ readonly contentInset?: number;
36
+ /**
37
+ * How wide the bar's contents may run, in dp, before they stop growing,
38
+ * with the row centred in what is left. The bar's surface still paints
39
+ * edge to edge. Unset, the row is as wide as the bar.
40
+ */
41
+ readonly contentMaxInlineSize?: number;
42
+ /**
43
+ * The screen's title, a header for a screen reader. Wraps rather than
44
+ * truncating, and grows the bar with it.
45
+ */
46
+ readonly headline?: ReactNode;
47
+ /**
48
+ * Usually a back or menu icon button. Sits before the text, and hands an
49
+ * icon the on-surface colour and a 24dp size through `useIconStyle()`.
50
+ */
51
+ readonly leading?: ReactNode;
52
+ /**
53
+ * Marks a bar drawn above the screen's scroll view rather than inside it,
54
+ * so it stays put while the content scrolls beneath its lower edge, and is
55
+ * drawn over that content. A pinned bar is the one `collapsed` and
56
+ * `scrolled` are for.
57
+ * @default false
58
+ */
59
+ readonly pinned?: boolean;
60
+ readonly ref?: Ref<ComponentRef<typeof View>>;
61
+ /**
62
+ * Whether the content beneath has been scrolled. Material Design separates
63
+ * a bar from scrolled content with a fill colour rather than a shadow, the
64
+ * surface container role, and this is what applies it.
65
+ *
66
+ * Controlled, with no listener of its own: only the app knows which view
67
+ * scrolls. `useAppBarScroll` derives it.
68
+ * @default false
69
+ */
70
+ readonly scrolled?: boolean;
71
+ /**
72
+ * `sm` is Material Design's small bar, 64dp; `md` and `lg` are its medium
73
+ * and large flexible bars, 112dp and 120dp, or 136dp and 152dp with a
74
+ * subtitle, which hug their text. The headline is title large, headline
75
+ * small and headline medium in turn.
76
+ * @default 'sm'
77
+ */
78
+ readonly size?: AppBarSize;
79
+ /** A second line under the headline. Makes `md` and `lg` taller. */
80
+ readonly subtitle?: ReactNode;
81
+ /**
82
+ * Actions, at the far end, usually icon buttons. An icon here takes the
83
+ * on-surface-variant colour and a 24dp size through `useIconStyle()`.
84
+ */
85
+ readonly trailing?: ReactNode;
86
+ };
87
+ type AppBarSize = 'lg' | 'md' | 'sm';
88
+ /**
89
+ * Material Design's top app bar: the bar at the top of a screen carrying its
90
+ * title, one or two actions, and the way back out.
91
+ *
92
+ * ```tsx
93
+ * <AppBar
94
+ * headline="Headline"
95
+ * leading={<IconButton aria-label="Back"><ArrowBackGlyph /></IconButton>}
96
+ * />
97
+ * ```
98
+ *
99
+ * Three sizes. `sm` is a 64dp bar for a screen whose title is a label; `md`
100
+ * and `lg` are the flexible bars, which give the headline a larger type role
101
+ * and grow to fit a subtitle or a headline that wraps.
102
+ *
103
+ * It paints its own surface, from the screen's top edge, and keeps its
104
+ * contents clear of the status bar and the notch. Separating itself from the
105
+ * content beneath is its job, which is why `scrolled` and `collapsed` exist;
106
+ * `useAppBarScroll` sets both from the screen's scroll view.
107
+ */
108
+ declare function AppBar({ align, collapsed, contentInset, contentMaxInlineSize, headline, leading, pinned, scrolled, size, style, subtitle, trailing, ...props }: AppBarProps): import("react").JSX.Element;
109
+ //#endregion
110
+ export { AppBar, type AppBarProps };
111
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,341 @@
1
+ import { IconStyleContext } from "../../glyphs/icon-style.js";
2
+ import { useTheme } from "../../theme/scope.js";
3
+ import { composeStyles, themedStyles } from "../../theme/styles.js";
4
+ import { hasContent } from "../../row/index.js";
5
+ import { Text as Text$1 } from "../text/index.js";
6
+ import { c } from "react/compiler-runtime";
7
+ import { StyleSheet, View } from "react-native";
8
+ import Animated, { Easing, useAnimatedStyle, useReducedMotion, withTiming } from "react-native-reanimated";
9
+ import { SafeAreaView } from "react-native-safe-area-context";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ //#region src/components/app-bar/index.tsx
12
+ const ICON_SIZE = 24;
13
+ const GLYPH_INSET = 12;
14
+ const HEIGHTS = {
15
+ lg: {
16
+ plain: 120,
17
+ withSubtitle: 152
18
+ },
19
+ md: {
20
+ plain: 112,
21
+ withSubtitle: 136
22
+ },
23
+ sm: {
24
+ plain: 64,
25
+ withSubtitle: 64
26
+ }
27
+ };
28
+ const HEADLINE_ROLES = {
29
+ lg: "headlineMedium",
30
+ md: "headlineSmall",
31
+ sm: "titleLarge"
32
+ };
33
+ const EDGES = [
34
+ "left",
35
+ "right",
36
+ "top"
37
+ ];
38
+ const styles = StyleSheet.create({
39
+ center: { textAlign: "center" },
40
+ pinned: { zIndex: 1 },
41
+ row: {
42
+ alignItems: "center",
43
+ flexDirection: "row",
44
+ width: "100%"
45
+ },
46
+ safeArea: { alignItems: "center" }
47
+ });
48
+ const stylesOf = themedStyles(({ sizing, spacing }) => StyleSheet.create({
49
+ row: { gap: spacing.xs },
50
+ slot: {
51
+ alignItems: "center",
52
+ flexDirection: "row",
53
+ flexShrink: 0,
54
+ gap: 48 - sizing.controlSm,
55
+ paddingHorizontal: (48 - sizing.controlSm) / 2
56
+ },
57
+ text: {
58
+ flexGrow: 1,
59
+ flexShrink: 1,
60
+ justifyContent: "center",
61
+ minWidth: 0,
62
+ paddingVertical: spacing.sm
63
+ }
64
+ }));
65
+ /**
66
+ * Material Design's top app bar: the bar at the top of a screen carrying its
67
+ * title, one or two actions, and the way back out.
68
+ *
69
+ * ```tsx
70
+ * <AppBar
71
+ * headline="Headline"
72
+ * leading={<IconButton aria-label="Back"><ArrowBackGlyph /></IconButton>}
73
+ * />
74
+ * ```
75
+ *
76
+ * Three sizes. `sm` is a 64dp bar for a screen whose title is a label; `md`
77
+ * and `lg` are the flexible bars, which give the headline a larger type role
78
+ * and grow to fit a subtitle or a headline that wraps.
79
+ *
80
+ * It paints its own surface, from the screen's top edge, and keeps its
81
+ * contents clear of the status bar and the notch. Separating itself from the
82
+ * content beneath is its job, which is why `scrolled` and `collapsed` exist;
83
+ * `useAppBarScroll` sets both from the screen's scroll view.
84
+ */
85
+ function AppBar(t0) {
86
+ const $ = c(58);
87
+ let contentInset;
88
+ let contentMaxInlineSize;
89
+ let headline;
90
+ let leading;
91
+ let props;
92
+ let style;
93
+ let subtitle;
94
+ let t1;
95
+ let t2;
96
+ let t3;
97
+ let t4;
98
+ let t5;
99
+ let trailing;
100
+ if ($[0] !== t0) {
101
+ ({align: t1, collapsed: t2, contentInset, contentMaxInlineSize, headline, leading, pinned: t3, scrolled: t4, size: t5, style, subtitle, trailing, ...props} = t0);
102
+ $[0] = t0;
103
+ $[1] = contentInset;
104
+ $[2] = contentMaxInlineSize;
105
+ $[3] = headline;
106
+ $[4] = leading;
107
+ $[5] = props;
108
+ $[6] = style;
109
+ $[7] = subtitle;
110
+ $[8] = t1;
111
+ $[9] = t2;
112
+ $[10] = t3;
113
+ $[11] = t4;
114
+ $[12] = t5;
115
+ $[13] = trailing;
116
+ } else {
117
+ contentInset = $[1];
118
+ contentMaxInlineSize = $[2];
119
+ headline = $[3];
120
+ leading = $[4];
121
+ props = $[5];
122
+ style = $[6];
123
+ subtitle = $[7];
124
+ t1 = $[8];
125
+ t2 = $[9];
126
+ t3 = $[10];
127
+ t4 = $[11];
128
+ t5 = $[12];
129
+ trailing = $[13];
130
+ }
131
+ const align = t1 === void 0 ? "start" : t1;
132
+ const collapsed = t2 === void 0 ? false : t2;
133
+ const pinned = t3 === void 0 ? false : t3;
134
+ const scrolled = t4 === void 0 ? false : t4;
135
+ const size = t5 === void 0 ? "sm" : t5;
136
+ const theme = useTheme();
137
+ const own = stylesOf(theme);
138
+ const inset = contentInset ?? theme.spacing.lg;
139
+ const collapse = collapsed && size !== "sm";
140
+ const hasLeading = hasContent(leading);
141
+ const hasTrailing = hasContent(trailing);
142
+ let t6;
143
+ if ($[14] !== collapse || $[15] !== subtitle) {
144
+ t6 = hasContent(subtitle) && !collapse;
145
+ $[14] = collapse;
146
+ $[15] = subtitle;
147
+ $[16] = t6;
148
+ } else t6 = $[16];
149
+ const showsSubtitle = t6;
150
+ const heights = HEIGHTS[collapse ? "sm" : size];
151
+ const fill = useFill(scrolled);
152
+ const t7 = Animated;
153
+ const t8 = pinned ? styles.pinned : null;
154
+ let t9;
155
+ if ($[17] !== fill || $[18] !== style || $[19] !== t8) {
156
+ t9 = composeStyles(fill, t8, style);
157
+ $[17] = fill;
158
+ $[18] = style;
159
+ $[19] = t8;
160
+ $[20] = t9;
161
+ } else t9 = $[20];
162
+ const T0 = SafeAreaView;
163
+ const t10 = EDGES;
164
+ const t11 = styles;
165
+ const T1 = View;
166
+ const t12 = composeStyles(styles.row, own.row, {
167
+ maxWidth: contentMaxInlineSize,
168
+ minHeight: showsSubtitle ? heights.withSubtitle : heights.plain,
169
+ paddingEnd: edgePadding(inset, hasTrailing),
170
+ paddingStart: edgePadding(inset, hasLeading)
171
+ });
172
+ let t13;
173
+ if ($[21] !== hasLeading || $[22] !== leading || $[23] !== theme) {
174
+ t13 = hasLeading ? /* @__PURE__ */ jsx(Slot, {
175
+ color: theme.colors.onSurface,
176
+ children: leading
177
+ }) : null;
178
+ $[21] = hasLeading;
179
+ $[22] = leading;
180
+ $[23] = theme;
181
+ $[24] = t13;
182
+ } else t13 = $[24];
183
+ let t14;
184
+ if ($[25] !== align || $[26] !== collapse || $[27] !== headline || $[28] !== size) {
185
+ t14 = hasContent(headline) ? /* @__PURE__ */ jsx(Text$1, {
186
+ role: "heading",
187
+ style: align === "center" ? styles.center : void 0,
188
+ variant: HEADLINE_ROLES[collapse ? "sm" : size],
189
+ children: headline
190
+ }) : null;
191
+ $[25] = align;
192
+ $[26] = collapse;
193
+ $[27] = headline;
194
+ $[28] = size;
195
+ $[29] = t14;
196
+ } else t14 = $[29];
197
+ let t15;
198
+ if ($[30] !== align || $[31] !== showsSubtitle || $[32] !== subtitle) {
199
+ t15 = showsSubtitle ? /* @__PURE__ */ jsx(Text$1, {
200
+ style: align === "center" ? styles.center : void 0,
201
+ tone: "muted",
202
+ variant: "bodyMedium",
203
+ children: subtitle
204
+ }) : null;
205
+ $[30] = align;
206
+ $[31] = showsSubtitle;
207
+ $[32] = subtitle;
208
+ $[33] = t15;
209
+ } else t15 = $[33];
210
+ let t16;
211
+ if ($[34] !== own.text || $[35] !== t14 || $[36] !== t15) {
212
+ t16 = /* @__PURE__ */ jsxs(View, {
213
+ style: own.text,
214
+ children: [t14, t15]
215
+ });
216
+ $[34] = own.text;
217
+ $[35] = t14;
218
+ $[36] = t15;
219
+ $[37] = t16;
220
+ } else t16 = $[37];
221
+ let t17;
222
+ if ($[38] !== hasTrailing || $[39] !== theme || $[40] !== trailing) {
223
+ t17 = hasTrailing ? /* @__PURE__ */ jsx(Slot, {
224
+ color: theme.colors.onSurfaceVariant,
225
+ children: trailing
226
+ }) : null;
227
+ $[38] = hasTrailing;
228
+ $[39] = theme;
229
+ $[40] = trailing;
230
+ $[41] = t17;
231
+ } else t17 = $[41];
232
+ let t18;
233
+ if ($[42] !== T1 || $[43] !== t12 || $[44] !== t13 || $[45] !== t16 || $[46] !== t17) {
234
+ t18 = /* @__PURE__ */ jsxs(T1, {
235
+ style: t12,
236
+ children: [
237
+ t13,
238
+ t16,
239
+ t17
240
+ ]
241
+ });
242
+ $[42] = T1;
243
+ $[43] = t12;
244
+ $[44] = t13;
245
+ $[45] = t16;
246
+ $[46] = t17;
247
+ $[47] = t18;
248
+ } else t18 = $[47];
249
+ let t19;
250
+ if ($[48] !== T0 || $[49] !== t10 || $[50] !== t11.safeArea || $[51] !== t18) {
251
+ t19 = /* @__PURE__ */ jsx(T0, {
252
+ edges: t10,
253
+ style: t11.safeArea,
254
+ children: t18
255
+ });
256
+ $[48] = T0;
257
+ $[49] = t10;
258
+ $[50] = t11.safeArea;
259
+ $[51] = t18;
260
+ $[52] = t19;
261
+ } else t19 = $[52];
262
+ let t20;
263
+ if ($[53] !== props || $[54] !== t19 || $[55] !== t7.View || $[56] !== t9) {
264
+ t20 = /* @__PURE__ */ jsx(t7.View, {
265
+ ...props,
266
+ style: t9,
267
+ children: t19
268
+ });
269
+ $[53] = props;
270
+ $[54] = t19;
271
+ $[55] = t7.View;
272
+ $[56] = t9;
273
+ $[57] = t20;
274
+ } else t20 = $[57];
275
+ return t20;
276
+ }
277
+ function edgePadding(inset, hasSlot) {
278
+ return hasSlot ? Math.max(0, inset - GLYPH_INSET) : inset;
279
+ }
280
+ function iconStyleOf(color) {
281
+ return {
282
+ color,
283
+ size: ICON_SIZE
284
+ };
285
+ }
286
+ function Slot(t0) {
287
+ const $ = c(10);
288
+ const { children, color } = t0;
289
+ const t1 = useTheme();
290
+ let t2;
291
+ if ($[0] !== t1) {
292
+ t2 = stylesOf(t1);
293
+ $[0] = t1;
294
+ $[1] = t2;
295
+ } else t2 = $[1];
296
+ const own = t2;
297
+ let t3;
298
+ if ($[2] !== color) {
299
+ t3 = iconStyleOf(color);
300
+ $[2] = color;
301
+ $[3] = t3;
302
+ } else t3 = $[3];
303
+ let t4;
304
+ if ($[4] !== children || $[5] !== own.slot) {
305
+ t4 = /* @__PURE__ */ jsx(View, {
306
+ style: own.slot,
307
+ children
308
+ });
309
+ $[4] = children;
310
+ $[5] = own.slot;
311
+ $[6] = t4;
312
+ } else t4 = $[6];
313
+ let t5;
314
+ if ($[7] !== t3 || $[8] !== t4) {
315
+ t5 = /* @__PURE__ */ jsx(IconStyleContext, {
316
+ value: t3,
317
+ children: t4
318
+ });
319
+ $[7] = t3;
320
+ $[8] = t4;
321
+ $[9] = t5;
322
+ } else t5 = $[9];
323
+ return t5;
324
+ }
325
+ function useFill(scrolled) {
326
+ const { colors, motion } = useTheme();
327
+ const reduceMotion = useReducedMotion();
328
+ const fill = scrolled ? colors.surfaceContainer : colors.surface;
329
+ const timing = {
330
+ duration: motion.durationMedium1,
331
+ easing: Easing.bezier(...motion.easingEmphasized)
332
+ };
333
+ return useAnimatedStyle(() => {
334
+ "worklet";
335
+ return { backgroundColor: reduceMotion ? fill : withTiming(fill, timing) };
336
+ });
337
+ }
338
+ //#endregion
339
+ export { AppBar };
340
+
341
+ //# sourceMappingURL=index.js.map