@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
@@ -0,0 +1,350 @@
1
+ import { useTheme } from "../theme/scope.js";
2
+ import { styles, stylesOf } from "./styles.js";
3
+ import { c } from "react/compiler-runtime";
4
+ import { StyleSheet, View } from "react-native";
5
+ import { useRef, useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import Animated, { Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withDelay, withTiming } from "react-native-reanimated";
8
+ const INITIAL_ORIGIN_SCALE = .2;
9
+ const FADE_IN = 105;
10
+ const FADE_OUT = 375;
11
+ const NO_RIPPLE = {
12
+ end: {
13
+ x: 0,
14
+ y: 0
15
+ },
16
+ scale: 1,
17
+ size: 0,
18
+ start: {
19
+ x: 0,
20
+ y: 0
21
+ }
22
+ };
23
+ /**
24
+ * The `hitSlop` that brings a control smaller than 48dp up to a 48dp target,
25
+ * centred on it, as web's controls draw their target with a `::before`.
26
+ * Nothing for a control already that big. A width left out is taken to be
27
+ * wide enough, as a labelled button's is.
28
+ */
29
+ function hitSlop(height, width = 48) {
30
+ const vertical = Math.max(0, (48 - height) / 2);
31
+ const horizontal = Math.max(0, (48 - width) / 2);
32
+ if (vertical === 0 && horizontal === 0) return;
33
+ return {
34
+ bottom: vertical,
35
+ left: horizontal,
36
+ right: horizontal,
37
+ top: vertical
38
+ };
39
+ }
40
+ /**
41
+ * The press feedback every interactive control draws: the state layer, the
42
+ * ripple and the focus ring, as Material Design's state layers page sets them
43
+ * out.
44
+ *
45
+ * The state layer is the control's content colour at the state's opacity
46
+ * token, over the whole control: hover for an iPad pointer or an Android
47
+ * mouse, focus while the ring shows, pressed while pressed. React Native has
48
+ * no `color-mix()`, so it is a view of its own rather than a blended
49
+ * background.
50
+ *
51
+ * The ripple grows from the press point to cover the control and fades out
52
+ * once the press ends, held at least `durationMedium1` so a tap reads, and is
53
+ * drawn the same on iOS and Android rather than through Android's own
54
+ * `android_ripple`. It is a solid disc where web's has a soft edge, since
55
+ * React Native draws a gradient only through an experimental style that React
56
+ * Native Web ignores. It is not drawn while the device asks for reduced
57
+ * motion, where the pressed layer alone marks the press.
58
+ *
59
+ * A control whose layer is a disc of its own beside its label, as a
60
+ * checkbox's is, passes the disc's side as `disc`, and its ripple grows from
61
+ * the disc's centre instead. One whose pressed layer takes another colour
62
+ * passes it as `pressedColor`.
63
+ *
64
+ * The ring shows when focus arrives from a keyboard, and not when it arrives
65
+ * with a press, which on web is a click focusing what it presses.
66
+ *
67
+ * ```tsx
68
+ * const feedback = usePressFeedback({ color: colors.onPrimary, corners, onPressIn })
69
+ *
70
+ * <Pressable {...feedback.handlers} style={[styles.button, feedback.ring]}>
71
+ * {feedback.layer}
72
+ * <Text>Label</Text>
73
+ * </Pressable>
74
+ * ```
75
+ */
76
+ function usePressFeedback(t0) {
77
+ const $ = c(61);
78
+ const { color, corners, disabled, disableRipple: t1, disc, onBlur, onFocus, onHoverIn, onHoverOut, onPressIn, onPressOut, pressedColor: t2 } = t0;
79
+ const disableRipple = t1 === void 0 ? false : t1;
80
+ const pressedColor = t2 === void 0 ? color : t2;
81
+ const theme = useTheme();
82
+ const reduceMotion = useReducedMotion();
83
+ const [hovered, setHovered] = useState(false);
84
+ const [pressed, setPressed] = useState(false);
85
+ const [ringShown, setRingShown] = useState(false);
86
+ const focused = useRef(false);
87
+ const pressedUnfocused = useRef(false);
88
+ const pressStarted = useRef(0);
89
+ const phase = useRef("idle");
90
+ const geometry = useSharedValue(NO_RIPPLE);
91
+ const grow = useSharedValue(0);
92
+ const fade = useSharedValue(0);
93
+ const { motion, spacing, stateLayerOpacity } = theme;
94
+ const active = disabled !== true;
95
+ const rippled = active && !disableRipple && !reduceMotion;
96
+ const rippleMotion = useAnimatedStyle(() => {
97
+ "worklet";
98
+ const { end, scale, size, start } = geometry.get();
99
+ const progress = grow.get();
100
+ return {
101
+ backgroundColor: pressedColor,
102
+ borderRadius: size / 2,
103
+ height: size,
104
+ left: 0,
105
+ opacity: fade.get(),
106
+ position: "absolute",
107
+ top: 0,
108
+ transform: [
109
+ { translateX: start.x + (end.x - start.x) * progress },
110
+ { translateY: start.y + (end.y - start.y) * progress },
111
+ { scale: 1 + (scale - 1) * progress }
112
+ ],
113
+ width: size
114
+ };
115
+ });
116
+ let t3;
117
+ if ($[0] !== fade || $[1] !== geometry || $[2] !== grow || $[3] !== motion || $[4] !== spacing || $[5] !== stateLayerOpacity) {
118
+ t3 = function ripple(width, height, origin) {
119
+ const size_0 = Math.max(1, Math.floor(Math.max(width, height) * INITIAL_ORIGIN_SCALE));
120
+ geometry.set({
121
+ end: {
122
+ x: (width - size_0) / 2,
123
+ y: (height - size_0) / 2
124
+ },
125
+ scale: (Math.hypot(width, height) + spacing.sm) / size_0,
126
+ size: size_0,
127
+ start: {
128
+ x: origin.x - size_0 / 2,
129
+ y: origin.y - size_0 / 2
130
+ }
131
+ });
132
+ grow.set(0);
133
+ grow.set(withTiming(1, {
134
+ duration: motion.durationLong1,
135
+ easing: Easing.bezier(...motion.easingStandard)
136
+ }));
137
+ fade.set(withTiming(stateLayerOpacity.pressed, {
138
+ duration: FADE_IN,
139
+ easing: Easing.linear
140
+ }));
141
+ pressStarted.current = Date.now();
142
+ };
143
+ $[0] = fade;
144
+ $[1] = geometry;
145
+ $[2] = grow;
146
+ $[3] = motion;
147
+ $[4] = spacing;
148
+ $[5] = stateLayerOpacity;
149
+ $[6] = t3;
150
+ } else t3 = $[6];
151
+ const ripple = t3;
152
+ let t4;
153
+ if ($[7] !== fade || $[8] !== motion) {
154
+ t4 = function release() {
155
+ const shown = Date.now() - pressStarted.current;
156
+ fade.set(withDelay(Math.max(0, motion.durationMedium1 - shown), withTiming(0, {
157
+ duration: FADE_OUT,
158
+ easing: Easing.linear
159
+ })));
160
+ };
161
+ $[7] = fade;
162
+ $[8] = motion;
163
+ $[9] = t4;
164
+ } else t4 = $[9];
165
+ const release = t4;
166
+ let t5;
167
+ if ($[10] !== onBlur) {
168
+ t5 = (event) => {
169
+ focused.current = false;
170
+ setRingShown(false);
171
+ onBlur?.(event);
172
+ };
173
+ $[10] = onBlur;
174
+ $[11] = t5;
175
+ } else t5 = $[11];
176
+ let t6;
177
+ if ($[12] !== onFocus) {
178
+ t6 = (event_0) => {
179
+ focused.current = true;
180
+ setRingShown(!pressedUnfocused.current);
181
+ pressedUnfocused.current = false;
182
+ onFocus?.(event_0);
183
+ };
184
+ $[12] = onFocus;
185
+ $[13] = t6;
186
+ } else t6 = $[13];
187
+ let t7;
188
+ if ($[14] !== onHoverIn) {
189
+ t7 = (event_1) => {
190
+ setHovered(true);
191
+ onHoverIn?.(event_1);
192
+ };
193
+ $[14] = onHoverIn;
194
+ $[15] = t7;
195
+ } else t7 = $[15];
196
+ let t8;
197
+ if ($[16] !== onHoverOut) {
198
+ t8 = (event_2) => {
199
+ setHovered(false);
200
+ onHoverOut?.(event_2);
201
+ };
202
+ $[16] = onHoverOut;
203
+ $[17] = t8;
204
+ } else t8 = $[17];
205
+ let t9;
206
+ if ($[18] !== disc || $[19] !== onPressIn || $[20] !== release || $[21] !== ripple || $[22] !== rippled) {
207
+ t9 = (event_3) => {
208
+ pressedUnfocused.current = !focused.current;
209
+ setPressed(true);
210
+ if (rippled && disc !== void 0) {
211
+ ripple(disc, disc, {
212
+ x: disc / 2,
213
+ y: disc / 2
214
+ });
215
+ phase.current = "shown";
216
+ } else if (rippled) {
217
+ const { pageX, pageY } = event_3.nativeEvent;
218
+ phase.current = "measuring";
219
+ event_3.currentTarget.measure((_x, _y, width_0, height_0, left, top) => {
220
+ ripple(width_0, height_0, {
221
+ x: Number.isFinite(pageX - left) ? pageX - left : width_0 / 2,
222
+ y: Number.isFinite(pageY - top) ? pageY - top : height_0 / 2
223
+ });
224
+ if (phase.current === "released") {
225
+ release();
226
+ phase.current = "idle";
227
+ } else phase.current = "shown";
228
+ });
229
+ }
230
+ onPressIn?.(event_3);
231
+ };
232
+ $[18] = disc;
233
+ $[19] = onPressIn;
234
+ $[20] = release;
235
+ $[21] = ripple;
236
+ $[22] = rippled;
237
+ $[23] = t9;
238
+ } else t9 = $[23];
239
+ let t10;
240
+ if ($[24] !== onPressOut || $[25] !== release) {
241
+ t10 = (event_4) => {
242
+ pressedUnfocused.current = false;
243
+ setPressed(false);
244
+ if (phase.current === "shown") release();
245
+ phase.current = phase.current === "measuring" ? "released" : "idle";
246
+ onPressOut?.(event_4);
247
+ };
248
+ $[24] = onPressOut;
249
+ $[25] = release;
250
+ $[26] = t10;
251
+ } else t10 = $[26];
252
+ let t11;
253
+ if ($[27] !== t10 || $[28] !== t5 || $[29] !== t6 || $[30] !== t7 || $[31] !== t8 || $[32] !== t9) {
254
+ t11 = {
255
+ onBlur: t5,
256
+ onFocus: t6,
257
+ onHoverIn: t7,
258
+ onHoverOut: t8,
259
+ onPressIn: t9,
260
+ onPressOut: t10
261
+ };
262
+ $[27] = t10;
263
+ $[28] = t5;
264
+ $[29] = t6;
265
+ $[30] = t7;
266
+ $[31] = t8;
267
+ $[32] = t9;
268
+ $[33] = t11;
269
+ } else t11 = $[33];
270
+ const handlers = t11;
271
+ const layerOpacity = !active ? 0 : pressed ? stateLayerOpacity.pressed : ringShown ? stateLayerOpacity.focus : hovered ? stateLayerOpacity.hover : 0;
272
+ let t12;
273
+ if ($[34] !== corners) {
274
+ t12 = StyleSheet.compose(styles.surface, corners);
275
+ $[34] = corners;
276
+ $[35] = t12;
277
+ } else t12 = $[35];
278
+ const surface = t12;
279
+ let t13;
280
+ if ($[36] !== active || $[37] !== color || $[38] !== layerOpacity || $[39] !== pressed || $[40] !== pressedColor) {
281
+ t13 = StyleSheet.compose(styles.layer, {
282
+ backgroundColor: active && pressed ? pressedColor : color,
283
+ opacity: layerOpacity
284
+ });
285
+ $[36] = active;
286
+ $[37] = color;
287
+ $[38] = layerOpacity;
288
+ $[39] = pressed;
289
+ $[40] = pressedColor;
290
+ $[41] = t13;
291
+ } else t13 = $[41];
292
+ const state = t13;
293
+ const t14 = active && hovered;
294
+ let t15;
295
+ if ($[42] !== state) {
296
+ t15 = /* @__PURE__ */ jsx(View, { style: state });
297
+ $[42] = state;
298
+ $[43] = t15;
299
+ } else t15 = $[43];
300
+ let t16;
301
+ if ($[44] !== rippleMotion || $[45] !== rippled) {
302
+ t16 = rippled ? /* @__PURE__ */ jsx(Animated.View, { style: rippleMotion }) : null;
303
+ $[44] = rippleMotion;
304
+ $[45] = rippled;
305
+ $[46] = t16;
306
+ } else t16 = $[46];
307
+ let t17;
308
+ if ($[47] !== surface || $[48] !== t15 || $[49] !== t16) {
309
+ t17 = /* @__PURE__ */ jsxs(View, {
310
+ accessibilityElementsHidden: true,
311
+ importantForAccessibility: "no-hide-descendants",
312
+ style: surface,
313
+ children: [t15, t16]
314
+ });
315
+ $[47] = surface;
316
+ $[48] = t15;
317
+ $[49] = t16;
318
+ $[50] = t17;
319
+ } else t17 = $[50];
320
+ const t18 = active && pressed;
321
+ let t19;
322
+ if ($[51] !== active || $[52] !== ringShown || $[53] !== theme) {
323
+ t19 = active && ringShown ? stylesOf(theme).ring : void 0;
324
+ $[51] = active;
325
+ $[52] = ringShown;
326
+ $[53] = theme;
327
+ $[54] = t19;
328
+ } else t19 = $[54];
329
+ let t20;
330
+ if ($[55] !== handlers || $[56] !== t14 || $[57] !== t17 || $[58] !== t18 || $[59] !== t19) {
331
+ t20 = {
332
+ handlers,
333
+ hovered: t14,
334
+ layer: t17,
335
+ pressed: t18,
336
+ ring: t19
337
+ };
338
+ $[55] = handlers;
339
+ $[56] = t14;
340
+ $[57] = t17;
341
+ $[58] = t18;
342
+ $[59] = t19;
343
+ $[60] = t20;
344
+ } else t20 = $[60];
345
+ return t20;
346
+ }
347
+ //#endregion
348
+ export { hitSlop, usePressFeedback };
349
+
350
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["ReactNode","Insets","PressableProps","ViewStyle","useRef","useState","StyleSheet","View","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withDelay","withTiming","useTheme","styles","stylesOf","Corners","Pick","Handlers","Point","x","y","PressFeedback","handlers","NonNullable","Name","hovered","layer","pressed","ring","PressFeedbackOptions","color","corners","disabled","disableRipple","disc","pressedColor","RippleGeometry","end","scale","size","start","RipplePhase","TARGET_SIZE","INITIAL_ORIGIN_SCALE","FADE_IN","FADE_OUT","NO_RIPPLE","hitSlop","height","width","vertical","Math","max","horizontal","undefined","bottom","left","right","top","usePressFeedback","t0","$","_c","t1","onBlur","onFocus","onHoverIn","onHoverOut","onPressIn","onPressOut","t2","theme","reduceMotion","setHovered","setPressed","ringShown","setRingShown","focused","pressedUnfocused","pressStarted","phase","geometry","grow","fade","motion","spacing","stateLayerOpacity","active","rippled","rippleMotion","get","progress","backgroundColor","borderRadius","opacity","position","transform","translateX","translateY","t3","ripple","origin","size_0","floor","set","hypot","sm","duration","durationLong1","easing","bezier","easingStandard","linear","current","Date","now","t4","release","shown","durationMedium1","t5","event","t6","event_0","t7","event_1","t8","event_2","t9","event_3","pageX","pageY","nativeEvent","currentTarget","measure","_x","_y","width_0","height_0","Number","isFinite","t10","event_4","t11","layerOpacity","focus","hover","t12","compose","surface","t13","state","t14","t15","t16","t17","t18","t19","t20","_c"],"sources":["../../src/press/index.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { Insets, PressableProps, ViewStyle } from 'react-native'\n\nimport { useRef, useState } from 'react'\nimport { StyleSheet, View } from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withDelay,\n withTiming,\n} from 'react-native-reanimated'\n\nimport { useTheme } from '../theme/scope'\nimport { styles, stylesOf } from './styles'\n\n/** The corners a control is drawn with, which its feedback clips to. */\ntype Corners = Pick<\n ViewStyle,\n | 'borderBottomEndRadius'\n | 'borderBottomStartRadius'\n | 'borderRadius'\n | 'borderTopEndRadius'\n | 'borderTopStartRadius'\n>\n\n/** The handlers the feedback reads its states from. */\ntype Handlers = Pick<\n PressableProps,\n 'onBlur' | 'onFocus' | 'onHoverIn' | 'onHoverOut' | 'onPressIn' | 'onPressOut'\n>\n\ntype Point = { readonly x: number; readonly y: number }\n\ntype PressFeedback = {\n /**\n * The handlers to hand the control's `Pressable`. Each calls the one of\n * the same name it was given after its own.\n */\n readonly handlers: {\n readonly [Name in keyof Handlers]-?: NonNullable<Handlers[Name]>\n }\n /**\n * Whether a pointer is over the control, for a control that draws more\n * than the layer while hovered. Never while disabled.\n */\n readonly hovered: boolean\n /** The state layer and the ripple, to render as the `Pressable`'s first child. */\n readonly layer: ReactNode\n /**\n * Whether the control is pressed, for a control that draws more than the\n * layer while pressed. Never while disabled.\n */\n readonly pressed: boolean\n /** The focus ring while it shows, to compose into the control's style. */\n readonly ring: undefined | ViewStyle\n}\n\ntype PressFeedbackOptions = Handlers & {\n /**\n * The colour the layer and the ripple are drawn in: the theme's value for\n * the control's content colour role.\n */\n readonly color: string\n /** The control's corners, which the layer and the ripple clip to. */\n readonly corners?: Corners | undefined\n readonly disabled?: boolean | null | undefined\n /**\n * Draws no ripple and leaves the state layer, as `disableRipple` does on\n * web's Button.\n */\n readonly disableRipple?: boolean | undefined\n /**\n * The side of a round layer of a fixed size, for a control whose state\n * layer is a disc beside its label rather than the whole control: a\n * checkbox's. The ripple then grows from the disc's centre wherever the\n * press lands, and nothing is measured. Left out, the ripple grows from\n * the press point across the control.\n */\n readonly disc?: number | undefined\n /**\n * The colour the pressed layer and the ripple are drawn in, where it\n * differs from the hover and focus layers': a checkbox's swaps between\n * primary and on surface once pressed. Defaults to `color`.\n */\n readonly pressedColor?: string | undefined\n}\n\n/** Where the ripple starts and ends, and how far it grows, for one press. */\ntype RippleGeometry = {\n readonly end: Point\n readonly scale: number\n readonly size: number\n readonly start: Point\n}\n\n// Where the ripple of the latest press is: waiting on the control's\n// measurement, shown, or released before the measurement arrived.\ntype RipplePhase = 'idle' | 'measuring' | 'released' | 'shown'\n\n/**\n * The least a touch target measures each way, from Material Design's\n * accessibility guidance. A floor rather than a token, as web's `TARGET_SIZE`\n * is: a theme changes how big a control is drawn, never how small a target\n * may be.\n */\nconst TARGET_SIZE = 48\n\n// The ripple starts as a disc a fifth of the control's longer side, as on web.\nconst INITIAL_ORIGIN_SCALE = 0.2\n\n// The fade, in milliseconds: quick in and slow out, so a tap reads as a ripple\n// rather than a flicker. These are web's, and deliberately not motion tokens:\n// the two are tuned as a ratio to each other, which snapping either to the\n// scale would undo. Linear for the same reason, since a fade to and from a\n// state layer wants no acceleration.\nconst FADE_IN = 105\nconst FADE_OUT = 375\n\nconst NO_RIPPLE: RippleGeometry = {\n end: { x: 0, y: 0 },\n scale: 1,\n size: 0,\n start: { x: 0, y: 0 },\n}\n\n/**\n * The `hitSlop` that brings a control smaller than 48dp up to a 48dp target,\n * centred on it, as web's controls draw their target with a `::before`.\n * Nothing for a control already that big. A width left out is taken to be\n * wide enough, as a labelled button's is.\n */\nfunction hitSlop(height: number, width = TARGET_SIZE): Insets | undefined {\n const vertical = Math.max(0, (TARGET_SIZE - height) / 2)\n const horizontal = Math.max(0, (TARGET_SIZE - width) / 2)\n\n if (vertical === 0 && horizontal === 0) {\n return undefined\n }\n\n return {\n bottom: vertical,\n left: horizontal,\n right: horizontal,\n top: vertical,\n }\n}\n\n/**\n * The press feedback every interactive control draws: the state layer, the\n * ripple and the focus ring, as Material Design's state layers page sets them\n * out.\n *\n * The state layer is the control's content colour at the state's opacity\n * token, over the whole control: hover for an iPad pointer or an Android\n * mouse, focus while the ring shows, pressed while pressed. React Native has\n * no `color-mix()`, so it is a view of its own rather than a blended\n * background.\n *\n * The ripple grows from the press point to cover the control and fades out\n * once the press ends, held at least `durationMedium1` so a tap reads, and is\n * drawn the same on iOS and Android rather than through Android's own\n * `android_ripple`. It is a solid disc where web's has a soft edge, since\n * React Native draws a gradient only through an experimental style that React\n * Native Web ignores. It is not drawn while the device asks for reduced\n * motion, where the pressed layer alone marks the press.\n *\n * A control whose layer is a disc of its own beside its label, as a\n * checkbox's is, passes the disc's side as `disc`, and its ripple grows from\n * the disc's centre instead. One whose pressed layer takes another colour\n * passes it as `pressedColor`.\n *\n * The ring shows when focus arrives from a keyboard, and not when it arrives\n * with a press, which on web is a click focusing what it presses.\n *\n * ```tsx\n * const feedback = usePressFeedback({ color: colors.onPrimary, corners, onPressIn })\n *\n * <Pressable {...feedback.handlers} style={[styles.button, feedback.ring]}>\n * {feedback.layer}\n * <Text>Label</Text>\n * </Pressable>\n * ```\n */\nfunction usePressFeedback({\n color,\n corners,\n disabled,\n disableRipple = false,\n disc,\n onBlur,\n onFocus,\n onHoverIn,\n onHoverOut,\n onPressIn,\n onPressOut,\n pressedColor = color,\n}: PressFeedbackOptions): PressFeedback {\n const theme = useTheme()\n const reduceMotion = useReducedMotion()\n const [hovered, setHovered] = useState(false)\n const [pressed, setPressed] = useState(false)\n const [ringShown, setRingShown] = useState(false)\n const focused = useRef(false)\n // Whether the press in flight began while the control had no focus, which\n // makes the focus that follows it a pointer's.\n const pressedUnfocused = useRef(false)\n const pressStarted = useRef(0)\n const phase = useRef<RipplePhase>('idle')\n const geometry = useSharedValue(NO_RIPPLE)\n const grow = useSharedValue(0)\n const fade = useSharedValue(0)\n\n const { motion, spacing, stateLayerOpacity } = theme\n const active = disabled !== true\n const rippled = active && !disableRipple && !reduceMotion\n\n const rippleMotion = useAnimatedStyle(() => {\n 'worklet'\n const { end, scale, size, start } = geometry.get()\n const progress = grow.get()\n\n // The whole style is the animation's, so the view takes the one object it\n // hands back.\n return {\n backgroundColor: pressedColor,\n borderRadius: size / 2,\n height: size,\n left: 0,\n opacity: fade.get(),\n position: 'absolute',\n top: 0,\n transform: [\n { translateX: start.x + (end.x - start.x) * progress },\n { translateY: start.y + (end.y - start.y) * progress },\n { scale: 1 + (scale - 1) * progress },\n ],\n width: size,\n }\n })\n\n // Grows a disc a fifth of the control's longer side from the press point\n // to the centre, until it covers the control and `spacing.sm` beyond its\n // corners, as web's does.\n function ripple(width: number, height: number, origin: Point) {\n const size = Math.max(\n 1,\n Math.floor(Math.max(width, height) * INITIAL_ORIGIN_SCALE),\n )\n\n geometry.set({\n end: { x: (width - size) / 2, y: (height - size) / 2 },\n scale: (Math.hypot(width, height) + spacing.sm) / size,\n size,\n start: { x: origin.x - size / 2, y: origin.y - size / 2 },\n })\n grow.set(0)\n grow.set(\n withTiming(1, {\n duration: motion.durationLong1,\n easing: Easing.bezier(...motion.easingStandard),\n }),\n )\n fade.set(\n withTiming(stateLayerOpacity.pressed, {\n duration: FADE_IN,\n easing: Easing.linear,\n }),\n )\n pressStarted.current = Date.now()\n }\n\n // Fades the ripple out, once it has been shown for `durationMedium1`.\n function release() {\n const shown = Date.now() - pressStarted.current\n\n fade.set(\n withDelay(\n Math.max(0, motion.durationMedium1 - shown),\n withTiming(0, { duration: FADE_OUT, easing: Easing.linear }),\n ),\n )\n }\n\n const handlers: PressFeedback['handlers'] = {\n onBlur: (event) => {\n focused.current = false\n setRingShown(false)\n onBlur?.(event)\n },\n onFocus: (event) => {\n focused.current = true\n setRingShown(!pressedUnfocused.current)\n pressedUnfocused.current = false\n onFocus?.(event)\n },\n onHoverIn: (event) => {\n setHovered(true)\n onHoverIn?.(event)\n },\n onHoverOut: (event) => {\n setHovered(false)\n onHoverOut?.(event)\n },\n onPressIn: (event) => {\n pressedUnfocused.current = !focused.current\n setPressed(true)\n\n if (rippled && disc !== undefined) {\n ripple(disc, disc, { x: disc / 2, y: disc / 2 })\n phase.current = 'shown'\n } else if (rippled) {\n const { pageX, pageY } = event.nativeEvent\n\n // A touch's own location is measured from whichever child it landed\n // on, so the point is taken from the page and the control measured\n // there. A press from a key carries no point, and ripples from the\n // centre. React Native Web measures a frame later, so a press can end\n // first, and its ripple is then drawn whole.\n phase.current = 'measuring'\n event.currentTarget.measure((_x, _y, width, height, left, top) => {\n ripple(width, height, {\n x: Number.isFinite(pageX - left) ? pageX - left : width / 2,\n y: Number.isFinite(pageY - top) ? pageY - top : height / 2,\n })\n\n if (phase.current === 'released') {\n release()\n phase.current = 'idle'\n } else {\n phase.current = 'shown'\n }\n })\n }\n\n onPressIn?.(event)\n },\n onPressOut: (event) => {\n pressedUnfocused.current = false\n setPressed(false)\n\n if (phase.current === 'shown') {\n release()\n }\n phase.current = phase.current === 'measuring' ? 'released' : 'idle'\n\n onPressOut?.(event)\n },\n }\n\n const layerOpacity = !active\n ? 0\n : pressed\n ? stateLayerOpacity.pressed\n : ringShown\n ? stateLayerOpacity.focus\n : hovered\n ? stateLayerOpacity.hover\n : 0\n const surface = StyleSheet.compose(styles.surface, corners)\n const state = StyleSheet.compose(styles.layer, {\n backgroundColor: active && pressed ? pressedColor : color,\n opacity: layerOpacity,\n })\n\n return {\n handlers,\n hovered: active && hovered,\n layer: (\n <View\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n style={surface}\n >\n <View style={state} />\n {rippled ? <Animated.View style={rippleMotion} /> : null}\n </View>\n ),\n pressed: active && pressed,\n ring: active && ringShown ? stylesOf(theme).ring : undefined,\n }\n}\n\nexport { hitSlop, TARGET_SIZE, usePressFeedback }\nexport type { Corners, PressFeedback, PressFeedbackOptions }\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { themedStyles } from "../theme/styles.js";
2
+ import { StyleSheet } from "react-native";
3
+ //#region src/press/styles.ts
4
+ const stylesOf = themedStyles(({ colors }) => StyleSheet.create({ ring: {
5
+ outlineColor: colors.primary,
6
+ outlineOffset: 2,
7
+ outlineStyle: "solid",
8
+ outlineWidth: 2
9
+ } }));
10
+ const styles = StyleSheet.create({
11
+ layer: {
12
+ bottom: 0,
13
+ left: 0,
14
+ position: "absolute",
15
+ right: 0,
16
+ top: 0
17
+ },
18
+ surface: {
19
+ bottom: 0,
20
+ left: 0,
21
+ overflow: "hidden",
22
+ pointerEvents: "none",
23
+ position: "absolute",
24
+ right: 0,
25
+ top: 0
26
+ }
27
+ });
28
+ //#endregion
29
+ export { styles, stylesOf };
30
+
31
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","names":["StyleSheet","themedStyles","stylesOf","colors","create","ring","outlineColor","primary","outlineOffset","outlineStyle","outlineWidth","styles","layer","bottom","left","position","right","top","surface","overflow","pointerEvents"],"sources":["../../src/press/styles.ts"],"sourcesContent":["import { StyleSheet } from 'react-native'\n\nimport { themedStyles } from '../theme/styles'\n\n// The ring is web's `focus.ring`: 2dp wide, 2dp outside the control, in\n// primary. An outline rather than a border, so showing it moves nothing, and\n// rather than a shadow, which a high-contrast mode drops.\nconst stylesOf = themedStyles(({ colors }) =>\n StyleSheet.create({\n ring: {\n outlineColor: colors.primary,\n outlineOffset: 2,\n outlineStyle: 'solid',\n outlineWidth: 2,\n },\n }),\n)\n\nconst styles = StyleSheet.create({\n // The state layer fills the surface. Its colour and opacity are the\n // control's and the state's, so they are set where it renders.\n layer: {\n bottom: 0,\n left: 0,\n position: 'absolute',\n right: 0,\n top: 0,\n },\n // Fills the control, clips to the corners it is given, and takes no\n // touches, so a press lands on the control under it.\n surface: {\n bottom: 0,\n left: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\nexport { styles, stylesOf }\n"],"mappings":";;;AAOA,MAAME,WAAWD,cAAc,EAAEE,aAC/BH,WAAWI,OAAO,EAChBC,MAAM;CACJC,cAAcH,OAAOI;CACrBC,eAAe;CACfC,cAAc;CACdC,cAAc;AAChB,EACF,CAAC,CACH;AAEA,MAAMC,SAASX,WAAWI,OAAO;CAG/BQ,OAAO;EACLC,QAAQ;EACRC,MAAM;EACNC,UAAU;EACVC,OAAO;EACPC,KAAK;CACP;CAGAC,SAAS;EACPL,QAAQ;EACRC,MAAM;EACNK,UAAU;EACVC,eAAe;EACfL,UAAU;EACVC,OAAO;EACPC,KAAK;CACP;AACF,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/router/index.d.ts
3
+ type Navigate = (href: string) => void;
4
+ type RouterProviderProps = {
5
+ readonly children?: ReactNode;
6
+ /** Takes the app to the screen a pressed link's `href` names. */
7
+ readonly navigate: Navigate;
8
+ };
9
+ /**
10
+ * Sends the `href` of every link the components draw through the app's own
11
+ * router, as React Aria's `RouterProvider` does for kanso-ui on web, so a
12
+ * component with an `href` moves between screens rather than leaving the app.
13
+ *
14
+ * It takes `navigate` alone. React Aria's also takes `useHref`, which turns an
15
+ * `href` into the one a rendered `<a>` carries, and native renders no anchor.
16
+ *
17
+ * ```tsx
18
+ * const router = useRouter()
19
+ *
20
+ * <RouterProvider navigate={(href) => router.push(href)}>
21
+ * <Stack />
22
+ * </RouterProvider>
23
+ * ```
24
+ */
25
+ declare function RouterProvider({ children, navigate }: RouterProviderProps): import("react").JSX.Element;
26
+ //#endregion
27
+ export { RouterProvider, type RouterProviderProps };
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,82 @@
1
+ import { c } from "react/compiler-runtime";
2
+ import { Linking } from "react-native";
3
+ import { createContext, use } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/router/index.tsx
6
+ const SCHEME = /^[a-z][\d+.a-z-]*:/iu;
7
+ const RouterContext = createContext(null);
8
+ async function openExternally(href) {
9
+ try {
10
+ await Linking.openURL(href);
11
+ } catch (error) {
12
+ console.warn(`Could not open ${href}`, error);
13
+ }
14
+ }
15
+ /**
16
+ * Sends the `href` of every link the components draw through the app's own
17
+ * router, as React Aria's `RouterProvider` does for kanso-ui on web, so a
18
+ * component with an `href` moves between screens rather than leaving the app.
19
+ *
20
+ * It takes `navigate` alone. React Aria's also takes `useHref`, which turns an
21
+ * `href` into the one a rendered `<a>` carries, and native renders no anchor.
22
+ *
23
+ * ```tsx
24
+ * const router = useRouter()
25
+ *
26
+ * <RouterProvider navigate={(href) => router.push(href)}>
27
+ * <Stack />
28
+ * </RouterProvider>
29
+ * ```
30
+ */
31
+ function RouterProvider(t0) {
32
+ const $ = c(3);
33
+ const { children, navigate } = t0;
34
+ let t1;
35
+ if ($[0] !== children || $[1] !== navigate) {
36
+ t1 = /* @__PURE__ */ jsx(RouterContext, {
37
+ value: navigate,
38
+ children
39
+ });
40
+ $[0] = children;
41
+ $[1] = navigate;
42
+ $[2] = t1;
43
+ } else t1 = $[2];
44
+ return t1;
45
+ }
46
+ /**
47
+ * The press handler of a component that leads somewhere: the call site's own
48
+ * `onPress`, then the `href` through `useOpenLink`, which is the order web
49
+ * reports them in. Without an `href` it calls `onPress` alone.
50
+ */
51
+ function useLinkPress(href, onPress) {
52
+ const $ = c(4);
53
+ const openLink = useOpenLink();
54
+ let t0;
55
+ if ($[0] !== href || $[1] !== onPress || $[2] !== openLink) {
56
+ t0 = (event) => {
57
+ onPress?.(event);
58
+ if (href !== void 0) openLink(href);
59
+ };
60
+ $[0] = href;
61
+ $[1] = onPress;
62
+ $[2] = openLink;
63
+ $[3] = t0;
64
+ } else t0 = $[3];
65
+ return t0;
66
+ }
67
+ /**
68
+ * The handler every component with an `href` presses through. Under a
69
+ * `RouterProvider` it hands the `href` to `navigate`; without one, or for a
70
+ * URL with a scheme of its own, it asks the platform to open it.
71
+ */
72
+ function useOpenLink() {
73
+ const navigate = use(RouterContext);
74
+ return (href) => {
75
+ if (navigate === null || SCHEME.test(href)) openExternally(href);
76
+ else navigate(href);
77
+ };
78
+ }
79
+ //#endregion
80
+ export { RouterProvider, useLinkPress, useOpenLink };
81
+
82
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["ReactNode","createContext","use","Linking","SCHEME","Navigate","href","RouterContext","RouterProviderProps","children","navigate","openExternally","openURL","error","console","warn","RouterProvider","t0","$","_c","t1","useLinkPress","onPress","openLink","useOpenLink","event","undefined","test","_c"],"sources":["../../src/router/index.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nimport { createContext, use } from 'react'\nimport { Linking } from 'react-native'\n\n// A URL with a scheme of its own, such as `https:`, `mailto:`, `tel:` or an\n// app's deep link, is the platform's to open rather than the app router's.\nconst SCHEME = /^[a-z][\\d+.a-z-]*:/iu\n\ntype Navigate = (href: string) => void\n\nconst RouterContext = createContext<Navigate | null>(null)\n\ntype RouterProviderProps = {\n readonly children?: ReactNode\n /** Takes the app to the screen a pressed link's `href` names. */\n readonly navigate: Navigate\n}\n\n// `openURL` rejects when nothing on the device handles the URL, and a press\n// has nobody to hand that to, so it is reported here rather than left\n// unhandled.\nasync function openExternally(href: string) {\n try {\n await Linking.openURL(href)\n } catch (error: unknown) {\n console.warn(`Could not open ${href}`, error)\n }\n}\n\n/**\n * Sends the `href` of every link the components draw through the app's own\n * router, as React Aria's `RouterProvider` does for kanso-ui on web, so a\n * component with an `href` moves between screens rather than leaving the app.\n *\n * It takes `navigate` alone. React Aria's also takes `useHref`, which turns an\n * `href` into the one a rendered `<a>` carries, and native renders no anchor.\n *\n * ```tsx\n * const router = useRouter()\n *\n * <RouterProvider navigate={(href) => router.push(href)}>\n * <Stack />\n * </RouterProvider>\n * ```\n */\nfunction RouterProvider({ children, navigate }: RouterProviderProps) {\n return <RouterContext value={navigate}>{children}</RouterContext>\n}\n\n/**\n * The press handler of a component that leads somewhere: the call site's own\n * `onPress`, then the `href` through `useOpenLink`, which is the order web\n * reports them in. Without an `href` it calls `onPress` alone.\n */\nfunction useLinkPress<Event>(\n href: string | undefined,\n onPress: ((event: Event) => void) | null | undefined,\n): (event: Event) => void {\n const openLink = useOpenLink()\n return (event) => {\n onPress?.(event)\n\n if (href !== undefined) {\n openLink(href)\n }\n }\n}\n\n/**\n * The handler every component with an `href` presses through. Under a\n * `RouterProvider` it hands the `href` to `navigate`; without one, or for a\n * URL with a scheme of its own, it asks the platform to open it.\n */\nfunction useOpenLink(): Navigate {\n const navigate = use(RouterContext)\n return (href) => {\n if (navigate === null || SCHEME.test(href)) {\n void openExternally(href)\n } else {\n navigate(href)\n }\n }\n}\n\nexport { RouterProvider, useLinkPress, useOpenLink }\nexport type { RouterProviderProps }\n"],"mappings":";;;;;AAOA,MAAMI,SAAS;AAIf,MAAMG,gBAAgBN,cAA+B,IAAI;AAWzD,eAAeU,eAAeL,MAAc;CAC1C,IAAI;EACF,MAAMH,QAAQS,QAAQN,IAAI;CAC5B,SAASO,OAAgB;EACvBC,QAAQC,KAAK,kBAAkBT,QAAQO,KAAK;CAC9C;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAAG,eAAAC,IAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAwB,MAAA,EAAAV,UAAAC,aAAAO;CAA2C,IAAAG;CAAA,IAAAF,EAAA,OAAAT,YAAAS,EAAA,OAAAR,UAAA;EAC1DU,KAAA,oBAAC,eAAD;GAAsBV,OAAAA;GAAWD;EAAnB,CAAA;EAA4CS,EAAA,KAAAT;EAAAS,EAAA,KAAAR;EAAAQ,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAA,OAA1DE;AAA0D;;;;;;AAQnE,SAAAC,aAAAf,MAAAgB,SAAA;CAAA,MAAAJ,IAAAC,EAAA,CAAA;CAIE,MAAAI,WAAiBC,YAAY;CAAC,IAAAP;CAAA,IAAAC,EAAA,OAAAZ,QAAAY,EAAA,OAAAI,WAAAJ,EAAA,OAAAK,UAAA;EACvBN,MAAAQ,UAAA;GACLH,UAAUG,KAAK;GAEf,IAAInB,SAASoB,KAAAA,GACXH,SAASjB,IAAI;EACd;EACFY,EAAA,KAAAZ;EAAAY,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;EAAAL,EAAA,KAAAD;CAAA,OAAAA,KAAAC,EAAA;CAAA,OANMD;AAMN;;;;;;AAQH,SAASO,cAAwB;CAC/B,MAAMd,WAAWR,IAAIK,aAAa;CAClC,QAAQD,SAAS;EACf,IAAII,aAAa,QAAQN,OAAOuB,KAAKrB,IAAI,GACvC,eAAoBA,IAAI;OAExBI,SAASJ,IAAI;CAEjB;AACF"}
@@ -0,0 +1,25 @@
1
+ import { processColor } from "react-native";
2
+ //#region src/theme/color.ts
3
+ /**
4
+ * A colour at a fraction of its own opacity, for the roles Material Design
5
+ * gives as a colour at an opacity: a disabled container is on-surface at the
6
+ * `disabledContainer` opacity, and its label on-surface at `disabledContent`.
7
+ * Web reaches those through `color-mix()`, which React Native has no
8
+ * counterpart of, so the colour is read into its channels here and written
9
+ * back as `rgba()` with the alpha scaled.
10
+ *
11
+ * Any colour React Native reads is taken, a hex, a function or a name, and
12
+ * one with an alpha of its own keeps it, scaled. A colour React Native cannot
13
+ * read is handed back as it was.
14
+ */
15
+ function withOpacity(color, opacity) {
16
+ const processed = processColor(color);
17
+ if (typeof processed !== "number") return color;
18
+ const argb = processed >>> 0;
19
+ const alpha = (argb >>> 24) / 255 * opacity;
20
+ return `rgba(${String(argb >>> 16 & 255)}, ${String(argb >>> 8 & 255)}, ${String(argb & 255)}, ${String(Math.round(alpha * 1e3) / 1e3)})`;
21
+ }
22
+ //#endregion
23
+ export { withOpacity };
24
+
25
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","names":["processColor","withOpacity","color","opacity","processed","argb","alpha","String","Math","round"],"sources":["../../src/theme/color.ts"],"sourcesContent":["import { processColor } from 'react-native'\n\n/**\n * A colour at a fraction of its own opacity, for the roles Material Design\n * gives as a colour at an opacity: a disabled container is on-surface at the\n * `disabledContainer` opacity, and its label on-surface at `disabledContent`.\n * Web reaches those through `color-mix()`, which React Native has no\n * counterpart of, so the colour is read into its channels here and written\n * back as `rgba()` with the alpha scaled.\n *\n * Any colour React Native reads is taken, a hex, a function or a name, and\n * one with an alpha of its own keeps it, scaled. A colour React Native cannot\n * read is handed back as it was.\n */\nfunction withOpacity(color: string, opacity: number): string {\n const processed = processColor(color)\n\n if (typeof processed !== 'number') {\n return color\n }\n\n // `processColor` hands back the channels as ARGB, unsigned on iOS and on\n // the web and signed on Android, which the shift makes unsigned.\n const argb = processed >>> 0\n const alpha = ((argb >>> 24) / 255) * opacity\n\n return `rgba(${String((argb >>> 16) & 255)}, ${String((argb >>> 8) & 255)}, ${String(argb & 255)}, ${String(Math.round(alpha * 1000) / 1000)})`\n}\n\nexport { withOpacity }\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAASC,YAAYC,OAAeC,SAAyB;CAC3D,MAAMC,YAAYJ,aAAaE,KAAK;CAEpC,IAAI,OAAOE,cAAc,UACvB,OAAOF;CAKT,MAAMG,OAAOD,cAAc;CAC3B,MAAME,SAAUD,SAAS,MAAM,MAAOF;CAEtC,OAAO,QAAQI,OAAQF,SAAS,KAAM,GAAG,EAAC,IAAKE,OAAQF,SAAS,IAAK,GAAG,EAAC,IAAKE,OAAOF,OAAO,GAAG,EAAC,IAAKE,OAAOC,KAAKC,MAAMH,QAAQ,GAAI,IAAI,GAAI,EAAC;AAC9I"}
@@ -0,0 +1,6 @@
1
+ //#region src/theme/resolve.d.ts
2
+ /** A cubic Bézier curve's control points, as `cubic-bezier()` lists them. */
3
+ type Easing = readonly [x1: number, y1: number, x2: number, y2: number];
4
+ //#endregion
5
+ export type { Easing };
6
+ //# sourceMappingURL=resolve.d.ts.map