@kanso-labs/react-native-kanso-ui 0.0.0-stage → 0.1.1

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 (145) hide show
  1. package/LICENSE.md +20 -0
  2. package/README.md +205 -2
  3. package/dist/button/index.d.ts +56 -0
  4. package/dist/button/index.js +169 -0
  5. package/dist/button/index.js.map +1 -0
  6. package/dist/button/palettes.js +47 -0
  7. package/dist/button/palettes.js.map +1 -0
  8. package/dist/chip/index.js +194 -0
  9. package/dist/chip/index.js.map +1 -0
  10. package/dist/chip/styles.js +36 -0
  11. package/dist/chip/styles.js.map +1 -0
  12. package/dist/clipboard/index.js +58 -0
  13. package/dist/clipboard/index.js.map +1 -0
  14. package/dist/components/avatar/index.d.ts +82 -0
  15. package/dist/components/avatar/index.js +62 -0
  16. package/dist/components/avatar/index.js.map +1 -0
  17. package/dist/components/button/index.d.ts +87 -0
  18. package/dist/components/button/index.js +377 -0
  19. package/dist/components/button/index.js.map +1 -0
  20. package/dist/components/card/index.d.ts +78 -0
  21. package/dist/components/card/index.js +315 -0
  22. package/dist/components/card/index.js.map +1 -0
  23. package/dist/components/checkbox/index.d.ts +76 -0
  24. package/dist/components/checkbox/index.js +360 -0
  25. package/dist/components/checkbox/index.js.map +1 -0
  26. package/dist/components/chip/index.d.ts +84 -0
  27. package/dist/components/chip/index.js +257 -0
  28. package/dist/components/chip/index.js.map +1 -0
  29. package/dist/components/chip-group/index.d.ts +143 -0
  30. package/dist/components/chip-group/index.js +669 -0
  31. package/dist/components/chip-group/index.js.map +1 -0
  32. package/dist/components/copy-field/index.d.ts +60 -0
  33. package/dist/components/copy-field/index.js +277 -0
  34. package/dist/components/copy-field/index.js.map +1 -0
  35. package/dist/components/currency/index.d.ts +69 -0
  36. package/dist/components/currency/index.js +162 -0
  37. package/dist/components/currency/index.js.map +1 -0
  38. package/dist/components/form/index.d.ts +84 -0
  39. package/dist/components/form/index.js +95 -0
  40. package/dist/components/form/index.js.map +1 -0
  41. package/dist/components/icon-button/index.d.ts +68 -0
  42. package/dist/components/icon-button/index.js +289 -0
  43. package/dist/components/icon-button/index.js.map +1 -0
  44. package/dist/components/link/index.d.ts +59 -0
  45. package/dist/components/link/index.js +191 -0
  46. package/dist/components/link/index.js.map +1 -0
  47. package/dist/components/number-field/index.d.ts +151 -0
  48. package/dist/components/number-field/index.js +832 -0
  49. package/dist/components/number-field/index.js.map +1 -0
  50. package/dist/components/product-icon/index.d.ts +70 -0
  51. package/dist/components/product-icon/index.js +60 -0
  52. package/dist/components/product-icon/index.js.map +1 -0
  53. package/dist/components/radio-group/index.d.ts +105 -0
  54. package/dist/components/radio-group/index.js +597 -0
  55. package/dist/components/radio-group/index.js.map +1 -0
  56. package/dist/components/search-field/index.d.ts +98 -0
  57. package/dist/components/search-field/index.js +521 -0
  58. package/dist/components/search-field/index.js.map +1 -0
  59. package/dist/components/segmented-button/index.d.ts +113 -0
  60. package/dist/components/segmented-button/index.js +937 -0
  61. package/dist/components/segmented-button/index.js.map +1 -0
  62. package/dist/components/separator/index.d.ts +45 -0
  63. package/dist/components/separator/index.js +109 -0
  64. package/dist/components/separator/index.js.map +1 -0
  65. package/dist/components/stack/index.d.ts +70 -0
  66. package/dist/components/stack/index.js +107 -0
  67. package/dist/components/stack/index.js.map +1 -0
  68. package/dist/components/switch/index.d.ts +76 -0
  69. package/dist/components/switch/index.js +591 -0
  70. package/dist/components/switch/index.js.map +1 -0
  71. package/dist/components/text/index.d.ts +44 -0
  72. package/dist/components/text/index.js +78 -0
  73. package/dist/components/text/index.js.map +1 -0
  74. package/dist/components/text-field/index.d.ts +146 -0
  75. package/dist/components/text-field/index.js +228 -0
  76. package/dist/components/text-field/index.js.map +1 -0
  77. package/dist/control/index.js +117 -0
  78. package/dist/control/index.js.map +1 -0
  79. package/dist/control/styles.js +36 -0
  80. package/dist/control/styles.js.map +1 -0
  81. package/dist/field/index.d.ts +13 -0
  82. package/dist/field/index.js +1007 -0
  83. package/dist/field/index.js.map +1 -0
  84. package/dist/field/styles.js +131 -0
  85. package/dist/field/styles.js.map +1 -0
  86. package/dist/form/index.d.ts +24 -0
  87. package/dist/form/index.js +209 -0
  88. package/dist/form/index.js.map +1 -0
  89. package/dist/glyphs/icon-style.d.ts +25 -0
  90. package/dist/glyphs/icon-style.js +22 -0
  91. package/dist/glyphs/icon-style.js.map +1 -0
  92. package/dist/glyphs/index.js +170 -0
  93. package/dist/glyphs/index.js.map +1 -0
  94. package/dist/glyphs/styles.js +7 -0
  95. package/dist/glyphs/styles.js.map +1 -0
  96. package/dist/hooks/useWindowSizeClass.d.ts +25 -0
  97. package/dist/hooks/useWindowSizeClass.js +39 -0
  98. package/dist/hooks/useWindowSizeClass.js.map +1 -0
  99. package/dist/index.d.ts +31 -0
  100. package/dist/index.js +27 -0
  101. package/dist/mark/image.js +61 -0
  102. package/dist/mark/image.js.map +1 -0
  103. package/dist/mark/index.d.ts +9 -0
  104. package/dist/mark/index.js +234 -0
  105. package/dist/mark/index.js.map +1 -0
  106. package/dist/mark/initials.js +65 -0
  107. package/dist/mark/initials.js.map +1 -0
  108. package/dist/mark/styles.d.ts +1 -0
  109. package/dist/mark/styles.js +73 -0
  110. package/dist/mark/styles.js.map +1 -0
  111. package/dist/number/parser.js +321 -0
  112. package/dist/number/parser.js.map +1 -0
  113. package/dist/number/state.js +235 -0
  114. package/dist/number/state.js.map +1 -0
  115. package/dist/overlay/host.js +111 -0
  116. package/dist/overlay/host.js.map +1 -0
  117. package/dist/press/index.js +350 -0
  118. package/dist/press/index.js.map +1 -0
  119. package/dist/press/styles.js +31 -0
  120. package/dist/press/styles.js.map +1 -0
  121. package/dist/router/index.d.ts +28 -0
  122. package/dist/router/index.js +82 -0
  123. package/dist/router/index.js.map +1 -0
  124. package/dist/theme/color.js +25 -0
  125. package/dist/theme/color.js.map +1 -0
  126. package/dist/theme/resolve.d.ts +6 -0
  127. package/dist/theme/resolve.js +310 -0
  128. package/dist/theme/resolve.js.map +1 -0
  129. package/dist/theme/scope.d.ts +42 -0
  130. package/dist/theme/scope.js +80 -0
  131. package/dist/theme/scope.js.map +1 -0
  132. package/dist/theme/styles.js +39 -0
  133. package/dist/theme/styles.js.map +1 -0
  134. package/dist/theme-scope.d.ts +56 -0
  135. package/dist/theme-scope.js +127 -0
  136. package/dist/theme-scope.js.map +1 -0
  137. package/dist/tokens/defaults.d.ts +407 -0
  138. package/dist/tokens/defaults.js +403 -0
  139. package/dist/tokens/defaults.js.map +1 -0
  140. package/dist/typography/fonts.js +84 -0
  141. package/dist/typography/fonts.js.map +1 -0
  142. package/dist/typography/roles.d.ts +10 -0
  143. package/dist/typography/roles.js +82 -0
  144. package/dist/typography/roles.js.map +1 -0
  145. package/package.json +132 -4
package/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kanso Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,206 @@
1
- # Temporary Holding Version
1
+ # react-native-kanso-ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![License][license-shield]][license] [![Build][build-shield]][build-workflow]
4
+ [![Test][test-shield]][test-workflow] [![Coverage][coverage-shield]][codecov]
5
+
6
+ Kanso UI's Material Design components for React Native, built on the same design
7
+ tokens and theming as
8
+ [`@kanso-labs/kanso-ui`](https://github.com/kanso-labs/kanso-ui).
9
+
10
+ A theme is the web library's: a set of `--kui-*` custom properties with values
11
+ in CSS syntax, as kanso-ui's `tokens.css` declares them, so one theme object
12
+ serves a web app and a native app alike.
13
+
14
+ The package has not been released yet.
15
+
16
+ ## Theming
17
+
18
+ `ThemeScope` themes everything inside it, and takes the same props as
19
+ kanso-ui's: `theme`, a map of `--kui-*` properties to CSS values, and `scheme`,
20
+ which pins the scope to light or dark and paints its surface. Left unset, the
21
+ scheme follows the scope around it, or the device's appearance at the root. A
22
+ nested scope's map applies over its parent's.
23
+
24
+ ```tsx
25
+ import { type KuiTheme, ThemeScope } from '@kanso-labs/react-native-kanso-ui'
26
+
27
+ // The same object a web app hands kanso-ui's ThemeScope.
28
+ const brand: KuiTheme = {
29
+ '--kui-color-primary': '#006a6a',
30
+ '--kui-radius-md': '16px',
31
+ }
32
+
33
+ export default function App() {
34
+ return (
35
+ <ThemeScope style={{ flex: 1 }} theme={brand}>
36
+ <Screen />
37
+ </ThemeScope>
38
+ )
39
+ }
40
+ ```
41
+
42
+ `useTheme()` reads the tokens in the values React Native styles take, with the
43
+ keys of kanso-ui's token modules, so `var(--kui-color-primary)` on web is
44
+ `useTheme().colors.primary` here. Lengths are in dp, durations in milliseconds,
45
+ and an easing is the four points `Easing.bezier()` takes.
46
+
47
+ A value React Native cannot evaluate, such as a `calc()` or a `color-mix()`,
48
+ keeps its property at the library's default, and a development build names it in
49
+ a warning.
50
+
51
+ The outermost `ThemeScope` also hosts what the library draws above the screen,
52
+ over everything inside it, so it belongs at the root of the app and fills it, as
53
+ the one above does. What draws there follows the scope it was opened in rather
54
+ than the root's.
55
+
56
+ ### Fonts
57
+
58
+ Neither package ships a font. On iOS and Android a type role draws in the first
59
+ family its token names when the app has registered that family with
60
+ [expo-font](https://docs.expo.dev/versions/latest/sdk/font/), under that name,
61
+ and in the system font otherwise. expo-font is an optional peer, so an app
62
+ without it draws every role in the system font.
63
+
64
+ ```tsx
65
+ import { useFonts } from 'expo-font'
66
+
67
+ const [loaded] = useFonts({
68
+ 'Roboto Flex': require('./assets/RobotoFlex.ttf'),
69
+ 'Roboto Serif': require('./assets/RobotoSerif.ttf'),
70
+ })
71
+ ```
72
+
73
+ Under React Native Web the token's whole list goes to CSS, as it does on web.
74
+
75
+ ## Peer dependencies
76
+
77
+ Beside `react` and `react-native`, the package needs `react-native-reanimated`,
78
+ which animates the ripple its controls draw when pressed and the corner a button
79
+ morphs between, the `react-native-worklets` Reanimated runs on,
80
+ `react-native-svg`, which draws the icons its controls show,
81
+ `react-native-safe-area-context`, whose insets keep what the library draws above
82
+ the screen clear of a notch, the status bar and the home indicator, and
83
+ `react-native-gesture-handler`, which carries a switch's handle across its track
84
+ when it is dragged. In an Expo app,
85
+
86
+ ```bash
87
+ npx expo install react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-svg react-native-worklets
88
+ ```
89
+
90
+ installs the versions the SDK ships with, and Expo's Babel preset compiles the
91
+ package's animations. Outside Expo, Reanimated's install guide adds
92
+ `react-native-worklets/plugin` to the app's Babel configuration, which does the
93
+ same.
94
+
95
+ The insets come from safe-area-context's `SafeAreaProvider`, which Expo Router
96
+ puts around an app already; elsewhere, wrap the app in one. Without it, the
97
+ library keeps to the window's edge.
98
+
99
+ Gesture Handler 2 and 3 both satisfy the peer. A `Switch` draws Gesture
100
+ Handler's root view around its own track, so an app need not wrap itself in
101
+ `GestureHandlerRootView` for it, and one that already does loses nothing.
102
+
103
+ ## Routing
104
+
105
+ A component with an `href` presses through the app's router once the app is
106
+ wrapped in `RouterProvider`, which takes the router's `navigate`. Without one,
107
+ and for any URL with a scheme of its own such as `https:` or `mailto:`, the
108
+ `href` is opened with React Native's `Linking.openURL`.
109
+
110
+ With Expo Router, the provider goes in the root layout:
111
+
112
+ ```tsx
113
+ // app/_layout.tsx
114
+ import { RouterProvider } from '@kanso-labs/react-native-kanso-ui'
115
+ import { type Href, Stack, useRouter } from 'expo-router'
116
+
117
+ export default function RootLayout() {
118
+ const router = useRouter()
119
+ return (
120
+ <RouterProvider navigate={(href) => router.push(href as Href)}>
121
+ <Stack />
122
+ </RouterProvider>
123
+ )
124
+ }
125
+ ```
126
+
127
+ The `as Href` is there for typed routes, which type `push` with the app's own
128
+ paths rather than any string.
129
+
130
+ With React Navigation, `useLinkTo` turns a path into a navigation through the
131
+ container's `linking` configuration, so the provider goes inside the container:
132
+
133
+ ```tsx
134
+ import type { ReactNode } from 'react'
135
+
136
+ import { RouterProvider } from '@kanso-labs/react-native-kanso-ui'
137
+ import { NavigationContainer, useLinkTo } from '@react-navigation/native'
138
+
139
+ function Routed({ children }: { children: ReactNode }) {
140
+ const linkTo = useLinkTo()
141
+ return <RouterProvider navigate={linkTo}>{children}</RouterProvider>
142
+ }
143
+
144
+ export default function App() {
145
+ return (
146
+ <NavigationContainer linking={linking}>
147
+ <Routed>
148
+ <RootStack />
149
+ </Routed>
150
+ </NavigationContainer>
151
+ )
152
+ }
153
+ ```
154
+
155
+ ## Development
156
+
157
+ Install with the Node version in `.tool-versions`:
158
+
159
+ ```bash
160
+ mise exec node@"$(awk '/^nodejs/{print $2}' .tool-versions)" -- npm install
161
+ ```
162
+
163
+ | Task | Command |
164
+ | --------- | ------------------------ |
165
+ | Test | `npm test` |
166
+ | Stories | `npm run test:storybook` |
167
+ | Storybook | `npm run storybook` |
168
+ | Lint | `npm run lint` |
169
+ | Build | `npm run build` |
170
+ | Verify | `npm run package:check` |
171
+ | Tokens | `npm run tokens:build` |
172
+
173
+ `npm run storybook` serves the components through React Native Web. Its Theme
174
+ control offers kanso-ui's seven demo schemes beside light and dark, and the
175
+ `Theming` section renders every component under each of them. To see the
176
+ components drawn by iOS or Android, run the on-device Storybook in `example/`,
177
+ an Expo app that reads the same stories. With the root installed:
178
+
179
+ ```bash
180
+ cd example
181
+ npm install
182
+ npm start
183
+ ```
184
+
185
+ Then open it in Expo Go from the QR code Expo prints. The device's light or dark
186
+ appearance picks the scheme the stories render in, apart from the `Theming`
187
+ pages, which each render in their own.
188
+
189
+ ## License
190
+
191
+ [MIT](LICENSE.md)
192
+
193
+ [build-shield]:
194
+ https://img.shields.io/github/actions/workflow/status/kanso-labs/react-native-kanso-ui/build.yaml?branch=main&label=Build
195
+ [build-workflow]:
196
+ https://github.com/kanso-labs/react-native-kanso-ui/actions/workflows/build.yaml
197
+ [codecov]: https://codecov.io/gh/kanso-labs/react-native-kanso-ui
198
+ [coverage-shield]:
199
+ https://img.shields.io/codecov/c/github/kanso-labs/react-native-kanso-ui?label=Coverage
200
+ [license]: ./LICENSE.md
201
+ [license-shield]:
202
+ https://img.shields.io/github/license/kanso-labs/react-native-kanso-ui
203
+ [test-shield]:
204
+ https://img.shields.io/github/actions/workflow/status/kanso-labs/react-native-kanso-ui/test.yaml?branch=main&label=Test
205
+ [test-workflow]:
206
+ https://github.com/kanso-labs/react-native-kanso-ui/actions/workflows/test.yaml
@@ -0,0 +1,56 @@
1
+ import { PressableProps } from "react-native";
2
+ //#region src/button/index.d.ts
3
+ /**
4
+ * A plain button, a link, or a toggle, of which a call site writes exactly
5
+ * one. Each form names the props of the other two as `never`, so a toggle
6
+ * given `href` is a compile error rather than a prop that silently does
7
+ * nothing.
8
+ */
9
+ type ButtonLinkForm = {
10
+ readonly defaultSelected?: never;
11
+ /**
12
+ * Where the button leads. Given one, a press opens it through the
13
+ * `RouterProvider` around the button, or through the platform for a URL
14
+ * with a scheme of its own, and the button is announced as a link.
15
+ */
16
+ readonly href: string;
17
+ readonly isSelected?: never;
18
+ readonly onChange?: never;
19
+ readonly type?: never;
20
+ };
21
+ type ButtonPressForm = {
22
+ readonly defaultSelected?: never;
23
+ readonly href?: never;
24
+ readonly isSelected?: never;
25
+ readonly onChange?: never;
26
+ /**
27
+ * What a press does past `onPress`. `submit` submits the `Form` the button
28
+ * is in, as a submit button does on web; `button` does nothing more.
29
+ * @default 'button'
30
+ */
31
+ readonly type?: 'button' | 'submit';
32
+ };
33
+ type ButtonToggleForm = {
34
+ /**
35
+ * Whether a toggle starts selected, when it keeps its own state. Passing
36
+ * this, `isSelected` or `onChange` is what makes the button a toggle.
37
+ */
38
+ readonly defaultSelected?: boolean;
39
+ readonly href?: never;
40
+ /**
41
+ * Whether a toggle is selected, when the call site holds the state. Pass
42
+ * it with `onChange`; passing this, `defaultSelected` or `onChange` is what
43
+ * makes the button a toggle.
44
+ */
45
+ readonly isSelected?: boolean;
46
+ /**
47
+ * Called with the new state when a toggle is pressed, after `onPress`.
48
+ * Passing this, `isSelected` or `defaultSelected` is what makes the button
49
+ * a toggle.
50
+ */
51
+ readonly onChange?: (isSelected: boolean) => void;
52
+ readonly type?: never;
53
+ };
54
+ //#endregion
55
+ export type { ButtonLinkForm, ButtonPressForm, ButtonToggleForm };
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,169 @@
1
+ import { useTheme } from "../theme/scope.js";
2
+ import { FormContext } from "../form/index.js";
3
+ import { useOpenLink } from "../router/index.js";
4
+ import { c } from "react/compiler-runtime";
5
+ import { Platform, Pressable, StyleSheet } from "react-native";
6
+ import { use, useState } from "react";
7
+ import Animated, { Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from "react-native-reanimated";
8
+ //#region src/button/index.ts
9
+ const HEIGHTS = {
10
+ lg: "controlLg",
11
+ md: "controlSm",
12
+ xl: "controlXl",
13
+ xs: "controlXs",
14
+ xxl: "controlXxl"
15
+ };
16
+ const OUTLINE_WIDTHS = {
17
+ lg: 1,
18
+ md: 1,
19
+ xl: 2,
20
+ xs: 1,
21
+ xxl: 3
22
+ };
23
+ const PRESSED_CORNERS = {
24
+ lg: "md",
25
+ md: "sm",
26
+ xl: "lg",
27
+ xs: "sm",
28
+ xxl: "lg"
29
+ };
30
+ const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
31
+ const toggleState = Platform.select({
32
+ default: (selected) => ({ "aria-selected": selected }),
33
+ web: (selected) => ({ "aria-pressed": selected })
34
+ });
35
+ const buttonStyles = StyleSheet.create({ clip: {
36
+ bottom: 0,
37
+ left: 0,
38
+ overflow: "hidden",
39
+ pointerEvents: "none",
40
+ position: "absolute",
41
+ right: 0,
42
+ top: 0
43
+ } });
44
+ function measureThen(height, onLayout) {
45
+ return (event) => {
46
+ height.set(event.nativeEvent.layout.height);
47
+ onLayout?.(event);
48
+ };
49
+ }
50
+ function pressThen(onPress, then) {
51
+ return (event) => {
52
+ onPress?.(event);
53
+ then();
54
+ };
55
+ }
56
+ /**
57
+ * What a press does, and whether a toggle is selected. A press calls the call
58
+ * site's own `onPress` first, then opens the link, changes the toggle's state
59
+ * or submits the form the button is in, which is the order web reports them
60
+ * in. A toggle keeps its own state from `defaultSelected` unless the call
61
+ * site holds it in `isSelected`. A submit button outside a form submits
62
+ * nothing, as on web.
63
+ */
64
+ function useButtonAction(t0) {
65
+ const $ = c(14);
66
+ const { defaultSelected, href, isSelected, isToggle, onChange, onPress, type } = t0;
67
+ const openLink = useOpenLink();
68
+ const form = use(FormContext);
69
+ const [ownSelected, setOwnSelected] = useState(defaultSelected ?? false);
70
+ const selected = isToggle && (isSelected ?? ownSelected);
71
+ 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) {
76
+ setOwnSelected(!selected);
77
+ onChange?.(!selected);
78
+ } else if (type === "submit") form?.requestSubmit();
79
+ };
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];
89
+ const follow = t1;
90
+ let t2;
91
+ if ($[8] !== follow || $[9] !== onPress) {
92
+ t2 = pressThen(onPress, follow);
93
+ $[8] = follow;
94
+ $[9] = onPress;
95
+ $[10] = t2;
96
+ } else t2 = $[10];
97
+ let t3;
98
+ if ($[11] !== selected || $[12] !== t2) {
99
+ t3 = {
100
+ onPress: t2,
101
+ selected
102
+ };
103
+ $[11] = selected;
104
+ $[12] = t2;
105
+ $[13] = t3;
106
+ } else t3 = $[13];
107
+ return t3;
108
+ }
109
+ /**
110
+ * The corner a button eases to, over the motion tokens' short duration on the
111
+ * emphasised curve, and at once for a reader who asks the device for reduced
112
+ * motion. The pill is the radius scale's `pill`, far larger than any button,
113
+ * so the ease runs from half the button's measured height rather than from
114
+ * that: otherwise the corner would hold the pill for most of the ease and
115
+ * jump at its end. The `onLayout` it hands back measures the button, then
116
+ * calls the call site's own.
117
+ */
118
+ function useCorner(radius, onLayout) {
119
+ const $ = c(11);
120
+ const { motion } = useTheme();
121
+ const reduceMotion = useReducedMotion();
122
+ const height = useSharedValue(0);
123
+ const t0 = motion.durationShort2;
124
+ let t1;
125
+ if ($[0] !== motion.easingEmphasized) {
126
+ t1 = Easing.bezier(...motion.easingEmphasized);
127
+ $[0] = motion.easingEmphasized;
128
+ $[1] = t1;
129
+ } else t1 = $[1];
130
+ let t2;
131
+ if ($[2] !== motion.durationShort2 || $[3] !== t1) {
132
+ t2 = {
133
+ duration: t0,
134
+ easing: t1
135
+ };
136
+ $[2] = motion.durationShort2;
137
+ $[3] = t1;
138
+ $[4] = t2;
139
+ } else t2 = $[4];
140
+ const timing = t2;
141
+ const corner = useAnimatedStyle(() => {
142
+ "worklet";
143
+ const measured = height.get();
144
+ const target = measured > 0 ? Math.min(radius, measured / 2) : radius;
145
+ return { borderRadius: reduceMotion ? target : withTiming(target, timing) };
146
+ });
147
+ let t3;
148
+ if ($[5] !== height || $[6] !== onLayout) {
149
+ t3 = measureThen(height, onLayout);
150
+ $[5] = height;
151
+ $[6] = onLayout;
152
+ $[7] = t3;
153
+ } else t3 = $[7];
154
+ let t4;
155
+ if ($[8] !== corner || $[9] !== t3) {
156
+ t4 = {
157
+ corner,
158
+ onLayout: t3
159
+ };
160
+ $[8] = corner;
161
+ $[9] = t3;
162
+ $[10] = t4;
163
+ } else t4 = $[10];
164
+ return t4;
165
+ }
166
+ //#endregion
167
+ export { AnimatedPressable, HEIGHTS, OUTLINE_WIDTHS, PRESSED_CORNERS, buttonStyles, toggleState, useButtonAction, useCorner };
168
+
169
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,47 @@
1
+ import { themedStyles } from "../theme/styles.js";
2
+ import { withOpacity } from "../theme/color.js";
3
+ //#region src/button/palettes.ts
4
+ function palette(content, container = {}) {
5
+ return {
6
+ container,
7
+ content,
8
+ label: { color: content }
9
+ };
10
+ }
11
+ /**
12
+ * The containers, from the theme's colour roles. Disabled is the pages'
13
+ * colour at an opacity, translucent: a container is on-surface at the
14
+ * `disabledContainer` opacity, and its content on-surface at
15
+ * `disabledContent`. Web mixes both over the surface role, since a
16
+ * `color-mix()` needs a colour to mix with; React Native has none, and the
17
+ * pages give the two as opacities over whatever the button sits on, which is
18
+ * what they are drawn as here.
19
+ */
20
+ const palettesOf = themedStyles(({ colors, stateLayerOpacity }) => {
21
+ const disabledContainer = withOpacity(colors.onSurface, stateLayerOpacity.disabledContainer);
22
+ const disabledContent = withOpacity(colors.onSurface, stateLayerOpacity.disabledContent);
23
+ return {
24
+ elevated: palette(colors.primary, { backgroundColor: colors.surfaceContainerLow }),
25
+ elevatedToggleSelected: palette(colors.onPrimary, { backgroundColor: colors.primary }),
26
+ filled: palette(colors.onPrimary, { backgroundColor: colors.primary }),
27
+ filledDisabled: palette(disabledContent, { backgroundColor: disabledContainer }),
28
+ filledToggle: palette(colors.onSurfaceVariant, { backgroundColor: colors.surfaceContainer }),
29
+ filledToggleSelected: palette(colors.onPrimary, { backgroundColor: colors.primary }),
30
+ outlined: palette(colors.onSurfaceVariant, { borderColor: colors.outlineVariant }),
31
+ outlinedDisabled: palette(disabledContent, { borderColor: disabledContainer }),
32
+ outlinedToggleSelected: palette(colors.inverseOnSurface, {
33
+ backgroundColor: colors.inverseSurface,
34
+ borderColor: "transparent"
35
+ }),
36
+ standard: palette(colors.onSurfaceVariant),
37
+ standardToggleSelected: palette(colors.primary),
38
+ text: palette(colors.primary),
39
+ textDisabled: palette(disabledContent),
40
+ tonal: palette(colors.onSecondaryContainer, { backgroundColor: colors.secondaryContainer }),
41
+ tonalToggleSelected: palette(colors.onSecondary, { backgroundColor: colors.secondary })
42
+ };
43
+ });
44
+ //#endregion
45
+ export { palettesOf };
46
+
47
+ //# sourceMappingURL=palettes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palettes.js","names":["TextStyle","ViewStyle","withOpacity","themedStyles","Palette","container","content","label","PaletteName","palette","color","palettesOf","colors","stateLayerOpacity","Readonly","Record","disabledContainer","onSurface","disabledContent","elevated","primary","backgroundColor","surfaceContainerLow","elevatedToggleSelected","onPrimary","filled","filledDisabled","filledToggle","onSurfaceVariant","surfaceContainer","filledToggleSelected","outlined","borderColor","outlineVariant","outlinedDisabled","outlinedToggleSelected","inverseOnSurface","inverseSurface","standard","standardToggleSelected","text","textDisabled","tonal","onSecondaryContainer","secondaryContainer","tonalToggleSelected","onSecondary","secondary"],"sources":["../../src/button/palettes.ts"],"sourcesContent":["import type { TextStyle, ViewStyle } from 'react-native'\n\nimport { withOpacity } from '../theme/color'\nimport { themedStyles } from '../theme/styles'\n\n/** What a container draws: its own style, and the colour of what is on it. */\ntype Palette = {\n readonly container: ViewStyle\n readonly content: string\n readonly label: TextStyle\n}\n\n// Every container a button or an icon button draws, by the name web's styles\n// give it. The two pages share their pairs, so the two components share the\n// table: a filled icon button is a filled button's colours around an icon.\n// Three of a toggle's unselected containers are the plain variant's own, so\n// they have no name of their own here.\ntype PaletteName =\n | 'elevated'\n | 'elevatedToggleSelected'\n | 'filled'\n | 'filledDisabled'\n | 'filledToggle'\n | 'filledToggleSelected'\n | 'outlined'\n | 'outlinedDisabled'\n | 'outlinedToggleSelected'\n | 'standard'\n | 'standardToggleSelected'\n | 'text'\n | 'textDisabled'\n | 'tonal'\n | 'tonalToggleSelected'\n\nfunction palette(content: string, container: ViewStyle = {}): Palette {\n return { container, content, label: { color: content } }\n}\n\n/**\n * The containers, from the theme's colour roles. Disabled is the pages'\n * colour at an opacity, translucent: a container is on-surface at the\n * `disabledContainer` opacity, and its content on-surface at\n * `disabledContent`. Web mixes both over the surface role, since a\n * `color-mix()` needs a colour to mix with; React Native has none, and the\n * pages give the two as opacities over whatever the button sits on, which is\n * what they are drawn as here.\n */\nconst palettesOf = themedStyles(\n ({ colors, stateLayerOpacity }): Readonly<Record<PaletteName, Palette>> => {\n const disabledContainer = withOpacity(\n colors.onSurface,\n stateLayerOpacity.disabledContainer,\n )\n const disabledContent = withOpacity(\n colors.onSurface,\n stateLayerOpacity.disabledContent,\n )\n\n return {\n elevated: palette(colors.primary, {\n backgroundColor: colors.surfaceContainerLow,\n }),\n elevatedToggleSelected: palette(colors.onPrimary, {\n backgroundColor: colors.primary,\n }),\n filled: palette(colors.onPrimary, { backgroundColor: colors.primary }),\n filledDisabled: palette(disabledContent, {\n backgroundColor: disabledContainer,\n }),\n filledToggle: palette(colors.onSurfaceVariant, {\n backgroundColor: colors.surfaceContainer,\n }),\n filledToggleSelected: palette(colors.onPrimary, {\n backgroundColor: colors.primary,\n }),\n outlined: palette(colors.onSurfaceVariant, {\n borderColor: colors.outlineVariant,\n }),\n outlinedDisabled: palette(disabledContent, {\n borderColor: disabledContainer,\n }),\n // The rule keeps its width, drawn transparent, so a toggle does not\n // change size as it is selected.\n outlinedToggleSelected: palette(colors.inverseOnSurface, {\n backgroundColor: colors.inverseSurface,\n borderColor: 'transparent',\n }),\n // Transparent, tinting whatever it sits on: the icon button's standard\n // style, whose icon moves to primary once a toggle is selected.\n standard: palette(colors.onSurfaceVariant),\n standardToggleSelected: palette(colors.primary),\n text: palette(colors.primary),\n // A text button's and a standard icon button's disabled colours, which\n // both pages give as the content alone, over no container.\n textDisabled: palette(disabledContent),\n tonal: palette(colors.onSecondaryContainer, {\n backgroundColor: colors.secondaryContainer,\n }),\n tonalToggleSelected: palette(colors.onSecondary, {\n backgroundColor: colors.secondary,\n }),\n }\n },\n)\n\nexport { palettesOf }\nexport type { Palette, PaletteName }\n"],"mappings":";;;AAkCA,SAASS,QAAQH,SAAiBD,YAAuB,CAAC,GAAY;CACpE,OAAO;EAAEA;EAAWC;EAASC,OAAO,EAAEG,OAAOJ,QAAQ;CAAE;AACzD;;;;;;;;;;AAWA,MAAMK,aAAaR,cAChB,EAAES,QAAQC,wBAAgE;CACzE,MAAMG,oBAAoBd,YACxBU,OAAOK,WACPJ,kBAAkBG,iBACpB;CACA,MAAME,kBAAkBhB,YACtBU,OAAOK,WACPJ,kBAAkBK,eACpB;CAEA,OAAO;EACLC,UAAUV,QAAQG,OAAOQ,SAAS,EAChCC,iBAAiBT,OAAOU,oBAC1B,CAAC;EACDC,wBAAwBd,QAAQG,OAAOY,WAAW,EAChDH,iBAAiBT,OAAOQ,QAC1B,CAAC;EACDK,QAAQhB,QAAQG,OAAOY,WAAW,EAAEH,iBAAiBT,OAAOQ,QAAQ,CAAC;EACrEM,gBAAgBjB,QAAQS,iBAAiB,EACvCG,iBAAiBL,kBACnB,CAAC;EACDW,cAAclB,QAAQG,OAAOgB,kBAAkB,EAC7CP,iBAAiBT,OAAOiB,iBAC1B,CAAC;EACDC,sBAAsBrB,QAAQG,OAAOY,WAAW,EAC9CH,iBAAiBT,OAAOQ,QAC1B,CAAC;EACDW,UAAUtB,QAAQG,OAAOgB,kBAAkB,EACzCI,aAAapB,OAAOqB,eACtB,CAAC;EACDC,kBAAkBzB,QAAQS,iBAAiB,EACzCc,aAAahB,kBACf,CAAC;EAGDmB,wBAAwB1B,QAAQG,OAAOwB,kBAAkB;GACvDf,iBAAiBT,OAAOyB;GACxBL,aAAa;EACf,CAAC;EAGDM,UAAU7B,QAAQG,OAAOgB,gBAAgB;EACzCW,wBAAwB9B,QAAQG,OAAOQ,OAAO;EAC9CoB,MAAM/B,QAAQG,OAAOQ,OAAO;EAG5BqB,cAAchC,QAAQS,eAAe;EACrCwB,OAAOjC,QAAQG,OAAO+B,sBAAsB,EAC1CtB,iBAAiBT,OAAOgC,mBAC1B,CAAC;EACDC,qBAAqBpC,QAAQG,OAAOkC,aAAa,EAC/CzB,iBAAiBT,OAAOmC,UAC1B,CAAC;CACH;AACF,CACF"}