@vellira-ui/react-native 2.44.3 → 2.44.5

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 (2) hide show
  1. package/dist/index.js +123 -70
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1528,8 +1528,26 @@ const createStyles$13 = (theme) => StyleSheet.create({ content: {
1528
1528
  //#endregion
1529
1529
  //#region src/components/Modal/Content/ModalContent.tsx
1530
1530
  const ModalContent = ({ children, style }) => {
1531
- return /* @__PURE__ */ jsx(View, {
1532
- style: [useThemeStyles(createStyles$13).content, style],
1531
+ const styles = useThemeStyles(createStyles$13);
1532
+ const { animation, animationProgress, open } = useModalContext();
1533
+ const closedTranslateY = open ? animation === "slide" ? 16 : 8 : 4;
1534
+ const closedScale = open ? .96 : .98;
1535
+ const animatedStyle = animation === "none" ? void 0 : {
1536
+ opacity: animationProgress,
1537
+ transform: animation === "fade" ? [] : [{ translateY: animationProgress.interpolate({
1538
+ inputRange: [0, 1],
1539
+ outputRange: [closedTranslateY, 0]
1540
+ }) }, ...animation === "scale" ? [{ scale: animationProgress.interpolate({
1541
+ inputRange: [0, 1],
1542
+ outputRange: [closedScale, 1]
1543
+ }) }] : []]
1544
+ };
1545
+ return /* @__PURE__ */ jsx(Animated.View, {
1546
+ style: [
1547
+ styles.content,
1548
+ style,
1549
+ animatedStyle
1550
+ ],
1533
1551
  children
1534
1552
  });
1535
1553
  };
@@ -1582,27 +1600,55 @@ const createStyles$11 = (theme) => StyleSheet.create({
1582
1600
  //#region src/components/Modal/Overlay/ModalOverlay.tsx
1583
1601
  const ModalOverlay = ({ children, overlayStyle }) => {
1584
1602
  const styles = useThemeStyles(createStyles$11);
1585
- const { closeOnOutsidePress, onClose, onOutsideClose, open } = useModalContext();
1603
+ const { animation, animationProgress, closeOnOutsidePress, onClose, onOutsideClose, shouldRender } = useModalContext();
1604
+ const backdropStyle = animation === "none" ? void 0 : { opacity: animationProgress };
1586
1605
  return /* @__PURE__ */ jsx(Modal$1, {
1587
- visible: open,
1606
+ visible: shouldRender,
1588
1607
  transparent: true,
1589
- animationType: "fade",
1608
+ animationType: "none",
1590
1609
  onRequestClose: onClose,
1591
1610
  children: /* @__PURE__ */ jsxs(View, {
1592
1611
  style: [styles.overlay, overlayStyle],
1593
- children: [/* @__PURE__ */ jsx(Pressable, {
1594
- testID: "modal-backdrop",
1595
- accessibilityRole: closeOnOutsidePress ? "button" : void 0,
1596
- accessibilityLabel: closeOnOutsidePress ? "Close modal" : void 0,
1597
- style: styles.backdrop,
1598
- onPress: closeOnOutsidePress ? onOutsideClose : void 0
1612
+ children: [/* @__PURE__ */ jsx(Animated.View, {
1613
+ style: [styles.backdrop, backdropStyle],
1614
+ children: /* @__PURE__ */ jsx(Pressable, {
1615
+ testID: "modal-backdrop",
1616
+ accessibilityRole: closeOnOutsidePress ? "button" : void 0,
1617
+ accessibilityLabel: closeOnOutsidePress ? "Close modal" : void 0,
1618
+ style: StyleSheet.absoluteFill,
1619
+ onPress: closeOnOutsidePress ? onOutsideClose : void 0
1620
+ })
1599
1621
  }), children]
1600
1622
  })
1601
1623
  });
1602
1624
  };
1603
1625
  //#endregion
1604
1626
  //#region src/components/Modal/Root/ModalRoot.tsx
1605
- const ModalRoot = ({ open, defaultOpen = false, onOpenChange, closeOnOutsidePress = true, children }) => {
1627
+ const linearEasing = (value) => value;
1628
+ const easingMap = {
1629
+ standard: Easing?.bezier?.(.22, 1, .36, 1) ?? linearEasing,
1630
+ linear: Easing?.linear ?? linearEasing,
1631
+ ease: Easing?.ease ?? linearEasing,
1632
+ "ease-in": Easing?.in?.(Easing?.ease ?? linearEasing) ?? linearEasing,
1633
+ "ease-out": Easing?.out?.(Easing?.ease ?? linearEasing) ?? linearEasing,
1634
+ "ease-in-out": Easing?.inOut?.(Easing?.ease ?? linearEasing) ?? linearEasing
1635
+ };
1636
+ const parseDuration = (duration) => Number.parseFloat(duration);
1637
+ const resolveDuration = (duration) => {
1638
+ if (typeof duration === "number") return {
1639
+ close: duration,
1640
+ open: duration
1641
+ };
1642
+ return {
1643
+ close: duration?.close ?? parseDuration(nativeThemes.light.components.modal.motion.closeDuration),
1644
+ open: duration?.open ?? parseDuration(nativeThemes.light.components.modal.motion.openDuration)
1645
+ };
1646
+ };
1647
+ const ModalRoot = ({ open, defaultOpen = false, onOpenChange, animation = "scale", duration, easing = "standard", closeOnOutsidePress = true, children }) => {
1648
+ const initialOpen = open ?? defaultOpen;
1649
+ const animationProgress = useRef(new Animated.Value(initialOpen ? 1 : 0));
1650
+ const [shouldRender, setShouldRender] = useState(initialOpen);
1651
+ const [reduceMotion, setReduceMotion] = useState(false);
1606
1652
  const modal = useModal({
1607
1653
  open,
1608
1654
  defaultOpen,
@@ -1615,13 +1661,62 @@ const ModalRoot = ({ open, defaultOpen = false, onOpenChange, closeOnOutsidePres
1615
1661
  closeOnOutsidePress: modal.closeOnOutsidePress,
1616
1662
  requestClose: modal.requestClose
1617
1663
  });
1664
+ const animationDuration = resolveDuration(duration);
1665
+ const shouldAnimate = animation !== "none" && !reduceMotion;
1666
+ useEffect(() => {
1667
+ AccessibilityInfo.isReduceMotionEnabled?.().then(setReduceMotion);
1668
+ const subscription = AccessibilityInfo.addEventListener?.("reduceMotionChanged", setReduceMotion);
1669
+ return () => {
1670
+ subscription?.remove();
1671
+ };
1672
+ }, []);
1673
+ useEffect(() => {
1674
+ const progress = animationProgress.current;
1675
+ if (modal.open) {
1676
+ setShouldRender(true);
1677
+ if (!shouldAnimate) {
1678
+ progress.setValue(1);
1679
+ return;
1680
+ }
1681
+ progress.setValue(0);
1682
+ Animated.timing(progress, {
1683
+ toValue: 1,
1684
+ duration: animationDuration.open,
1685
+ easing: easingMap[easing],
1686
+ useNativeDriver: Platform.OS !== "web"
1687
+ }).start();
1688
+ return;
1689
+ }
1690
+ if (!shouldAnimate) {
1691
+ progress.setValue(0);
1692
+ setShouldRender(false);
1693
+ return;
1694
+ }
1695
+ Animated.timing(progress, {
1696
+ toValue: 0,
1697
+ duration: animationDuration.close,
1698
+ easing: easingMap[easing],
1699
+ useNativeDriver: Platform.OS !== "web"
1700
+ }).start(({ finished }) => {
1701
+ if (finished) setShouldRender(false);
1702
+ });
1703
+ }, [
1704
+ animationDuration.close,
1705
+ animationDuration.open,
1706
+ easing,
1707
+ modal.open,
1708
+ shouldAnimate
1709
+ ]);
1618
1710
  return /* @__PURE__ */ jsx(ModalContext.Provider, {
1619
1711
  value: {
1712
+ animation,
1713
+ animationProgress: animationProgress.current,
1620
1714
  closeOnOutsidePress: modal.closeOnOutsidePress,
1621
1715
  onClose: dismiss.requestClose,
1622
1716
  onOutsideClose: dismiss.requestOutsideClose,
1623
1717
  open: modal.open,
1624
- setOpen: modal.setOpen
1718
+ setOpen: modal.setOpen,
1719
+ shouldRender
1625
1720
  },
1626
1721
  children
1627
1722
  });
@@ -1694,7 +1789,7 @@ const createStyles$10 = (theme) => StyleSheet.create({
1694
1789
  alignItems: "flex-start",
1695
1790
  gap: theme.tokens.spacing[2]
1696
1791
  },
1697
- pressablePressed: { opacity: .8 },
1792
+ pressablePressed: { opacity: theme.components.radio.motion.pressedOpacity },
1698
1793
  pressableDisabled: { opacity: 1 },
1699
1794
  control: {
1700
1795
  alignItems: "center",
@@ -1704,14 +1799,6 @@ const createStyles$10 = (theme) => StyleSheet.create({
1704
1799
  borderColor: theme.components.radio.default.border,
1705
1800
  backgroundColor: theme.components.radio.default.bg
1706
1801
  },
1707
- controlChecked: {
1708
- borderColor: theme.components.radio.primary.default.border,
1709
- backgroundColor: theme.components.radio.primary.default.bg
1710
- },
1711
- controlCheckedPressed: {
1712
- borderColor: theme.components.radio.primary.pressed.border,
1713
- backgroundColor: theme.components.radio.primary.pressed.bg
1714
- },
1715
1802
  controlInvalid: { borderColor: theme.components.radio.invalid.border },
1716
1803
  controlDisabled: {
1717
1804
  borderColor: theme.components.radio.disabled.border,
@@ -1725,7 +1812,6 @@ const createStyles$10 = (theme) => StyleSheet.create({
1725
1812
  borderRadius: theme.tokens.radius.full,
1726
1813
  backgroundColor: theme.components.radio.primary.default.fg
1727
1814
  },
1728
- indicatorPressed: { backgroundColor: theme.components.radio.primary.pressed.fg },
1729
1815
  indicatorDisabled: { backgroundColor: theme.components.radio.selectedDisabled.fg },
1730
1816
  content: {
1731
1817
  flexShrink: 1,
@@ -1735,8 +1821,6 @@ const createStyles$10 = (theme) => StyleSheet.create({
1735
1821
  color: theme.components.radio.default.fg,
1736
1822
  fontFamily: theme.tokens.typography.family.regular
1737
1823
  },
1738
- labelChecked: { color: theme.components.radio.primary.default.labelFg },
1739
- labelCheckedPressed: { color: theme.components.radio.primary.pressed.labelFg },
1740
1824
  labelInvalid: { color: theme.components.radio.invalid.fg },
1741
1825
  description: {
1742
1826
  color: theme.components.formField.description.fg,
@@ -1750,16 +1834,6 @@ const createStyles$10 = (theme) => StyleSheet.create({
1750
1834
  });
1751
1835
  //#endregion
1752
1836
  //#region src/primitives/Radio/Radio.tsx
1753
- const controlSizeBySize = {
1754
- sm: 14,
1755
- md: 16,
1756
- lg: 20
1757
- };
1758
- const indicatorSizeBySize = {
1759
- sm: 6,
1760
- md: 8,
1761
- lg: 10
1762
- };
1763
1837
  const nativePointerEventsNone$4 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
1764
1838
  const webPointerEventsNone$4 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
1765
1839
  const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: disabledProp = false, required: requiredProp = false, size: sizeProp, color: colorProp, onCheckedChange, label, description, icon, error, accessibilityLabel, accessibilityHint, containerStyle, labelStyle, descriptionStyle, errorStyle, style, ...rest }, ref) => {
@@ -1779,29 +1853,8 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
1779
1853
  const resolvedSize = sizeProp ?? group?.size ?? "md";
1780
1854
  const resolvedColor = colorProp ?? group?.color ?? "primary";
1781
1855
  const radioColor = theme.components.radio[resolvedColor];
1782
- const controlSize = controlSizeBySize[resolvedSize];
1783
- const indicatorSize = indicatorSizeBySize[resolvedSize];
1784
- const typographySize = {
1785
- sm: {
1786
- labelSize: theme.tokens.typography.size.sm,
1787
- labelLineHeight: theme.tokens.typography.lineHeight.sm,
1788
- descriptionSize: theme.tokens.typography.size.xs,
1789
- descriptionLineHeight: theme.tokens.typography.lineHeight.xs
1790
- },
1791
- md: {
1792
- labelSize: theme.tokens.typography.size.md,
1793
- labelLineHeight: theme.tokens.typography.lineHeight.md,
1794
- descriptionSize: theme.tokens.typography.size.sm,
1795
- descriptionLineHeight: theme.tokens.typography.lineHeight.sm
1796
- },
1797
- lg: {
1798
- labelSize: theme.tokens.typography.size.lg,
1799
- labelLineHeight: theme.tokens.typography.lineHeight.lg,
1800
- descriptionSize: theme.tokens.typography.size.md,
1801
- descriptionLineHeight: theme.tokens.typography.lineHeight.md
1802
- }
1803
- }[resolvedSize];
1804
- const controlMarginTop = (typographySize.labelLineHeight - controlSize) / 2;
1856
+ const radioSize = theme.components.radio.size[resolvedSize];
1857
+ const controlMarginTop = (radioSize.labelLineHeight - radioSize.controlSize) / 2;
1805
1858
  useEffect(() => {
1806
1859
  if (typeof __DEV__ !== "undefined" && __DEV__ && !label && !accessibilityLabel) console.warn("Radio requires either a visible label or accessibilityLabel.");
1807
1860
  }, [accessibilityLabel, label]);
@@ -1846,8 +1899,8 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
1846
1899
  styles.control,
1847
1900
  webPointerEventsNone$4,
1848
1901
  {
1849
- width: controlSize,
1850
- height: controlSize,
1902
+ width: radioSize.controlSize,
1903
+ height: radioSize.controlSize,
1851
1904
  marginTop: controlMarginTop
1852
1905
  },
1853
1906
  resolvedChecked && {
@@ -1857,7 +1910,7 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
1857
1910
  resolvedChecked && state.pressed && !resolvedDisabled && {
1858
1911
  backgroundColor: radioColor.pressed.bg,
1859
1912
  borderColor: radioColor.pressed.border,
1860
- transform: [{ scale: .96 }]
1913
+ transform: [{ scale: theme.components.radio.motion.pressedScale }]
1861
1914
  },
1862
1915
  resolvedInvalid && styles.controlInvalid,
1863
1916
  resolvedDisabled && styles.controlDisabled,
@@ -1866,8 +1919,8 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
1866
1919
  children: resolvedChecked && (icon ?? /* @__PURE__ */ jsx(View, { style: [
1867
1920
  styles.indicator,
1868
1921
  {
1869
- width: indicatorSize,
1870
- height: indicatorSize,
1922
+ width: radioSize.indicatorSize,
1923
+ height: radioSize.indicatorSize,
1871
1924
  backgroundColor: state.pressed && !resolvedDisabled ? radioColor.pressed.fg : radioColor.default.fg
1872
1925
  },
1873
1926
  resolvedDisabled && styles.indicatorDisabled
@@ -1879,8 +1932,8 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
1879
1932
  style: [
1880
1933
  styles.label,
1881
1934
  {
1882
- fontSize: typographySize.labelSize,
1883
- lineHeight: typographySize.labelLineHeight
1935
+ fontSize: radioSize.labelFontSize,
1936
+ lineHeight: radioSize.labelLineHeight
1884
1937
  },
1885
1938
  resolvedChecked && { color: radioColor.default.labelFg },
1886
1939
  resolvedChecked && state.pressed && !resolvedDisabled && { color: radioColor.pressed.labelFg },
@@ -1893,8 +1946,8 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
1893
1946
  style: [
1894
1947
  styles.description,
1895
1948
  {
1896
- fontSize: typographySize.descriptionSize,
1897
- lineHeight: typographySize.descriptionLineHeight
1949
+ fontSize: radioSize.descriptionFontSize,
1950
+ lineHeight: radioSize.descriptionLineHeight
1898
1951
  },
1899
1952
  resolvedDisabled && styles.textDisabled,
1900
1953
  descriptionStyle
@@ -1907,9 +1960,9 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
1907
1960
  style: [
1908
1961
  styles.error,
1909
1962
  {
1910
- marginLeft: controlSize + theme.tokens.spacing[2],
1911
- fontSize: typographySize.descriptionSize,
1912
- lineHeight: typographySize.descriptionLineHeight
1963
+ marginLeft: radioSize.controlSize + theme.tokens.spacing[2],
1964
+ fontSize: radioSize.descriptionFontSize,
1965
+ lineHeight: radioSize.descriptionLineHeight
1913
1966
  },
1914
1967
  errorStyle
1915
1968
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellira-ui/react-native",
3
- "version": "2.44.3",
3
+ "version": "2.44.5",
4
4
  "description": "React Native components for Vellira Design System",
5
5
  "author": "Roman Bakurov",
6
6
  "license": "MIT",