@xaui/native 0.9.1-alpha.10 → 0.9.1-alpha.12

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.
@@ -1,29 +1,13 @@
1
1
  import {
2
2
  ThemeContext,
3
3
  alpha,
4
- mix
5
- } from "./chunk-X2K2FX3W.js";
4
+ mix,
5
+ stableHash
6
+ } from "./chunk-EMXBFHIP.js";
6
7
 
7
8
  // src/provider/xaui-provider.tsx
8
9
  import { useColorScheme } from "react-native";
9
10
 
10
- // src/utils/stable-hash.ts
11
- function canonical(value) {
12
- if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
13
- if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
14
- const entries = Object.entries(value).filter(([, v]) => typeof v !== "function").sort(([a], [b]) => a < b ? -1 : 1).map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`);
15
- return `{${entries.join(",")}}`;
16
- }
17
- function stableHash(value) {
18
- const input = canonical(value);
19
- let hash = 2166136261;
20
- for (let i = 0; i < input.length; i++) {
21
- hash ^= input.charCodeAt(i);
22
- hash = Math.imul(hash, 16777619) >>> 0;
23
- }
24
- return hash.toString(36);
25
- }
26
-
27
11
  // src/theme/derive-colors.ts
28
12
  function deriveColors(s) {
29
13
  return {
@@ -0,0 +1,545 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ function _interopRequireDefault(obj) {
7
+ return obj && obj.__esModule ? obj : {
8
+ default: obj
9
+ };
10
+ }
11
+ function _nullishCoalesce(lhs, rhsFn) {
12
+ if (lhs != null) {
13
+ return lhs;
14
+ } else {
15
+ return rhsFn();
16
+ }
17
+ }
18
+ function _optionalChain(ops) {
19
+ let lastAccessLHS = undefined;
20
+ let value = ops[0];
21
+ let i = 1;
22
+ while (i < ops.length) {
23
+ const op = ops[i];
24
+ const fn = ops[i + 1];
25
+ i += 2;
26
+ if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) {
27
+ return undefined;
28
+ }
29
+ if (op === 'access' || op === 'optionalAccess') {
30
+ lastAccessLHS = value;
31
+ value = fn(value);
32
+ } else if (op === 'call' || op === 'optionalCall') {
33
+ value = fn((...args) => value.call(lastAccessLHS, ...args));
34
+ lastAccessLHS = undefined;
35
+ }
36
+ }
37
+ return value;
38
+ }
39
+ var _chunkGZIOB63Ocjs = require('./chunk-GZIOB63O.cjs');
40
+ var _chunkFDUI4IAMcjs = require('./chunk-FDUI4IAM.cjs');
41
+
42
+ // src/components/button/button.context.ts
43
+ var [ButtonProvider, useButton] = _chunkGZIOB63Ocjs.createSlotContext.call(void 0, "Button");
44
+
45
+ // src/components/button/button-icon.tsx
46
+ var _jsxruntime = require('react/jsx-runtime');
47
+ function ButtonIcon({
48
+ size,
49
+ color,
50
+ ...rest
51
+ }) {
52
+ const {
53
+ icon
54
+ } = useButton();
55
+ return /* @__PURE__ */_jsxruntime.jsx.call(void 0, _chunkGZIOB63Ocjs.Icon, {
56
+ size: _nullishCoalesce(size, () => icon.size),
57
+ color: _nullishCoalesce(color, () => icon.color),
58
+ ...rest
59
+ });
60
+ }
61
+ ButtonIcon.displayName = "XAUI.Button.Icon";
62
+
63
+ // src/components/button/button-label.tsx
64
+ var _react = require('react');
65
+ var _reactnative = require('react-native');
66
+ var ButtonLabel = _react.forwardRef.call(void 0, function ButtonLabel2({
67
+ children,
68
+ style,
69
+ numberOfLines = 1,
70
+ ...props
71
+ }, ref) {
72
+ const {
73
+ labelStyle
74
+ } = useButton();
75
+ const [styleProps, rest] = _chunkGZIOB63Ocjs.useStyleProps.call(void 0, props);
76
+ return /* @__PURE__ */_jsxruntime.jsx.call(void 0, _reactnative.Text, {
77
+ ref,
78
+ numberOfLines,
79
+ style: [labelStyle, styleProps, style],
80
+ ...rest,
81
+ children
82
+ });
83
+ });
84
+ ButtonLabel.displayName = "XAUI.Button.Label";
85
+
86
+ // src/components/button/button-spinner.tsx
87
+
88
+ var _reactnativereanimated = require('react-native-reanimated');
89
+ var _reactnativereanimated2 = _interopRequireDefault(_reactnativereanimated);
90
+ var ROTATION_DURATION = 800;
91
+ function ButtonSpinner({
92
+ style,
93
+ animation = true,
94
+ ...props
95
+ }) {
96
+ const {
97
+ spinnerStyle
98
+ } = useButton();
99
+ const [styleProps] = _chunkGZIOB63Ocjs.useStyleProps.call(void 0, props);
100
+ const ringStyle = [spinnerStyle, styleProps, style];
101
+ if (!animation) return /* @__PURE__ */_jsxruntime.jsx.call(void 0, _reactnative.View, {
102
+ style: ringStyle
103
+ });
104
+ return /* @__PURE__ */_jsxruntime.jsx.call(void 0, SpinningRing, {
105
+ style: ringStyle
106
+ });
107
+ }
108
+ ButtonSpinner.displayName = "XAUI.Button.Spinner";
109
+ const _worklet_5280721983457_init_data = {
110
+ code: "function chunkCTULMYVICjs1(){const{angle}=this.__closure;return{transform:[{rotate:angle.value+\"deg\"}]};}",
111
+ location: "/home/runner/work/xaui/xaui/packages/native/dist/chunk-CTULMYVI.cjs",
112
+ sourceMap: "{\"version\":3,\"names\":[\"chunkCTULMYVICjs1\",\"angle\",\"__closure\",\"transform\",\"rotate\",\"value\"],\"sources\":[\"/home/runner/work/xaui/xaui/packages/native/dist/chunk-CTULMYVI.cjs\"],\"mappings\":\"AA0E6E,SAAAA,iBAAMA,CAAA,QAAAC,KAAA,OAAAC,SAAA,CAE/E,MAAO,CAAEC,SAAS,CAAE,CAAC,CAAEC,MAAM,CAAKH,KAAK,CAACI,KAAK,MAAM,CAAC,CAAE,CAAC,CACzD\",\"ignoreList\":[]}"
113
+ };
114
+ function SpinningRing({
115
+ style
116
+ }) {
117
+ const angle = _reactnativereanimated.useSharedValue.call(void 0, 0);
118
+ _react.useEffect.call(void 0, () => {
119
+ angle.value = _reactnativereanimated.withRepeat.call(void 0, _reactnativereanimated.withTiming.call(void 0, 360, {
120
+ duration: ROTATION_DURATION,
121
+ easing: _reactnativereanimated.Easing.linear
122
+ }), -1, false);
123
+ return () => _reactnativereanimated.cancelAnimation.call(void 0, angle);
124
+ }, [angle]);
125
+ const animatedStyle = _reactnativereanimated.useAnimatedStyle.call(void 0, function chunkCTULMYVICjs1Factory({
126
+ _worklet_5280721983457_init_data,
127
+ angle
128
+ }) {
129
+ const _e = [new global.Error(), -2, -27];
130
+ const chunkCTULMYVICjs1 = function () {
131
+ return {
132
+ transform: [{
133
+ rotate: `${angle.value}deg`
134
+ }]
135
+ };
136
+ };
137
+ chunkCTULMYVICjs1.__closure = {
138
+ angle
139
+ };
140
+ chunkCTULMYVICjs1.__workletHash = 5280721983457;
141
+ chunkCTULMYVICjs1.__pluginVersion = "0.7.4";
142
+ chunkCTULMYVICjs1.__initData = _worklet_5280721983457_init_data;
143
+ chunkCTULMYVICjs1.__stackDetails = _e;
144
+ return chunkCTULMYVICjs1;
145
+ }({
146
+ _worklet_5280721983457_init_data,
147
+ angle
148
+ }), [angle]);
149
+ return /* @__PURE__ */_jsxruntime.jsx.call(void 0, _reactnativereanimated2.default.View, {
150
+ style: [style, animatedStyle]
151
+ });
152
+ }
153
+
154
+ // src/components/button/button.tsx
155
+
156
+ // src/hooks/use-press-state.ts
157
+
158
+ function usePressState(handlers = {}) {
159
+ const [isPressed, setIsPressed] = _react.useState.call(void 0, false);
160
+ const latest = _react.useRef.call(void 0, handlers);
161
+ latest.current = handlers;
162
+ const onPressIn = _react.useCallback.call(void 0, event => {
163
+ setIsPressed(true);
164
+ _optionalChain([latest, 'access', _ => _.current, 'access', _2 => _2.onPressIn, 'optionalCall', _3 => _3(event)]);
165
+ }, []);
166
+ const onPressOut = _react.useCallback.call(void 0, event => {
167
+ setIsPressed(false);
168
+ _optionalChain([latest, 'access', _4 => _4.current, 'access', _5 => _5.onPressOut, 'optionalCall', _6 => _6(event)]);
169
+ }, []);
170
+ const press = _react.useRef.call(void 0, {
171
+ onPressIn,
172
+ onPressOut
173
+ }).current;
174
+ return [isPressed, press];
175
+ }
176
+
177
+ // src/utils/warn-dev.ts
178
+ function warnDev(message) {
179
+ if (typeof __DEV__ !== "undefined" && !__DEV__) return;
180
+ console.warn(`XAUI: ${message}`);
181
+ }
182
+
183
+ // src/components/button/button.recipe.ts
184
+ var SLOTS = ["root", "label", "icon", "spinner"];
185
+ var VARIANT_TOKENS = {
186
+ primary: {
187
+ bg: "accent",
188
+ bgPressed: "accentPressed",
189
+ fg: "accentForeground"
190
+ },
191
+ secondary: {
192
+ bg: "default",
193
+ bgPressed: "defaultPressed",
194
+ fg: "defaultForeground"
195
+ },
196
+ tertiary: {
197
+ border: "border",
198
+ bgPressed: "defaultSoftPressed",
199
+ fg: "foreground"
200
+ },
201
+ ghost: {
202
+ bgPressed: "defaultSoftPressed",
203
+ fg: "foreground"
204
+ },
205
+ success: {
206
+ bg: "success",
207
+ bgPressed: "successPressed",
208
+ fg: "successForeground"
209
+ },
210
+ "success-soft": {
211
+ bg: "successSoft",
212
+ bgPressed: "successSoftPressed",
213
+ fg: "successSoftForeground"
214
+ },
215
+ warning: {
216
+ bg: "warning",
217
+ bgPressed: "warningPressed",
218
+ fg: "warningForeground"
219
+ },
220
+ "warning-soft": {
221
+ bg: "warningSoft",
222
+ bgPressed: "warningSoftPressed",
223
+ fg: "warningSoftForeground"
224
+ },
225
+ danger: {
226
+ bg: "danger",
227
+ bgPressed: "dangerPressed",
228
+ fg: "dangerForeground"
229
+ },
230
+ "danger-soft": {
231
+ bg: "dangerSoft",
232
+ bgPressed: "dangerSoftPressed",
233
+ fg: "dangerSoftForeground"
234
+ }
235
+ };
236
+ function ring(theme, size) {
237
+ return {
238
+ width: size,
239
+ height: size,
240
+ borderRadius: size / 2,
241
+ borderWidth: theme.borderWidth.default * 2,
242
+ borderTopColor: "transparent"
243
+ };
244
+ }
245
+ function sizeAxis(step) {
246
+ const {
247
+ size,
248
+ padding,
249
+ gap,
250
+ glyph,
251
+ radius
252
+ } = step;
253
+ return theme => ({
254
+ root: {
255
+ height: theme.controlHeights[size],
256
+ paddingHorizontal: theme.spacing(padding),
257
+ gap: theme.spacing(gap),
258
+ borderRadius: theme.radius[radius]
259
+ },
260
+ label: {
261
+ fontSize: theme.fontSizes[size],
262
+ lineHeight: theme.lineHeights[size]
263
+ },
264
+ icon: {
265
+ fontSize: theme.fontSizes[glyph]
266
+ },
267
+ spinner: ring(theme, theme.fontSizes[glyph])
268
+ });
269
+ }
270
+ var buttonRecipe = _chunkGZIOB63Ocjs.createRecipe.call(void 0, {
271
+ slots: SLOTS,
272
+ base: theme => ({
273
+ root: {
274
+ flexDirection: "row",
275
+ alignItems: "center",
276
+ justifyContent: "center",
277
+ borderWidth: 0,
278
+ // iOS's squircle. Free on Android, and it is what makes a large radius read as a
279
+ // shape rather than as two arcs meeting a straight edge.
280
+ borderCurve: "continuous"
281
+ },
282
+ label: {
283
+ fontFamily: theme.fontFamilies.body,
284
+ fontWeight: theme.fontWeights.medium
285
+ }
286
+ }),
287
+ variantTokens: VARIANT_TOKENS,
288
+ /**
289
+ * Where the variant's colours land, for every variant at once. The border width follows
290
+ * the *presence* of the border role rather than a per-variant flag — `tertiary` is the
291
+ * only variant that names one, and that fact is already in the table above.
292
+ */
293
+ paint: (theme, colors) => ({
294
+ root: {
295
+ backgroundColor: colors.bg,
296
+ borderColor: colors.border,
297
+ borderWidth: colors.border ? theme.borderWidth.default : 0
298
+ },
299
+ label: {
300
+ color: colors.fg
301
+ },
302
+ icon: {
303
+ color: colors.fg
304
+ },
305
+ spinner: {
306
+ borderColor: colors.fg
307
+ }
308
+ }),
309
+ /**
310
+ * Declaration order is application order: `radius` overrides the radius `size` set, and
311
+ * `isIconOnly` overrides its padding. Both are axes rather than a static sheet merged
312
+ * at the call site, so they stay inside the cache key and a press still allocates
313
+ * nothing.
314
+ */
315
+ variants: {
316
+ size: {
317
+ xs: sizeAxis({
318
+ size: "xs",
319
+ padding: 3,
320
+ gap: 1,
321
+ glyph: "sm",
322
+ radius: "3xl"
323
+ }),
324
+ sm: sizeAxis({
325
+ size: "sm",
326
+ padding: 3.5,
327
+ gap: 1.5,
328
+ glyph: "md",
329
+ radius: "3xl"
330
+ }),
331
+ md: sizeAxis({
332
+ size: "md",
333
+ padding: 4,
334
+ gap: 2,
335
+ glyph: "lg",
336
+ radius: "3xl"
337
+ }),
338
+ lg: sizeAxis({
339
+ size: "lg",
340
+ padding: 5,
341
+ gap: 2.5,
342
+ glyph: "xl",
343
+ radius: "4xl"
344
+ })
345
+ },
346
+ radius: {
347
+ xs: t => ({
348
+ root: {
349
+ borderRadius: t.radius.xs
350
+ }
351
+ }),
352
+ sm: t => ({
353
+ root: {
354
+ borderRadius: t.radius.sm
355
+ }
356
+ }),
357
+ md: t => ({
358
+ root: {
359
+ borderRadius: t.radius.md
360
+ }
361
+ }),
362
+ lg: t => ({
363
+ root: {
364
+ borderRadius: t.radius.lg
365
+ }
366
+ }),
367
+ xl: t => ({
368
+ root: {
369
+ borderRadius: t.radius.xl
370
+ }
371
+ }),
372
+ "2xl": t => ({
373
+ root: {
374
+ borderRadius: t.radius["2xl"]
375
+ }
376
+ }),
377
+ "3xl": t => ({
378
+ root: {
379
+ borderRadius: t.radius["3xl"]
380
+ }
381
+ }),
382
+ "4xl": t => ({
383
+ root: {
384
+ borderRadius: t.radius["4xl"]
385
+ }
386
+ }),
387
+ field: t => ({
388
+ root: {
389
+ borderRadius: t.radius.field
390
+ }
391
+ }),
392
+ full: t => ({
393
+ root: {
394
+ borderRadius: t.radius.full
395
+ }
396
+ })
397
+ },
398
+ // A square on a fixed height. No width is computed, and none needs to be.
399
+ isIconOnly: {
400
+ true: () => ({
401
+ root: {
402
+ paddingHorizontal: 0,
403
+ aspectRatio: 1
404
+ }
405
+ })
406
+ }
407
+ },
408
+ /**
409
+ * The pressed colour lives here rather than in a `PressableFeedback.Highlight`, because
410
+ * a control picks one treatment or the other — both, and a pressed button darkens
411
+ * twice. This one is the variant's own `…Pressed` token, so the press reads as the
412
+ * button's colour going down rather than as a neutral film over it, and a tinted button
413
+ * presses through the same OKLab formula as a token one.
414
+ */
415
+ states: {
416
+ pressed: (_theme, colors) => ({
417
+ root: {
418
+ backgroundColor: colors.bgPressed
419
+ }
420
+ }),
421
+ disabled: theme => ({
422
+ root: {
423
+ opacity: theme.opacity.disabled
424
+ }
425
+ })
426
+ },
427
+ defaultVariants: {
428
+ variant: "primary",
429
+ size: "md"
430
+ }
431
+ });
432
+
433
+ // src/components/button/button.utils.ts
434
+
435
+ function containsElementOfType(children, type) {
436
+ return _react.Children.toArray(children).some(child => _react.isValidElement.call(void 0, child) && child.type === type);
437
+ }
438
+
439
+ // src/components/button/button.tsx
440
+
441
+ var ButtonRoot = _react.forwardRef.call(void 0, function Button({
442
+ children,
443
+ variant,
444
+ size,
445
+ radius,
446
+ color,
447
+ isDisabled = false,
448
+ isLoading = false,
449
+ isIconOnly = false,
450
+ asChild = false,
451
+ accessibilityRole = "button",
452
+ accessibilityState,
453
+ style,
454
+ onPressIn,
455
+ onPressOut,
456
+ ...props
457
+ }, ref) {
458
+ const theme = _chunkFDUI4IAMcjs.useXAUITheme.call(void 0);
459
+ const [styleProps, rest] = _chunkGZIOB63Ocjs.useStyleProps.call(void 0, props);
460
+ const [isPressed, press] = usePressState({
461
+ onPressIn,
462
+ onPressOut
463
+ });
464
+ const selection = {
465
+ variant,
466
+ size,
467
+ radius,
468
+ isIconOnly: isIconOnly ? "true" : void 0
469
+ };
470
+ const states = {
471
+ pressed: isPressed,
472
+ disabled: isDisabled || isLoading
473
+ };
474
+ const styles = buttonRecipe.resolve({
475
+ theme,
476
+ selection,
477
+ states
478
+ });
479
+ const tint = color ? buttonRecipe.tint({
480
+ theme,
481
+ color,
482
+ selection,
483
+ states
484
+ }) : void 0;
485
+ const context = _react.useMemo.call(void 0, () => {
486
+ const icon = _reactnative.StyleSheet.flatten([styles.icon, _optionalChain([tint, 'optionalAccess', _7 => _7.icon])]);
487
+ return {
488
+ labelStyle: tint ? [styles.label, tint.label] : styles.label,
489
+ spinnerStyle: tint ? [styles.spinner, tint.spinner] : styles.spinner,
490
+ icon: {
491
+ size: icon.fontSize,
492
+ // `ColorValue` also covers the platform's opaque colours, which `Icon` cannot
493
+ // hand to a third-party component expecting a string.
494
+ color: typeof icon.color === "string" ? icon.color : void 0
495
+ },
496
+ isDisabled,
497
+ isLoading
498
+ };
499
+ }, [styles, tint, isDisabled, isLoading]);
500
+ const rootStyle = [styles.root, _optionalChain([tint, 'optionalAccess', _8 => _8.root]), styleProps, typeof style === "function" ? style({
501
+ pressed: isPressed
502
+ }) : style];
503
+ const text = _chunkGZIOB63Ocjs.childrenToString.call(void 0, children);
504
+ const showSpinner = isLoading && !containsElementOfType(children, ButtonSpinner);
505
+ if (isIconOnly && !rest.accessibilityLabel && !rest["aria-label"]) {
506
+ warnDev("Button: an icon-only button needs an `accessibilityLabel` \u2014 there is no text for a screen reader to read, so it announces as an unlabelled button.");
507
+ }
508
+ return /* @__PURE__ */_jsxruntime.jsx.call(void 0, ButtonProvider, {
509
+ value: context,
510
+ children: /* @__PURE__ */_jsxruntime.jsx.call(void 0, _chunkGZIOB63Ocjs.PressableFeedback, {
511
+ ref,
512
+ isPressed,
513
+ isDisabled: isDisabled || isLoading,
514
+ asChild,
515
+ accessibilityRole,
516
+ accessibilityState: {
517
+ disabled: isDisabled,
518
+ busy: isLoading,
519
+ ...accessibilityState
520
+ },
521
+ ...rest,
522
+ style: rootStyle,
523
+ onPressIn: press.onPressIn,
524
+ onPressOut: press.onPressOut,
525
+ children: asChild ? children : /* @__PURE__ */_jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, {
526
+ children: [showSpinner ? /* @__PURE__ */_jsxruntime.jsx.call(void 0, ButtonSpinner, {}) : null, text !== null ? /* @__PURE__ */_jsxruntime.jsx.call(void 0, ButtonLabel, {
527
+ children: text
528
+ }) : children]
529
+ })
530
+ })
531
+ });
532
+ });
533
+ ButtonRoot.displayName = "XAUI.Button.Root";
534
+
535
+ // src/components/button/index.ts
536
+ var Button2 = Object.assign(ButtonRoot, {
537
+ Label: ButtonLabel,
538
+ Icon: ButtonIcon,
539
+ Spinner: ButtonSpinner
540
+ });
541
+ exports.useButton = useButton;
542
+ exports.usePressState = usePressState;
543
+ exports.warnDev = warnDev;
544
+ exports.buttonRecipe = buttonRecipe;
545
+ exports.Button = Button2;
@@ -114,12 +114,32 @@ function deriveTint(tint, theme) {
114
114
  return derived;
115
115
  }
116
116
 
117
+ // src/utils/stable-hash.ts
118
+ function canonical(value) {
119
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
120
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
121
+ const entries = Object.entries(value).filter(([, v]) => typeof v !== "function").sort(([a], [b]) => a < b ? -1 : 1).map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`);
122
+ return `{${entries.join(",")}}`;
123
+ }
124
+ function stableHash(value) {
125
+ const input = canonical(value);
126
+ let hash = 2166136261;
127
+ for (let i = 0; i < input.length; i++) {
128
+ hash ^= input.charCodeAt(i);
129
+ hash = Math.imul(hash, 16777619) >>> 0;
130
+ }
131
+ return hash.toString(36);
132
+ }
133
+
117
134
  export {
118
135
  ThemeContext,
119
136
  useXAUITheme,
120
137
  useColorMode,
121
138
  useThemeColor,
139
+ stableHash,
140
+ isHex,
122
141
  mix,
123
142
  alpha,
143
+ contrastOn,
124
144
  deriveTint
125
145
  };