alouette 20.2.0 → 20.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/bin/install-skills.mjs +72 -0
  2. package/dist/definitions/animationDurationsMs.d.ts +3 -0
  3. package/dist/definitions/animationDurationsMs.d.ts.map +1 -1
  4. package/dist/definitions/core/useColorToken.d.ts +9 -0
  5. package/dist/definitions/core/useColorToken.d.ts.map +1 -0
  6. package/dist/definitions/index.d.ts +8 -0
  7. package/dist/definitions/index.d.ts.map +1 -1
  8. package/dist/definitions/ui/containers/AlertDialog.d.ts +78 -0
  9. package/dist/definitions/ui/containers/AlertDialog.d.ts.map +1 -0
  10. package/dist/definitions/ui/containers/AlertDialog.stories.d.ts +33 -0
  11. package/dist/definitions/ui/containers/AlertDialog.stories.d.ts.map +1 -0
  12. package/dist/definitions/ui/containers/Modal.d.ts +66 -0
  13. package/dist/definitions/ui/containers/Modal.d.ts.map +1 -0
  14. package/dist/definitions/ui/containers/Modal.stories.d.ts +31 -0
  15. package/dist/definitions/ui/containers/Modal.stories.d.ts.map +1 -0
  16. package/dist/definitions/ui/feedback/CircularProgress.d.ts +21 -0
  17. package/dist/definitions/ui/feedback/CircularProgress.d.ts.map +1 -0
  18. package/dist/definitions/ui/feedback/CircularProgress.stories.d.ts +38 -0
  19. package/dist/definitions/ui/feedback/CircularProgress.stories.d.ts.map +1 -0
  20. package/dist/definitions/ui/feedback/LinearProgress.d.ts +21 -0
  21. package/dist/definitions/ui/feedback/LinearProgress.d.ts.map +1 -0
  22. package/dist/definitions/ui/feedback/LinearProgress.stories.d.ts +38 -0
  23. package/dist/definitions/ui/feedback/LinearProgress.stories.d.ts.map +1 -0
  24. package/dist/definitions/ui/feedback/RingCircle.d.ts +16 -0
  25. package/dist/definitions/ui/feedback/RingCircle.d.ts.map +1 -0
  26. package/dist/definitions/ui/feedback/RingCircle.web.d.ts +4 -0
  27. package/dist/definitions/ui/feedback/RingCircle.web.d.ts.map +1 -0
  28. package/dist/definitions/ui/feedback/useSimulatedProgress.d.ts +5 -0
  29. package/dist/definitions/ui/feedback/useSimulatedProgress.d.ts.map +1 -0
  30. package/dist/definitions/ui/primitives/Icon.d.ts +2 -1
  31. package/dist/definitions/ui/primitives/Icon.d.ts.map +1 -1
  32. package/dist/definitions/ui/story-components/IndeterminateToggleDemo.d.ts +7 -0
  33. package/dist/definitions/ui/story-components/IndeterminateToggleDemo.d.ts.map +1 -0
  34. package/dist/index-browser.es.js +380 -32
  35. package/dist/index-browser.es.js.map +1 -1
  36. package/dist/index-node22.cjs +411 -28
  37. package/dist/index-node22.cjs.map +1 -1
  38. package/dist/index-node22.mjs +407 -32
  39. package/dist/index-node22.mjs.map +1 -1
  40. package/dist/index-react-native.cjs.js +411 -28
  41. package/dist/index-react-native.cjs.js.map +1 -1
  42. package/dist/index-react-native.es.js +407 -32
  43. package/dist/index-react-native.es.js.map +1 -1
  44. package/package.json +6 -2
  45. package/src/animationDurationsMs.ts +4 -1
  46. package/src/config/defaultColorScales.ts +1 -1
  47. package/src/core/useColorToken.ts +31 -0
  48. package/src/global.css +21 -12
  49. package/src/index.ts +25 -0
  50. package/src/themeVariables.ts +6 -6
  51. package/src/ui/containers/AlertDialog.stories.tsx +425 -0
  52. package/src/ui/containers/AlertDialog.tsx +211 -0
  53. package/src/ui/containers/Box.tsx +1 -1
  54. package/src/ui/containers/Modal.stories.tsx +231 -0
  55. package/src/ui/containers/Modal.tsx +197 -0
  56. package/src/ui/feedback/CircularProgress.stories.tsx +100 -0
  57. package/src/ui/feedback/CircularProgress.tsx +118 -0
  58. package/src/ui/feedback/ConnectionState.tsx +2 -2
  59. package/src/ui/feedback/LinearProgress.stories.tsx +101 -0
  60. package/src/ui/feedback/LinearProgress.tsx +77 -0
  61. package/src/ui/feedback/RingCircle.tsx +83 -0
  62. package/src/ui/feedback/RingCircle.web.tsx +39 -0
  63. package/src/ui/feedback/useSimulatedProgress.ts +66 -0
  64. package/src/ui/inputs/InputText.tsx +1 -1
  65. package/src/ui/inputs/Select.shared.tsx +1 -1
  66. package/src/ui/inputs/Select.web.tsx +2 -2
  67. package/src/ui/inputs/Switch.web.tsx +2 -2
  68. package/src/ui/primitives/Icon.tsx +4 -22
  69. package/src/ui/story-components/IndeterminateToggleDemo.tsx +29 -0
@@ -1,16 +1,19 @@
1
1
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
2
  import { VariableContextProvider, styled as styled$1 } from 'nativewind';
3
- import { createContext, useContext, forwardRef, Children, cloneElement, Fragment, useRef, useState, useEffect, isValidElement, useCallback } from 'react';
4
- import { useColorScheme, View as View$1, Text as Text$1, ScrollView as ScrollView$1, FlatList as FlatList$1, SectionList as SectionList$1, Pressable, Platform, TextInput, Switch as Switch$1, useWindowDimensions, Modal, Linking } from 'react-native';
3
+ import { createContext, useContext, forwardRef, Children, cloneElement, Fragment, useRef, useState, useEffect, isValidElement, useId, useCallback } from 'react';
4
+ import { useColorScheme, View as View$1, Text as Text$1, ScrollView as ScrollView$1, FlatList as FlatList$1, SectionList as SectionList$1, Pressable, Platform, useWindowDimensions, Modal as Modal$1, TextInput, Switch as Switch$1, Linking } from 'react-native';
5
5
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  export { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
7
7
  import { extendTailwindMerge, twMerge as twMerge$1 } from 'tailwind-merge';
8
8
  import { tv } from 'tailwind-variants';
9
+ import { XRegularIcon } from 'alouette-icons/phosphor-icons/XRegularIcon';
9
10
  import { CheckRegularIcon } from 'alouette-icons/phosphor-icons/CheckRegularIcon';
10
- import { CaretDownRegularIcon } from 'alouette-icons/phosphor-icons/CaretDownRegularIcon';
11
11
  import { InfoRegularIcon } from 'alouette-icons/phosphor-icons/InfoRegularIcon';
12
+ import { QuestionRegularIcon } from 'alouette-icons/phosphor-icons/QuestionRegularIcon';
12
13
  import { WarningRegularIcon } from 'alouette-icons/phosphor-icons/WarningRegularIcon';
13
- import { XRegularIcon } from 'alouette-icons/phosphor-icons/XRegularIcon';
14
+ import { CaretDownRegularIcon } from 'alouette-icons/phosphor-icons/CaretDownRegularIcon';
15
+ import Animated, { Easing, useSharedValue, withTiming, useAnimatedProps } from 'react-native-reanimated';
16
+ import { Circle, Svg } from 'react-native-svg';
14
17
  import { CaretRightRegularIcon } from 'alouette-icons/phosphor-icons/CaretRightRegularIcon';
15
18
  import * as WebBrowser from 'expo-web-browser';
16
19
  import { WebBrowserPresentationStyle } from 'expo-web-browser';
@@ -39,7 +42,7 @@ const themeVariables = {
39
42
  "--color-interactive-accent-contained-bg-disabled": "#EBEBEB",
40
43
  "--color-interactive-accent-outlined-disabled": "#B8B8B8",
41
44
  "--color-screen": "#EBEBEB",
42
- "--color-surface": "#F5F5F5",
45
+ "--color-surface": "#FAFAFA",
43
46
  "--color-highlight": "#FFFFFF",
44
47
  "--color-enabled": "#C7C7C7",
45
48
  "--color-highlight-accent": "#E0E0E0",
@@ -50,8 +53,8 @@ const themeVariables = {
50
53
  "--color-border-muted": "#8F8F8F",
51
54
  "--color-border-sharp": "#616161",
52
55
  "--color-interactive-contained-pressable": "#FFFFFF",
53
- "--color-interactive-contained-hover": "#F5F5F5",
54
- "--color-interactive-contained-focus": "#F5F5F5",
56
+ "--color-interactive-contained-hover": "#FAFAFA",
57
+ "--color-interactive-contained-focus": "#FAFAFA",
55
58
  "--color-interactive-contained-active": "#EBEBEB",
56
59
  "--color-interactive-outlined-pressable": "#616161",
57
60
  "--color-interactive-outlined-hover": "#8F8F8F",
@@ -68,9 +71,9 @@ const themeVariables = {
68
71
  "--color-on-accent-muted": "#616161",
69
72
  "--color-selection": "#47474740",
70
73
  "--color-interactive-accent-contained-bg": "#EBEBEB",
71
- "--color-interactive-accent-contained-bg-hover": "#F5F5F5",
72
- "--color-interactive-accent-contained-bg-focus": "#F5F5F5",
73
- "--color-interactive-accent-contained-bg-active": "#F5F5F5"
74
+ "--color-interactive-accent-contained-bg-hover": "#FAFAFA",
75
+ "--color-interactive-accent-contained-bg-focus": "#FAFAFA",
76
+ "--color-interactive-accent-contained-bg-active": "#FAFAFA"
74
77
  },
75
78
  "dark": {
76
79
  "--color-translucent": "#1f1e1e55",
@@ -777,7 +780,7 @@ const interactiveBoxVariants = tv({
777
780
  base: [
778
781
  boxBaseClasses,
779
782
  "cursor-pointer",
780
- "transition-[transform,background-color,border-color] duration-200 ease-in",
783
+ "transition-[transform,background-color,border-color] duration-fast ease-in",
781
784
  "disabled:cursor-not-allowed disabled:opacity-70 aria-disabled:cursor-not-allowed aria-disabled:opacity-70",
782
785
  "active:scale-[0.975]"
783
786
  ].join(" "),
@@ -1243,23 +1246,12 @@ function PresenceOne({
1243
1246
 
1244
1247
  const animationDurationsMs = {
1245
1248
  "slide": 600,
1246
- "collapse": 800
1249
+ "collapse": 800,
1250
+ "progress": 300,
1251
+ "fade": 300,
1252
+ "fast": 200
1247
1253
  };
1248
1254
 
1249
- function Icon({
1250
- icon,
1251
- size = 20,
1252
- className = "text-sharp"
1253
- }) {
1254
- const token = className.split(/\s+/).find((part) => part.startsWith("text-"))?.slice("text-".length);
1255
- const color = useThemeToken(`--color-${token ?? "sharp"}`);
1256
- return cloneElement(icon, {
1257
- color,
1258
- width: size,
1259
- height: size
1260
- });
1261
- }
1262
-
1263
1255
  const pressableBoxVariants = tv(
1264
1256
  {
1265
1257
  extend: interactiveBoxVariants,
@@ -1391,6 +1383,24 @@ const PressableBox = forwardRef(
1391
1383
  }
1392
1384
  );
1393
1385
 
1386
+ function useColorToken(className) {
1387
+ const token = className.split(/\s+/).find((part) => part.startsWith("text-"))?.slice("text-".length);
1388
+ return useThemeToken(`--color-${token ?? "sharp"}`);
1389
+ }
1390
+
1391
+ function Icon({
1392
+ icon,
1393
+ size = 20,
1394
+ className = "text-sharp"
1395
+ }) {
1396
+ const color = useColorToken(className);
1397
+ return cloneElement(icon, {
1398
+ color,
1399
+ width: size,
1400
+ height: size
1401
+ });
1402
+ }
1403
+
1394
1404
  const buttonHeight = {
1395
1405
  sm: 38,
1396
1406
  md: 44
@@ -1602,12 +1612,231 @@ function IconButton({
1602
1612
  );
1603
1613
  }
1604
1614
 
1615
+ const panelVariants = tv(
1616
+ {
1617
+ // w-full so the panel shrinks on small screens (the backdrop padding keeps a
1618
+ // margin); max-w caps it on wide viewports.
1619
+ base: "w-full",
1620
+ variants: {
1621
+ size: {
1622
+ sm: "max-w-[360px]",
1623
+ md: "max-w-[520px]",
1624
+ lg: "max-w-[720px]"
1625
+ }
1626
+ },
1627
+ defaultVariants: { size: "md" }
1628
+ },
1629
+ { twMerge: false }
1630
+ );
1631
+ const titleReserveVariants = tv(
1632
+ {
1633
+ variants: {
1634
+ size: {
1635
+ sm: "pr-xxl",
1636
+ md: "pr-xl",
1637
+ lg: "pr-xl"
1638
+ }
1639
+ },
1640
+ defaultVariants: { size: "md" }
1641
+ },
1642
+ { twMerge: false }
1643
+ );
1644
+ function Modal({
1645
+ visible,
1646
+ onClose,
1647
+ children,
1648
+ icon,
1649
+ footer,
1650
+ accent,
1651
+ size = "md",
1652
+ title,
1653
+ hideCloseButton = false,
1654
+ closeButtonAriaLabel = "Close",
1655
+ role = "dialog",
1656
+ "aria-describedby": ariaDescribedby,
1657
+ testID,
1658
+ "aria-label": ariaLabel
1659
+ }) {
1660
+ const { height: windowHeight } = useWindowDimensions();
1661
+ const titleId = useId();
1662
+ return /* @__PURE__ */ jsx(AccentScope, { accent, children: /* @__PURE__ */ jsx(
1663
+ Modal$1,
1664
+ {
1665
+ transparent: true,
1666
+ visible,
1667
+ animationType: "fade",
1668
+ onRequestClose: onClose,
1669
+ children: /* @__PURE__ */ jsxs(View, { className: "flex-1 flex-center p-l", children: [
1670
+ /* @__PURE__ */ jsx(
1671
+ Pressable,
1672
+ {
1673
+ "aria-hidden": true,
1674
+ focusable: false,
1675
+ className: "absolute inset-0 bg-translucent",
1676
+ onPress: onClose
1677
+ }
1678
+ ),
1679
+ /* @__PURE__ */ jsx(
1680
+ View,
1681
+ {
1682
+ "aria-modal": true,
1683
+ role,
1684
+ "aria-label": title === void 0 ? ariaLabel : void 0,
1685
+ "aria-labelledby": title === void 0 ? void 0 : titleId,
1686
+ "aria-describedby": ariaDescribedby,
1687
+ testID,
1688
+ className: `relative ${panelVariants({ size })}`,
1689
+ children: /* @__PURE__ */ jsxs(
1690
+ Surface,
1691
+ {
1692
+ variant: "highlight",
1693
+ size,
1694
+ shadow: "l",
1695
+ className: "relative gap-m",
1696
+ children: [
1697
+ title === void 0 && icon === void 0 ? null : /* @__PURE__ */ jsxs(
1698
+ HStack,
1699
+ {
1700
+ className: `items-center gap-xs ${hideCloseButton ? "" : titleReserveVariants({ size })}`,
1701
+ children: [
1702
+ icon === void 0 ? null : /* @__PURE__ */ jsx(Icon, { icon, size: 24, className: "text-accent" }),
1703
+ title === void 0 ? null : /* @__PURE__ */ jsx(
1704
+ Text,
1705
+ {
1706
+ nativeID: titleId,
1707
+ className: "shrink font-heading-bold text-xl leading-tight text-sharp",
1708
+ children: title
1709
+ }
1710
+ )
1711
+ ]
1712
+ }
1713
+ ),
1714
+ hideCloseButton ? null : /* @__PURE__ */ jsx(
1715
+ IconButton,
1716
+ {
1717
+ icon: /* @__PURE__ */ jsx(XRegularIcon, {}),
1718
+ variant: "ghost",
1719
+ size: size === "lg" ? "md" : size,
1720
+ "aria-label": closeButtonAriaLabel,
1721
+ className: "absolute right-sm top-sm",
1722
+ onPress: onClose
1723
+ }
1724
+ ),
1725
+ /* @__PURE__ */ jsx(ScrollView, { style: { maxHeight: windowHeight * 0.7 }, children: /* @__PURE__ */ jsx(VStack, { className: "gap-m", children }) }),
1726
+ footer === void 0 ? null : /* @__PURE__ */ jsx(HStack, { className: "items-center justify-end gap-m", children: footer })
1727
+ ]
1728
+ }
1729
+ )
1730
+ }
1731
+ )
1732
+ ] })
1733
+ }
1734
+ ) });
1735
+ }
1736
+
1737
+ function resolveVariant(props, accent) {
1738
+ switch (props.variant) {
1739
+ case "alert": {
1740
+ const { onClose, closeText } = props;
1741
+ return {
1742
+ onDismiss: onClose,
1743
+ footer: /* @__PURE__ */ jsx(Button, { accent, text: closeText ?? "OK", onPress: onClose })
1744
+ };
1745
+ }
1746
+ case "required": {
1747
+ const { onConfirm, confirmText, confirmDisabled } = props;
1748
+ return {
1749
+ // Non-dismissible: only the explicit action closes it.
1750
+ onDismiss: () => void 0,
1751
+ footer: /* @__PURE__ */ jsx(
1752
+ Button,
1753
+ {
1754
+ accent,
1755
+ text: confirmText ?? "OK",
1756
+ disabled: confirmDisabled,
1757
+ onPress: onConfirm
1758
+ }
1759
+ )
1760
+ };
1761
+ }
1762
+ case "confirm":
1763
+ case void 0:
1764
+ default: {
1765
+ const { onConfirm, onCancel, confirmText, cancelText, confirmDisabled } = props;
1766
+ return {
1767
+ onDismiss: onCancel,
1768
+ footer: /* @__PURE__ */ jsxs(Fragment$1, { children: [
1769
+ /* @__PURE__ */ jsx(
1770
+ Button,
1771
+ {
1772
+ variant: "outlined",
1773
+ text: cancelText ?? "Cancel",
1774
+ onPress: onCancel
1775
+ }
1776
+ ),
1777
+ /* @__PURE__ */ jsx(
1778
+ Button,
1779
+ {
1780
+ accent,
1781
+ text: confirmText ?? "Confirm",
1782
+ disabled: confirmDisabled,
1783
+ onPress: onConfirm
1784
+ }
1785
+ )
1786
+ ] })
1787
+ };
1788
+ }
1789
+ }
1790
+ }
1791
+ function AlertDialog(props) {
1792
+ const {
1793
+ visible,
1794
+ title,
1795
+ children,
1796
+ accent = "danger",
1797
+ icon,
1798
+ size = "md",
1799
+ testID
1800
+ } = props;
1801
+ const descriptionId = useId();
1802
+ const { footer, onDismiss } = resolveVariant(props, accent);
1803
+ return /* @__PURE__ */ jsx(
1804
+ Modal,
1805
+ {
1806
+ hideCloseButton: true,
1807
+ visible,
1808
+ role: "alertdialog",
1809
+ accent,
1810
+ size,
1811
+ title,
1812
+ icon,
1813
+ "aria-describedby": children === void 0 ? void 0 : descriptionId,
1814
+ testID,
1815
+ footer,
1816
+ onClose: onDismiss,
1817
+ children: children === void 0 ? null : /* @__PURE__ */ jsx(Text, { nativeID: descriptionId, className: "text-base text-muted", children })
1818
+ }
1819
+ );
1820
+ }
1821
+ function QuestionAlertDialog(props) {
1822
+ return /* @__PURE__ */ jsx(AlertDialog, { ...props, icon: /* @__PURE__ */ jsx(QuestionRegularIcon, {}) });
1823
+ }
1824
+ function WarningAlertDialog(props) {
1825
+ return /* @__PURE__ */ jsx(AlertDialog, { ...props, icon: /* @__PURE__ */ jsx(WarningRegularIcon, {}) });
1826
+ }
1827
+ function InfoAlertDialog(props) {
1828
+ return /* @__PURE__ */ jsx(AlertDialog, { ...props, icon: /* @__PURE__ */ jsx(InfoRegularIcon, {}) });
1829
+ }
1830
+ function SuccessAlertDialog(props) {
1831
+ return /* @__PURE__ */ jsx(AlertDialog, { ...props, icon: /* @__PURE__ */ jsx(CheckRegularIcon, {}) });
1832
+ }
1833
+
1605
1834
  const inputVariants = tv(
1606
1835
  {
1607
1836
  base: [
1608
1837
  "bg-highlight text-base text-sharp",
1609
1838
  "border",
1610
- "transition-[border-color,background-color,outline-color] duration-200 ease-in",
1839
+ "transition-[border-color,background-color,outline-color] duration-fast ease-in",
1611
1840
  "outline-interactive-outlined-pressable",
1612
1841
  // to have proper outline color transition
1613
1842
  process.env.EXPO_PUBLIC_STORYBOOK_ENABLED ? "" : "border-interactive-outlined-pressable",
@@ -1758,7 +1987,7 @@ function useControllableValue(controlled, defaultValue, onValueChange) {
1758
1987
  const selectTriggerBaseClassName = [
1759
1988
  "flex-row items-center justify-between gap-xs",
1760
1989
  "rounded-md border px-m py-xs min-h-[44px]",
1761
- "transition-[border-color,outline-color,background-color] duration-200 ease-in"
1990
+ "transition-[border-color,outline-color,background-color] duration-fast ease-in"
1762
1991
  ].join(" ");
1763
1992
  const triggerLabelVariants = tv({
1764
1993
  base: "flex-1 text-base",
@@ -1916,7 +2145,7 @@ function SelectInner({
1916
2145
  }
1917
2146
  ),
1918
2147
  /* @__PURE__ */ jsx(
1919
- Modal,
2148
+ Modal$1,
1920
2149
  {
1921
2150
  transparent: true,
1922
2151
  visible: open,
@@ -2032,8 +2261,154 @@ function ConnectionState({
2032
2261
  return /* @__PURE__ */ jsx(AccentScope, { accent, children: /* @__PURE__ */ jsx(
2033
2262
  View,
2034
2263
  {
2035
- className: `absolute inset-x-0 top-0 z-9 h-0.5 bg-interactive-contained-pressable shadow-m transition-transform duration-700 ease-in-out ${hidden ? "-translate-y-6" : "translate-y-0"}`,
2036
- children: state ? /* @__PURE__ */ jsx(Text, { className: "absolute left-1/2 top-0.5 h-5.5 w-50 -translate-x-1/2 rounded-b-sm bg-interactive-contained-pressable text-center leading-5.5 text-on-accent transition-colors duration-200", children }) : null
2264
+ className: `absolute inset-x-0 top-0 z-9 h-0.5 bg-interactive-contained-pressable shadow-m transition-transform duration-slide ease-in-out ${hidden ? "-translate-y-6" : "translate-y-0"}`,
2265
+ children: state ? /* @__PURE__ */ jsx(Text, { className: "absolute left-1/2 top-0.5 h-5.5 w-50 -translate-x-1/2 rounded-b-sm bg-interactive-contained-pressable text-center leading-5.5 text-on-accent transition-colors duration-fast", children }) : null
2266
+ }
2267
+ ) });
2268
+ }
2269
+
2270
+ const track = tv({
2271
+ base: "absolute inset-x-0 top-0 z-10 overflow-hidden transition-opacity duration-fade",
2272
+ variants: {
2273
+ size: {
2274
+ xs: "h-0.5",
2275
+ sm: "h-1",
2276
+ md: "h-1.5",
2277
+ lg: "h-2"
2278
+ },
2279
+ hidden: {
2280
+ true: "opacity-0",
2281
+ false: "opacity-100"
2282
+ }
2283
+ },
2284
+ defaultVariants: { size: "md", hidden: false }
2285
+ });
2286
+ function LinearProgress({
2287
+ progress,
2288
+ hidden = false,
2289
+ accent = "brand",
2290
+ size = "md"
2291
+ }) {
2292
+ return /* @__PURE__ */ jsx(AccentScope, { accent, children: /* @__PURE__ */ jsx(View, { pointerEvents: "none", className: track({ size, hidden }), children: /* @__PURE__ */ jsx(
2293
+ View,
2294
+ {
2295
+ className: "h-full bg-accent transition-[width] duration-progress ease-out",
2296
+ style: { width: `${progress}%` }
2297
+ }
2298
+ ) }) });
2299
+ }
2300
+
2301
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
2302
+ const easeOut = Easing.bezier(0, 0, 0.58, 1);
2303
+ function RingCircle({
2304
+ center,
2305
+ radius,
2306
+ strokeWidth,
2307
+ strokeDasharray,
2308
+ strokeDashoffset,
2309
+ color,
2310
+ width,
2311
+ height
2312
+ }) {
2313
+ const animatedOffset = useSharedValue(strokeDashoffset ?? 0);
2314
+ useEffect(() => {
2315
+ animatedOffset.value = withTiming(strokeDashoffset ?? 0, {
2316
+ duration: animationDurationsMs.progress,
2317
+ easing: easeOut
2318
+ });
2319
+ }, [animatedOffset, strokeDashoffset]);
2320
+ const animatedProps = useAnimatedProps(() => ({
2321
+ strokeDashoffset: animatedOffset.value
2322
+ }));
2323
+ return /* @__PURE__ */ jsx(Svg, { color, width, height, children: strokeDasharray == null ? /* @__PURE__ */ jsx(
2324
+ Circle,
2325
+ {
2326
+ cx: center,
2327
+ cy: center,
2328
+ r: radius,
2329
+ stroke: "currentColor",
2330
+ strokeWidth,
2331
+ fill: "none"
2332
+ }
2333
+ ) : /* @__PURE__ */ jsx(
2334
+ AnimatedCircle,
2335
+ {
2336
+ animatedProps,
2337
+ cx: center,
2338
+ cy: center,
2339
+ r: radius,
2340
+ stroke: "currentColor",
2341
+ strokeWidth,
2342
+ strokeDasharray,
2343
+ strokeLinecap: "round",
2344
+ transform: `rotate(-90 ${center} ${center})`,
2345
+ fill: "none"
2346
+ }
2347
+ ) });
2348
+ }
2349
+
2350
+ const diameterBySize = {
2351
+ xs: 16,
2352
+ sm: 32,
2353
+ md: 64,
2354
+ lg: 128
2355
+ };
2356
+ const strokeWidthBySize = {
2357
+ xs: 2,
2358
+ sm: 4,
2359
+ md: 8,
2360
+ lg: 16
2361
+ };
2362
+ const ring = tv({
2363
+ base: "relative transition-opacity duration-fade",
2364
+ variants: {
2365
+ hidden: {
2366
+ true: "opacity-0",
2367
+ false: "opacity-100"
2368
+ }
2369
+ },
2370
+ defaultVariants: { hidden: false }
2371
+ });
2372
+ function CircularProgress({
2373
+ progress,
2374
+ hidden = false,
2375
+ accent = "brand",
2376
+ size = "md"
2377
+ }) {
2378
+ const diameter = diameterBySize[size];
2379
+ const strokeWidth = strokeWidthBySize[size];
2380
+ const radius = (diameter - strokeWidth) / 2;
2381
+ const circumference = 2 * Math.PI * radius;
2382
+ const clampedProgress = Math.min(Math.max(progress, 0), 100);
2383
+ const dashOffset = circumference * (1 - clampedProgress / 100);
2384
+ const center = diameter / 2;
2385
+ const trackRing = /* @__PURE__ */ jsx(RingCircle, { center, radius, strokeWidth });
2386
+ const fillRing = /* @__PURE__ */ jsx(
2387
+ RingCircle,
2388
+ {
2389
+ center,
2390
+ radius,
2391
+ strokeWidth,
2392
+ strokeDasharray: circumference,
2393
+ strokeDashoffset: dashOffset
2394
+ }
2395
+ );
2396
+ return /* @__PURE__ */ jsx(AccentScope, { accent, children: /* @__PURE__ */ jsxs(
2397
+ View,
2398
+ {
2399
+ className: ring({ hidden }),
2400
+ style: { width: diameter, height: diameter },
2401
+ children: [
2402
+ /* @__PURE__ */ jsx(View, { className: "absolute inset-0", children: /* @__PURE__ */ jsx(
2403
+ Icon,
2404
+ {
2405
+ icon: trackRing,
2406
+ size: diameter,
2407
+ className: "text-highlight-accent"
2408
+ }
2409
+ ) }),
2410
+ /* @__PURE__ */ jsx(View, { className: "absolute inset-0", children: /* @__PURE__ */ jsx(Icon, { icon: fillRing, size: diameter, className: "text-accent" }) })
2411
+ ]
2037
2412
  }
2038
2413
  ) });
2039
2414
  }
@@ -2316,5 +2691,5 @@ function ExternalLink({
2316
2691
  return /* @__PURE__ */ jsx(C, { ...props, onPress: handlePress });
2317
2692
  }
2318
2693
 
2319
- export { AccentScope, AlouetteDecorator, AlouetteProvider, Badge, Box, BreakpointNameEnum, Breakpoints, Button, ConfirmationMessage, ConnectionState, ExternalLink, ExternalLinkButton, FlatList, GradientBackground, GradientScrollView, HStack, Icon, IconButton, InfoMessage, InputText, InteractiveBox, InternalLinkButton, Message, Paragraph, PresenceList, PresenceOne, PressableBox, PressableListItem, SafeAreaBox, ScopedTheme, ScrollView, SectionList, Select, Separator, Stack, Story, StoryContainer, StoryDecorator, StoryGrid, StoryTitle, Surface, Switch, SwitchBreakpointsUsingDisplayNone, SwitchBreakpointsUsingNull, Text, TextArea, VStack, View, WarningMessage, animationDurationsMs, styled, themeVariables, useCurrentBreakpointName, useCurrentBreakpointNameFiltered, useCurrentMode, useCurrentTheme, useThemeToken };
2694
+ export { AccentScope, AlertDialog, AlouetteDecorator, AlouetteProvider, Badge, Box, BreakpointNameEnum, Breakpoints, Button, CircularProgress, ConfirmationMessage, ConnectionState, ExternalLink, ExternalLinkButton, FlatList, GradientBackground, GradientScrollView, HStack, Icon, IconButton, InfoAlertDialog, InfoMessage, InputText, InteractiveBox, InternalLinkButton, LinearProgress, Message, Modal, Paragraph, PresenceList, PresenceOne, PressableBox, PressableListItem, QuestionAlertDialog, SafeAreaBox, ScopedTheme, ScrollView, SectionList, Select, Separator, Stack, Story, StoryContainer, StoryDecorator, StoryGrid, StoryTitle, SuccessAlertDialog, Surface, Switch, SwitchBreakpointsUsingDisplayNone, SwitchBreakpointsUsingNull, Text, TextArea, VStack, View, WarningAlertDialog, WarningMessage, animationDurationsMs, styled, themeVariables, useCurrentBreakpointName, useCurrentBreakpointNameFiltered, useCurrentMode, useCurrentTheme, useThemeToken };
2320
2695
  //# sourceMappingURL=index-node22.mjs.map