@platform-blocks/ui 0.7.2 → 0.8.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.
package/lib/esm/index.js CHANGED
@@ -466,8 +466,10 @@ function createTheme(themeOverride) {
466
466
 
467
467
  // Debug flag (only logs when in dev mode AND explicit debug env flag is set)
468
468
  typeof __DEV__ !== 'undefined' && __DEV__ && !!process.env.EXPO_PUBLIC_DEBUG;
469
- // Theme Context
469
+ // Full theme context (backwards compatible)
470
470
  const PlatformBlocksThemeContext = createContext(null);
471
+ const ThemeVisualsContext = createContext(null);
472
+ const ThemeLayoutContext = createContext(null);
471
473
  function PlatformBlocksThemeProvider({ theme, inherit = true, children }) {
472
474
  const parentTheme = useTheme();
473
475
  const mergedTheme = useMemo(() => {
@@ -487,11 +489,45 @@ function PlatformBlocksThemeProvider({ theme, inherit = true, children }) {
487
489
  // debugLog('[PlatformBlocksThemeProvider] Creating merged theme with override, colorScheme:', result.colorScheme);
488
490
  return result;
489
491
  }, [theme, parentTheme, inherit]);
492
+ // Derive stable sub-context values — only create new objects when the
493
+ // relevant subset of the theme actually changes.
494
+ const visuals = useMemo(() => ({
495
+ colorScheme: mergedTheme.colorScheme,
496
+ primaryColor: mergedTheme.primaryColor,
497
+ colors: mergedTheme.colors,
498
+ text: mergedTheme.text,
499
+ backgrounds: mergedTheme.backgrounds,
500
+ states: mergedTheme.states,
501
+ }), [
502
+ mergedTheme.colorScheme,
503
+ mergedTheme.primaryColor,
504
+ mergedTheme.colors,
505
+ mergedTheme.text,
506
+ mergedTheme.backgrounds,
507
+ mergedTheme.states,
508
+ ]);
509
+ const layout = useMemo(() => ({
510
+ fontFamily: mergedTheme.fontFamily,
511
+ fontSizes: mergedTheme.fontSizes,
512
+ spacing: mergedTheme.spacing,
513
+ radii: mergedTheme.radii,
514
+ shadows: mergedTheme.shadows,
515
+ breakpoints: mergedTheme.breakpoints,
516
+ designTokens: mergedTheme.designTokens,
517
+ }), [
518
+ mergedTheme.fontFamily,
519
+ mergedTheme.fontSizes,
520
+ mergedTheme.spacing,
521
+ mergedTheme.radii,
522
+ mergedTheme.shadows,
523
+ mergedTheme.breakpoints,
524
+ mergedTheme.designTokens,
525
+ ]);
490
526
  // debugLog('[PlatformBlocksThemeProvider] Rendering with theme colorScheme:', mergedTheme.colorScheme);
491
- return (jsx(PlatformBlocksThemeContext.Provider, { value: mergedTheme, children: children }));
527
+ return (jsx(PlatformBlocksThemeContext.Provider, { value: mergedTheme, children: jsx(ThemeVisualsContext.Provider, { value: visuals, children: jsx(ThemeLayoutContext.Provider, { value: layout, children: children }) }) }));
492
528
  }
493
529
  /**
494
- * Hook to access the current theme
530
+ * Hook to access the current theme (full object — backwards compatible)
495
531
  */
496
532
  function useTheme() {
497
533
  const theme = useContext(PlatformBlocksThemeContext);
@@ -501,6 +537,45 @@ function useTheme() {
501
537
  }
502
538
  return theme;
503
539
  }
540
+ /**
541
+ * Granular hook: subscribe only to visual / color-related properties.
542
+ * Components using this will NOT re-render when layout tokens change.
543
+ */
544
+ function useThemeVisuals() {
545
+ const visuals = useContext(ThemeVisualsContext);
546
+ if (!visuals) {
547
+ const t = DEFAULT_THEME;
548
+ return {
549
+ colorScheme: t.colorScheme,
550
+ primaryColor: t.primaryColor,
551
+ colors: t.colors,
552
+ text: t.text,
553
+ backgrounds: t.backgrounds,
554
+ states: t.states,
555
+ };
556
+ }
557
+ return visuals;
558
+ }
559
+ /**
560
+ * Granular hook: subscribe only to layout / token properties.
561
+ * Components using this will NOT re-render when colors change.
562
+ */
563
+ function useThemeLayout() {
564
+ const layout = useContext(ThemeLayoutContext);
565
+ if (!layout) {
566
+ const t = DEFAULT_THEME;
567
+ return {
568
+ fontFamily: t.fontFamily,
569
+ fontSizes: t.fontSizes,
570
+ spacing: t.spacing,
571
+ radii: t.radii,
572
+ shadows: t.shadows,
573
+ breakpoints: t.breakpoints,
574
+ designTokens: t.designTokens,
575
+ };
576
+ }
577
+ return layout;
578
+ }
504
579
 
505
580
  /**
506
581
  * Component that injects CSS variables based on the current theme
@@ -1506,6 +1581,58 @@ function useKeyboardManagerOptional() {
1506
1581
  return useContext(KeyboardManagerContext);
1507
1582
  }
1508
1583
 
1584
+ const BREAKPOINTS = {
1585
+ xs: 480, // Extra small devices
1586
+ sm: 576, // Small devices (landscape phones)
1587
+ md: 768, // Medium devices (tablets)
1588
+ lg: 992, // Large devices (desktops)
1589
+ xl: 1200, // Extra large devices (large desktops)
1590
+ };
1591
+ // Shared breakpoint context — a single resize listener feeds all consumers
1592
+ const BreakpointContext = createContext(null);
1593
+ function computeBreakpoint() {
1594
+ let width;
1595
+ if (Platform.OS === 'web') {
1596
+ width = window.innerWidth;
1597
+ }
1598
+ else {
1599
+ width = Dimensions.get('window').width;
1600
+ }
1601
+ if (width >= BREAKPOINTS.xl)
1602
+ return 'xl';
1603
+ if (width >= BREAKPOINTS.lg)
1604
+ return 'lg';
1605
+ if (width >= BREAKPOINTS.md)
1606
+ return 'md';
1607
+ if (width >= BREAKPOINTS.sm)
1608
+ return 'sm';
1609
+ if (width >= BREAKPOINTS.xs)
1610
+ return 'xs';
1611
+ return 'base';
1612
+ }
1613
+ /**
1614
+ * Provider that maintains a single resize / Dimensions listener and shares
1615
+ * the current breakpoint with all descendants via context.
1616
+ * Mount once near the root of the app (PlatformBlocksProvider does this automatically).
1617
+ */
1618
+ function BreakpointProvider({ children }) {
1619
+ const [breakpoint, setBreakpoint] = useState(computeBreakpoint);
1620
+ useEffect(() => {
1621
+ const update = () => {
1622
+ setBreakpoint(computeBreakpoint());
1623
+ };
1624
+ if (Platform.OS === 'web') {
1625
+ window.addEventListener('resize', update);
1626
+ return () => window.removeEventListener('resize', update);
1627
+ }
1628
+ else {
1629
+ const subscription = Dimensions.addEventListener('change', update);
1630
+ return () => subscription === null || subscription === void 0 ? void 0 : subscription.remove();
1631
+ }
1632
+ }, []);
1633
+ return React__default.createElement(BreakpointContext.Provider, { value: breakpoint }, children);
1634
+ }
1635
+
1509
1636
  /**
1510
1637
  * Global Spotlight Hook for Direct State Access
1511
1638
  *
@@ -1866,26 +1993,33 @@ function useSpotlightStoreInstance() {
1866
1993
  query: '',
1867
1994
  selectedIndex: -1
1868
1995
  });
1869
- const store = {
1996
+ const open = useCallback(() => setState(prev => ({ ...prev, opened: true })), []);
1997
+ const close = useCallback(() => setState(prev => ({ ...prev, opened: false, query: '', selectedIndex: -1 })), []);
1998
+ const toggle = useCallback(() => setState(prev => ({ ...prev, opened: !prev.opened })), []);
1999
+ const setQuery = useCallback((query) => setState(prev => ({ ...prev, query })), []);
2000
+ const setSelectedIndex = useCallback((selectedIndex) => setState(prev => ({ ...prev, selectedIndex })), []);
2001
+ const navigateUp = useCallback(() => setState(prev => ({ ...prev, selectedIndex: prev.selectedIndex > 0 ? prev.selectedIndex - 1 : -1 })), []);
2002
+ const navigateDown = useCallback((maxIndex = 0) => setState(prev => ({ ...prev, selectedIndex: prev.selectedIndex < maxIndex - 1 ? prev.selectedIndex + 1 : maxIndex - 1 })), []);
2003
+ const store = useMemo(() => ({
1870
2004
  state,
1871
- open: () => setState(prev => ({ ...prev, opened: true })),
1872
- close: () => setState(prev => ({ ...prev, opened: false, query: '', selectedIndex: -1 })),
1873
- toggle: () => setState(prev => ({ ...prev, opened: !prev.opened })),
1874
- setQuery: (query) => setState(prev => ({ ...prev, query })),
1875
- setSelectedIndex: (selectedIndex) => setState(prev => ({ ...prev, selectedIndex })),
1876
- navigateUp: () => setState(prev => ({ ...prev, selectedIndex: prev.selectedIndex > 0 ? prev.selectedIndex - 1 : -1 })),
1877
- navigateDown: (maxIndex = 0) => setState(prev => ({ ...prev, selectedIndex: prev.selectedIndex < maxIndex - 1 ? prev.selectedIndex + 1 : maxIndex - 1 })),
1878
- };
1879
- const actions = {
1880
- state: store.state,
1881
- open: store.open,
1882
- close: store.close,
1883
- toggle: store.toggle,
1884
- setQuery: store.setQuery,
1885
- setSelectedIndex: store.setSelectedIndex,
1886
- navigateUp: store.navigateUp,
1887
- navigateDown: store.navigateDown,
1888
- };
2005
+ open,
2006
+ close,
2007
+ toggle,
2008
+ setQuery,
2009
+ setSelectedIndex,
2010
+ navigateUp,
2011
+ navigateDown,
2012
+ }), [state, open, close, toggle, setQuery, setSelectedIndex, navigateUp, navigateDown]);
2013
+ const actions = useMemo(() => ({
2014
+ state,
2015
+ open,
2016
+ close,
2017
+ toggle,
2018
+ setQuery,
2019
+ setSelectedIndex,
2020
+ navigateUp,
2021
+ navigateDown,
2022
+ }), [state, open, close, toggle, setQuery, setSelectedIndex, navigateUp, navigateDown]);
1889
2023
  return [store, actions];
1890
2024
  }
1891
2025
  const createSpotlightStore = useSpotlightStoreInstance;
@@ -3754,13 +3888,27 @@ function factory(ui, options = {}) {
3754
3888
  /**
3755
3889
  * Factory function for creating polymorphic PlatformBlocks components
3756
3890
  */
3757
- function polymorphicFactory(ui) {
3758
- const Component = forwardRef(ui);
3891
+ function polymorphicFactory(ui, options = {}) {
3892
+ const { displayName, memo: shouldMemo = true, arePropsEqual } = options;
3893
+ const ForwardComponent = forwardRef(ui);
3894
+ if (displayName) {
3895
+ ForwardComponent.displayName = displayName;
3896
+ }
3897
+ const MemoizedComponent = shouldMemo
3898
+ ? memo(ForwardComponent, arePropsEqual)
3899
+ : ForwardComponent;
3900
+ const Component = MemoizedComponent;
3759
3901
  Component.withProps = (fixedProps) => {
3760
- const Extended = forwardRef((props, ref) => (jsx(Component, { ...fixedProps, ...props, ref: ref })));
3761
- Extended.extend = Component.extend;
3762
- Extended.displayName = `WithProps(${Component.displayName})`;
3763
- return Extended;
3902
+ const Extended = forwardRef((props, ref) => (jsx(ForwardComponent, { ...fixedProps, ...props, ref: ref })));
3903
+ const baseName = ForwardComponent.displayName || ui.name || 'PolymorphicComponent';
3904
+ Extended.displayName = `WithProps(${baseName})`;
3905
+ const ExtendedComponent = shouldMemo
3906
+ ? memo(Extended, arePropsEqual)
3907
+ : Extended;
3908
+ const Result = ExtendedComponent;
3909
+ Result.extend = Component.extend;
3910
+ Result.withProps = Component.withProps;
3911
+ return Result;
3764
3912
  };
3765
3913
  Component.extend = ((input) => input);
3766
3914
  return Component;
@@ -6939,10 +7087,10 @@ function Dialog({ visible, variant = 'modal', title, children, closable = true,
6939
7087
  }, [shouldClose]);
6940
7088
  const isDark = theme.colorScheme === 'dark';
6941
7089
  const surfaceColor = isDark ? theme.backgrounds.surface : '#FFFFFF';
6942
- isDark ? theme.colors.gray[6] : '#E1E3E6';
7090
+ const borderColor = isDark ? theme.colors.gray[6] : '#E1E3E6';
6943
7091
  const headerBg = surfaceColor;
6944
7092
  const contentBg = surfaceColor;
6945
- const dynamicStyles = StyleSheet.create({
7093
+ const dynamicStyles = useMemo(() => StyleSheet.create({
6946
7094
  backdrop: {
6947
7095
  ...StyleSheet.absoluteFillObject,
6948
7096
  backgroundColor: variant === 'fullscreen' ? 'transparent' : 'rgba(0, 0, 0, 0.5)',
@@ -7052,7 +7200,9 @@ function Dialog({ visible, variant = 'modal', title, children, closable = true,
7052
7200
  msUserSelect: 'none',
7053
7201
  }),
7054
7202
  },
7055
- });
7203
+ }), [variant, contentBg, resolvedRadius, resolvedMaxHeight, bottomSheetMaxWidth, defaultModalMaxWidth,
7204
+ modalEffectiveWidth, screenWidth, horizontalMargin, insets.top, insets.bottom,
7205
+ headerBg, borderColor, isRTL, isDark, theme.colors.gray, title]);
7056
7206
  // Animated styles using reanimated
7057
7207
  const backdropAnimatedStyle = useAnimatedStyle(() => {
7058
7208
  const opacity = backdropOpacity.value;
@@ -7820,12 +7970,13 @@ const TitleRegistryProvider = ({ children }) => {
7820
7970
  const clearTitles = useCallback(() => {
7821
7971
  setTitles([]);
7822
7972
  }, []);
7823
- return (jsx(TitleRegistryContext.Provider, { value: {
7824
- titles,
7825
- registerTitle,
7826
- unregisterTitle,
7827
- clearTitles
7828
- }, children: children }));
7973
+ const value = useMemo(() => ({
7974
+ titles,
7975
+ registerTitle,
7976
+ unregisterTitle,
7977
+ clearTitles
7978
+ }), [titles, registerTitle, unregisterTitle, clearTitles]);
7979
+ return (jsx(TitleRegistryContext.Provider, { value: value, children: children }));
7829
7980
  };
7830
7981
 
7831
7982
  const DEFAULT_SELECTOR = 'h1, h2, h3, h4, h5, h6';
@@ -8152,14 +8303,97 @@ function createMask(definition) {
8152
8303
  })
8153
8304
  });
8154
8305
 
8306
+ function useMaskedInput(options) {
8307
+ const { mask: maskDefinition, initialValue = '', onValueChange, onUnmaskedValueChange } = options;
8308
+ // Create mask function if needed
8309
+ const maskRef = useRef(typeof maskDefinition === 'function' || 'applyMask' in maskDefinition
8310
+ ? maskDefinition
8311
+ : createMask(maskDefinition));
8312
+ const mask = maskRef.current;
8313
+ // Initialize state
8314
+ const initialResult = mask.applyMask(initialValue);
8315
+ const [state, setState] = useState({
8316
+ value: initialResult.value,
8317
+ unmaskedValue: initialResult.unmaskedValue,
8318
+ isComplete: initialResult.isComplete,
8319
+ cursorPosition: initialResult.cursorPosition,
8320
+ previousValue: initialResult.value
8321
+ });
8322
+ const selectionRef = useRef({ start: 0, end: 0 });
8323
+ const updateState = useCallback((newResult) => {
8324
+ setState(prevState => {
8325
+ if (prevState.value === newResult.value &&
8326
+ prevState.unmaskedValue === newResult.unmaskedValue &&
8327
+ prevState.isComplete === newResult.isComplete &&
8328
+ prevState.cursorPosition === newResult.cursorPosition) {
8329
+ return prevState;
8330
+ }
8331
+ return {
8332
+ value: newResult.value,
8333
+ unmaskedValue: newResult.unmaskedValue,
8334
+ isComplete: newResult.isComplete,
8335
+ cursorPosition: newResult.cursorPosition,
8336
+ previousValue: prevState.value
8337
+ };
8338
+ });
8339
+ }, []);
8340
+ // Handle callbacks in useEffect to avoid calling them during render
8341
+ const prevUnmaskedValueRef = useRef(state.unmaskedValue);
8342
+ useEffect(() => {
8343
+ if (state.unmaskedValue !== prevUnmaskedValueRef.current) {
8344
+ prevUnmaskedValueRef.current = state.unmaskedValue;
8345
+ const result = {
8346
+ value: state.value,
8347
+ unmaskedValue: state.unmaskedValue,
8348
+ isComplete: state.isComplete,
8349
+ cursorPosition: state.cursorPosition
8350
+ };
8351
+ onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(result);
8352
+ onUnmaskedValueChange === null || onUnmaskedValueChange === void 0 ? void 0 : onUnmaskedValueChange(state.unmaskedValue, result);
8353
+ }
8354
+ }, [state.value, state.unmaskedValue, state.isComplete, state.cursorPosition, onValueChange, onUnmaskedValueChange]);
8355
+ const handleChangeText = useCallback((text) => {
8356
+ const result = mask.processInput(text, state.previousValue, selectionRef.current.start);
8357
+ updateState(result);
8358
+ }, [mask, state.previousValue, updateState]);
8359
+ const handleSelectionChange = useCallback((selection) => {
8360
+ selectionRef.current = selection;
8361
+ }, []);
8362
+ const reset = useCallback(() => {
8363
+ const result = mask.applyMask(initialValue);
8364
+ updateState(result);
8365
+ }, [mask, initialValue, updateState]);
8366
+ const setValue = useCallback((value) => {
8367
+ const result = mask.applyMask(value);
8368
+ updateState(result);
8369
+ }, [mask, updateState]);
8370
+ const setUnmaskedValue = useCallback((unmaskedValue) => {
8371
+ const result = mask.applyMask(unmaskedValue);
8372
+ updateState(result);
8373
+ }, [mask, updateState]);
8374
+ return {
8375
+ value: state.value,
8376
+ unmaskedValue: state.unmaskedValue,
8377
+ isComplete: state.isComplete,
8378
+ handleChangeText,
8379
+ handleSelectionChange,
8380
+ cursorPosition: state.cursorPosition,
8381
+ reset,
8382
+ setValue,
8383
+ setUnmaskedValue
8384
+ };
8385
+ }
8386
+
8155
8387
  const useTitleRegistration = (options) => {
8156
8388
  const registry = useTitleRegistryOptional();
8157
8389
  const elementRef = useRef(null);
8158
8390
  const { text, order, id: providedId, autoRegister = true } = options;
8159
8391
  // Generate ID from text if not provided
8160
8392
  const id = providedId || text.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
8393
+ const registerTitle = registry === null || registry === void 0 ? void 0 : registry.registerTitle;
8394
+ const unregisterTitle = registry === null || registry === void 0 ? void 0 : registry.unregisterTitle;
8161
8395
  useEffect(() => {
8162
- if (!registry || !autoRegister || !text)
8396
+ if (!registerTitle || !unregisterTitle || !autoRegister || !text)
8163
8397
  return;
8164
8398
  const titleItem = {
8165
8399
  id,
@@ -8167,11 +8401,11 @@ const useTitleRegistration = (options) => {
8167
8401
  order,
8168
8402
  ref: elementRef,
8169
8403
  };
8170
- registry.registerTitle(titleItem);
8404
+ registerTitle(titleItem);
8171
8405
  return () => {
8172
- registry.unregisterTitle(id);
8406
+ unregisterTitle(id);
8173
8407
  };
8174
- }, [registry, id, text, order, autoRegister]);
8408
+ }, [registerTitle, unregisterTitle, id, text, order, autoRegister]);
8175
8409
  return { elementRef, id };
8176
8410
  };
8177
8411
 
@@ -9440,7 +9674,7 @@ shortcut = ['cmd+k', 'ctrl+k'], searchProps = {}, store, variant = 'modal', widt
9440
9674
  // Reset selection when query changes
9441
9675
  useEffect(() => {
9442
9676
  currentStore.setSelectedIndex(-1);
9443
- }, [query, currentStore]);
9677
+ }, [query, currentStore.setSelectedIndex]);
9444
9678
  const handleNavigateUp = () => {
9445
9679
  if (!flatActions.length)
9446
9680
  return;
@@ -10007,7 +10241,7 @@ function PlatformBlocksProvider({ children, theme, inherit = true, withCSSVariab
10007
10241
  if (directionConfig) {
10008
10242
  enhancedTree = (jsx(DirectionProvider, { ...directionConfig, children: enhancedTree }));
10009
10243
  }
10010
- return (jsx(I18nBoundary, { locale: locale, fallbackLocale: fallbackLocale, resources: i18nStore, children: enhancedTree }));
10244
+ return (jsx(I18nBoundary, { locale: locale, fallbackLocale: fallbackLocale, resources: i18nStore, children: jsx(BreakpointProvider, { children: enhancedTree }) }));
10011
10245
  }
10012
10246
  PlatformBlocksProvider.displayName = 'PlatformBlocksProvider';
10013
10247
 
@@ -10742,7 +10976,9 @@ function resolveResponsiveProp(value, width, breakpoints = DEFAULT_BREAKPOINTS)
10742
10976
  const DEFAULT_EXTRA_SCROLL_HEIGHT = 24;
10743
10977
  const KeyboardAwareLayout = forwardRef((props, ref) => {
10744
10978
  var _a, _b;
10745
- const { children, behavior, keyboardVerticalOffset = 0, enabled = true, scrollable = true, extraScrollHeight = DEFAULT_EXTRA_SCROLL_HEIGHT, style, contentContainerStyle, keyboardShouldPersistTaps = 'handled', scrollRef, scrollViewProps, ...rest } = props;
10979
+ const { children, behavior, keyboardVerticalOffset = 0, enabled = true, scrollable = true, extraScrollHeight = DEFAULT_EXTRA_SCROLL_HEIGHT, style, contentContainerStyle, keyboardShouldPersistTaps = 'handled', scrollRef, scrollViewProps,
10980
+ // Native ScrollView passthrough props
10981
+ scrollEnabled, bounces, onScroll, scrollEventThrottle, onMomentumScrollBegin, onMomentumScrollEnd, showsVerticalScrollIndicator, showsHorizontalScrollIndicator, decelerationRate, overScrollMode: overScrollModeProp, refreshControl, ...rest } = props;
10746
10982
  const { spacingProps, otherProps } = extractSpacingProps(rest);
10747
10983
  const spacingStyles = getSpacingStyles(spacingProps);
10748
10984
  const keyboard = useKeyboardManagerOptional();
@@ -10780,7 +11016,7 @@ const KeyboardAwareLayout = forwardRef((props, ref) => {
10780
11016
  }
10781
11017
  return stylesArray;
10782
11018
  }, [spacingStyles, style]);
10783
- return (jsx(KeyboardAvoidingView, { ref: ref, behavior: resolvedBehavior, keyboardVerticalOffset: keyboardVerticalOffset, enabled: enabled, style: containerStyles, ...otherProps, children: scrollable ? (jsx(ScrollView, { ref: scrollRef, contentContainerStyle: contentStyles, keyboardShouldPersistTaps: keyboardShouldPersistTaps, showsVerticalScrollIndicator: false, overScrollMode: "never", ...restScrollViewProps, children: children })) : (jsx(View, { style: contentStyles, children: children })) }));
11019
+ return (jsx(KeyboardAvoidingView, { ref: ref, behavior: resolvedBehavior, keyboardVerticalOffset: keyboardVerticalOffset, enabled: enabled, style: containerStyles, ...otherProps, children: scrollable ? (jsx(ScrollView, { ref: scrollRef, contentContainerStyle: contentStyles, keyboardShouldPersistTaps: keyboardShouldPersistTaps, showsVerticalScrollIndicator: showsVerticalScrollIndicator !== null && showsVerticalScrollIndicator !== void 0 ? showsVerticalScrollIndicator : false, showsHorizontalScrollIndicator: showsHorizontalScrollIndicator, overScrollMode: overScrollModeProp !== null && overScrollModeProp !== void 0 ? overScrollModeProp : 'never', scrollEnabled: scrollEnabled, bounces: bounces, onScroll: onScroll, scrollEventThrottle: scrollEventThrottle, onMomentumScrollBegin: onMomentumScrollBegin, onMomentumScrollEnd: onMomentumScrollEnd, decelerationRate: decelerationRate, refreshControl: refreshControl, ...restScrollViewProps, children: children })) : (jsx(View, { style: contentStyles, children: children })) }));
10784
11020
  });
10785
11021
  KeyboardAwareLayout.displayName = 'KeyboardAwareLayout';
10786
11022
  const styles$d = StyleSheet.create({
@@ -10833,18 +11069,41 @@ const DefaultItemRenderer = ({ item, index, gap }) => {
10833
11069
  const Masonry = factory((props, ref) => {
10834
11070
  // const { width } = useWindowDimensions();
10835
11071
  const { spacingProps, otherProps } = extractSpacingProps(props);
10836
- const { data = [], numColumns = 2, gap = 'sm', optimizeItemArrangement = true, renderItem, contentContainerStyle, style, testID, loading = false, emptyContent, flashListProps = {}, ...restProps } = otherProps;
11072
+ const { data = [], numColumns = 2, gap = 'sm', optimizeItemArrangement = true, renderItem, contentContainerStyle, style, testID, loading = false, emptyContent, flashListProps = {},
11073
+ // Native FlashList passthrough props
11074
+ onEndReached, onEndReachedThreshold, onViewableItemsChanged, scrollEnabled, ListEmptyComponent, ListFooterComponent, ListHeaderComponent, estimatedItemSize, refreshControl, onScroll, scrollEventThrottle, ...restProps } = otherProps;
10837
11075
  const resolvedGap = getSpacing(gap);
10838
11076
  const spacingStyle = getSpacingStyles(spacingProps);
10839
11077
  // FlashList performance hints (overridable via flashListProps). Use loose typing to avoid version/type mismatches.
10840
11078
  const finalFlashListProps = React__default.useMemo(() => {
10841
11079
  const p = { ...flashListProps };
10842
11080
  if (p.estimatedItemSize == null)
10843
- p.estimatedItemSize = 180;
11081
+ p.estimatedItemSize = estimatedItemSize !== null && estimatedItemSize !== void 0 ? estimatedItemSize : 180;
10844
11082
  if (p.keyExtractor == null)
10845
11083
  p.keyExtractor = (item) => item.id;
11084
+ // Merge first-class passthrough props (explicit flashListProps overrides these)
11085
+ if (onEndReached !== undefined && p.onEndReached == null)
11086
+ p.onEndReached = onEndReached;
11087
+ if (onEndReachedThreshold !== undefined && p.onEndReachedThreshold == null)
11088
+ p.onEndReachedThreshold = onEndReachedThreshold;
11089
+ if (onViewableItemsChanged !== undefined && p.onViewableItemsChanged == null)
11090
+ p.onViewableItemsChanged = onViewableItemsChanged;
11091
+ if (scrollEnabled !== undefined && p.scrollEnabled == null)
11092
+ p.scrollEnabled = scrollEnabled;
11093
+ if (ListEmptyComponent !== undefined && p.ListEmptyComponent == null)
11094
+ p.ListEmptyComponent = ListEmptyComponent;
11095
+ if (ListFooterComponent !== undefined && p.ListFooterComponent == null)
11096
+ p.ListFooterComponent = ListFooterComponent;
11097
+ if (ListHeaderComponent !== undefined && p.ListHeaderComponent == null)
11098
+ p.ListHeaderComponent = ListHeaderComponent;
11099
+ if (refreshControl !== undefined && p.refreshControl == null)
11100
+ p.refreshControl = refreshControl;
11101
+ if (onScroll !== undefined && p.onScroll == null)
11102
+ p.onScroll = onScroll;
11103
+ if (scrollEventThrottle !== undefined && p.scrollEventThrottle == null)
11104
+ p.scrollEventThrottle = scrollEventThrottle;
10846
11105
  return p;
10847
- }, [flashListProps]);
11106
+ }, [flashListProps, estimatedItemSize, onEndReached, onEndReachedThreshold, onViewableItemsChanged, scrollEnabled, ListEmptyComponent, ListFooterComponent, ListHeaderComponent, refreshControl, onScroll, scrollEventThrottle]);
10848
11107
  const renderMasonryItem = ({ item, index }) => {
10849
11108
  if (renderItem) {
10850
11109
  return (jsx(View, { children: renderItem(item, index) }));
@@ -13758,12 +14017,17 @@ const Collapse = ({ isCollapsed, children, duration = 300, timing = 'ease-out',
13758
14017
  }
13759
14018
  setContentHeight(prev => {
13760
14019
  if (Math.abs(prev - height) > 0.5) {
13761
- heightAnimation.setValue(isCollapsed ? height : Math.min(height, collapsedHeight));
13762
14020
  return height;
13763
14021
  }
13764
14022
  return prev;
13765
14023
  });
13766
14024
  };
14025
+ // Sync animated value when contentHeight changes outside of initial measurement
14026
+ useEffect(() => {
14027
+ if (!hasMeasuredRef.current || contentHeight === 0)
14028
+ return;
14029
+ heightAnimation.setValue(isCollapsed ? contentHeight : Math.min(contentHeight, collapsedHeight));
14030
+ }, [contentHeight, isCollapsed, collapsedHeight, heightAnimation]);
13767
14031
  return (jsx(Animated$1.View, { style: [
13768
14032
  {
13769
14033
  overflow: 'hidden',
@@ -14546,7 +14810,7 @@ const CodeBlock = (props) => {
14546
14810
 
14547
14811
  const createDefaultComponents = (theme, handleLinkPress) => ({
14548
14812
  heading: ({ level, children }) => (jsx(Text, { variant: `h${Math.min(level, 6)}`, style: { marginTop: level === 1 ? 24 : 16, marginBottom: 8 }, weight: level <= 2 ? '700' : '600', children: children })),
14549
- paragraph: ({ children }) => (jsx(Text, { variant: "p", style: { marginBottom: 12 }, children: children })),
14813
+ paragraph: ({ children }) => (jsx(Text, { variant: "p", as: "div", style: { marginBottom: 12 }, children: children })),
14550
14814
  strong: ({ children }) => (jsx(Text, { variant: "strong", children: children })),
14551
14815
  em: ({ children }) => (jsx(Text, { variant: "em", children: children })),
14552
14816
  codeInline: ({ children }) => (jsx(Text, { variant: "code", style: {
@@ -17038,7 +17302,7 @@ const TextInputBase = factory((props, ref) => {
17038
17302
  height: styles.input.fontSize || 16,
17039
17303
  backgroundColor: textColor,
17040
17304
  marginLeft: 1,
17041
- } }))] }))] }), (showClearButton || endSection) && (jsxs(View, { style: styles.endSection, children: [showClearButton && (jsx(ClearButton, { onPress: handleClear, size: size, accessibilityLabel: clearButtonLabelText, hasRightSection: !!endSection })), endSection] }))] }), disclaimerNode, error && (jsx(Text$1, { style: styles.error, role: "alert", accessibilityLiveRegion: "polite", children: error })), helperText && !error && (jsx(Text$1, { style: styles.helperText, children: helperText }))] }));
17305
+ } }))] }))] }), (showClearButton || endSection) && (jsxs(View, { style: styles.endSection, children: [showClearButton && (jsx(ClearButton, { onPress: handleClear, size: size, accessibilityLabel: clearButtonLabelText, hasRightSection: !!endSection })), endSection] }))] }), disclaimerNode, error ? (jsx(Text$1, { style: styles.error, role: "alert", accessibilityLiveRegion: "polite", children: error })) : null, helperText && !error ? (jsx(Text$1, { style: styles.helperText, children: helperText })) : null] }));
17042
17306
  });
17043
17307
 
17044
17308
  const getInputTypeConfig = (type) => {
@@ -17090,7 +17354,9 @@ const getInputTypeConfig = (type) => {
17090
17354
  return configs[type || 'text'];
17091
17355
  };
17092
17356
  const Input = factory((props, ref) => {
17093
- const { type = 'text', validation, autoComplete, keyboardType, multiline, numberOfLines, minLines = 1, maxLines, maxLength, secureTextEntry, textInputProps, required, withAsterisk, endSection, inputRef, value, onChangeText, ...baseProps } = props;
17357
+ const { type = 'text', validation, autoComplete, keyboardType, multiline, numberOfLines, minLines = 1, maxLines, maxLength, secureTextEntry, textInputProps, required, withAsterisk, endSection, inputRef, value, onChangeText,
17358
+ // Native TextInput passthrough props
17359
+ autoCapitalize, autoCorrect, autoFocus, returnKeyType, blurOnSubmit, selectTextOnFocus, textContentType, textAlign, spellCheck, inputMode, enterKeyHint, selectionColor, showSoftInputOnFocus, editable, ...baseProps } = props;
17094
17360
  const theme = useTheme();
17095
17361
  // State for password visibility
17096
17362
  const [showPassword, setShowPassword] = useState(false);
@@ -17137,8 +17403,39 @@ const Input = factory((props, ref) => {
17137
17403
  }, [numberOfLines, multiline, currentLines]);
17138
17404
  // Merge type config with explicit props
17139
17405
  const mergedTextInputProps = useMemo(() => {
17406
+ // Collect native passthrough props (only include if explicitly set)
17407
+ const nativePassthrough = {};
17408
+ if (autoCapitalize !== undefined)
17409
+ nativePassthrough.autoCapitalize = autoCapitalize;
17410
+ if (autoCorrect !== undefined)
17411
+ nativePassthrough.autoCorrect = autoCorrect;
17412
+ if (autoFocus !== undefined)
17413
+ nativePassthrough.autoFocus = autoFocus;
17414
+ if (returnKeyType !== undefined)
17415
+ nativePassthrough.returnKeyType = returnKeyType;
17416
+ if (blurOnSubmit !== undefined)
17417
+ nativePassthrough.blurOnSubmit = blurOnSubmit;
17418
+ if (selectTextOnFocus !== undefined)
17419
+ nativePassthrough.selectTextOnFocus = selectTextOnFocus;
17420
+ if (textContentType !== undefined)
17421
+ nativePassthrough.textContentType = textContentType;
17422
+ if (textAlign !== undefined)
17423
+ nativePassthrough.textAlign = textAlign;
17424
+ if (spellCheck !== undefined)
17425
+ nativePassthrough.spellCheck = spellCheck;
17426
+ if (inputMode !== undefined)
17427
+ nativePassthrough.inputMode = inputMode;
17428
+ if (enterKeyHint !== undefined)
17429
+ nativePassthrough.enterKeyHint = enterKeyHint;
17430
+ if (selectionColor !== undefined)
17431
+ nativePassthrough.selectionColor = selectionColor;
17432
+ if (showSoftInputOnFocus !== undefined)
17433
+ nativePassthrough.showSoftInputOnFocus = showSoftInputOnFocus;
17434
+ if (editable !== undefined)
17435
+ nativePassthrough.editable = editable;
17140
17436
  const baseProps = {
17141
17437
  ...typeConfig,
17438
+ ...nativePassthrough,
17142
17439
  ...textInputProps,
17143
17440
  // Override secureTextEntry for password visibility toggle
17144
17441
  secureTextEntry: actualSecureTextEntry,
@@ -17182,7 +17479,21 @@ const Input = factory((props, ref) => {
17182
17479
  effectiveNumberOfLines,
17183
17480
  maxLength,
17184
17481
  isPasswordType,
17185
- showPassword
17482
+ showPassword,
17483
+ autoCapitalize,
17484
+ autoCorrect,
17485
+ autoFocus,
17486
+ returnKeyType,
17487
+ blurOnSubmit,
17488
+ selectTextOnFocus,
17489
+ textContentType,
17490
+ textAlign,
17491
+ spellCheck,
17492
+ inputMode,
17493
+ enterKeyHint,
17494
+ selectionColor,
17495
+ showSoftInputOnFocus,
17496
+ editable,
17186
17497
  ]);
17187
17498
  return (jsx(Fragment, { children: jsx(TextInputBase, { ...baseProps, value: value, onChangeText: handleChangeText, inputRef: inputRef || ref, required: required, withAsterisk: finalWithAsterisk, endSection: finalRightSection, textInputProps: mergedTextInputProps, secureTextEntry:
17188
17499
  // Ensure secureTextEntry is only passed when not controlled by password toggle
@@ -18582,7 +18893,9 @@ const NumberInput = factory((props, ref) => {
18582
18893
  NumberInput.displayName = 'NumberInput';
18583
18894
 
18584
18895
  const PinInput = factory((props, ref) => {
18585
- const { length = 4, value = '', onChange, mask = false, maskChar = '•', manageFocus = true, type = 'numeric', placeholder = '', allowPaste = true, oneTimeCode = false, spacing = 8, disabled = false, error, size = 'md', onComplete, textInputProps, label, helperText, style, keyboardFocusId, name, testID, ...spacingProps } = props;
18896
+ const { length = 4, value = '', onChange, mask = false, maskChar = '•', manageFocus = true, type = 'numeric', placeholder = '', allowPaste = true, oneTimeCode = false, spacing = 8, disabled = false, error, size = 'md', onComplete, textInputProps, label, helperText, style, keyboardFocusId, name, testID,
18897
+ // Native TextInput passthrough props
18898
+ autoCapitalize, autoCorrect, autoFocus, selectTextOnFocus: selectTextOnFocusProp, textContentType: textContentTypeProp, textAlign, spellCheck, selectionColor, showSoftInputOnFocus, ...spacingProps } = props;
18586
18899
  const theme = useTheme();
18587
18900
  const inputRefs = useRef([]);
18588
18901
  const [focusedIndex, setFocusedIndex] = useState(-1);
@@ -18781,15 +19094,15 @@ const PinInput = factory((props, ref) => {
18781
19094
  inputRefs.current[index] = ref;
18782
19095
  }, style: getInputStyle(index), value: mask && digit ? maskChar : digit, onChangeText: (text) => handleChangeText(text, index), onKeyPress: Platform.OS === 'web' ? ({ nativeEvent }) => {
18783
19096
  handleKeyPress(nativeEvent.key, index);
18784
- } : undefined, onFocus: () => handleFocus(index), onBlur: handleBlur, maxLength: allowPaste ? undefined : 1, keyboardType: type === 'numeric' ? 'number-pad' : 'default', textContentType: oneTimeCode ? 'oneTimeCode' : undefined, autoComplete: oneTimeCode ? 'one-time-code' : 'off', selectTextOnFocus: true, editable: !disabled, placeholder: placeholder, placeholderTextColor: theme.text.muted, ...textInputProps }, index))) }), error && (jsx(Text, { style: {
19097
+ } : undefined, onFocus: () => handleFocus(index), onBlur: handleBlur, maxLength: allowPaste ? undefined : 1, keyboardType: type === 'numeric' ? 'number-pad' : 'default', textContentType: oneTimeCode ? 'oneTimeCode' : (textContentTypeProp !== null && textContentTypeProp !== void 0 ? textContentTypeProp : undefined), autoComplete: oneTimeCode ? 'one-time-code' : 'off', selectTextOnFocus: selectTextOnFocusProp !== null && selectTextOnFocusProp !== void 0 ? selectTextOnFocusProp : true, editable: !disabled, placeholder: placeholder, placeholderTextColor: theme.text.muted, autoCapitalize: autoCapitalize, autoCorrect: autoCorrect, autoFocus: autoFocus && index === 0, textAlign: textAlign, spellCheck: spellCheck, selectionColor: selectionColor, showSoftInputOnFocus: showSoftInputOnFocus, ...textInputProps }, index))) }), error ? (jsx(Text, { style: {
18785
19098
  marginTop: 4,
18786
19099
  fontSize: 12,
18787
19100
  color: theme.colors.error[5]
18788
- }, children: error })), helperText && !error && (jsx(Text, { style: {
19101
+ }, children: error })) : null, helperText && !error ? (jsx(Text, { style: {
18789
19102
  marginTop: 4,
18790
19103
  fontSize: 12,
18791
19104
  color: theme.text.muted
18792
- }, children: helperText }))] }));
19105
+ }, children: helperText })) : null] }));
18793
19106
  });
18794
19107
  PinInput.displayName = 'PinInput';
18795
19108
 
@@ -18960,7 +19273,7 @@ const Checkbox = React__default.forwardRef((props, ref) => {
18960
19273
  }
18961
19274
  return null;
18962
19275
  };
18963
- const labelContent = children || label;
19276
+ const labelContent = children || label || undefined;
18964
19277
  // Determine layout direction based on label position
18965
19278
  const isVertical = labelPosition === 'top' || labelPosition === 'bottom';
18966
19279
  const checkboxElement = (jsx(View, { style: styles.checkboxContainer, children: jsx(Pressable, { ref: ref, style: [styles.checkbox, style], onPress: handlePress, disabled: disabled, testID: testID, hitSlop: 12, accessibilityRole: "checkbox", accessibilityState: {
@@ -24325,7 +24638,7 @@ const Select = factory((allProps, ref) => {
24325
24638
  close();
24326
24639
  }, [disabled, valueProp, onChange, onClear, close]);
24327
24640
  const fieldContent = selectedOption ? (jsx(Text$1, { style: { color: disabled ? theme.text.disabled : theme.text.primary }, children: selectedOption.label })) : (jsx(Text$1, { style: { color: disabled ? theme.text.disabled : theme.text.muted }, children: placeholder }));
24328
- return (jsxs(View, { style: [defaultMinWidthStyle, fullWidthStyle, spacingStyles, layoutStyles], children: [jsx(FieldHeader, { label: label, description: description, disabled: disabled, error: !!error, size: size }), jsxs(Pressable, { ref: setTriggerNode, onPress: toggle, accessibilityRole: "button", accessibilityLabel: label || placeholder, disabled: disabled, style: [
24641
+ return (jsxs(View, { style: [defaultMinWidthStyle, fullWidthStyle, spacingStyles, layoutStyles], children: [jsx(FieldHeader, { label: label, description: description, disabled: disabled, error: !!error, size: size }), jsxs(Pressable, { ref: setTriggerNode, onPress: toggle, ...(Platform.OS === 'web' ? { role: 'combobox' } : { accessibilityRole: 'button' }), accessibilityLabel: label || placeholder, disabled: disabled, style: [
24329
24642
  inputStyles.inputContainer,
24330
24643
  {
24331
24644
  flexDirection: isRTL ? 'row-reverse' : 'row',
@@ -27641,7 +27954,7 @@ const TimePickerInput = ({ value, defaultValue, onChange, format = 24, withSecon
27641
27954
  color: active ? 'white' : theme.colors.gray[8],
27642
27955
  fontSize: 16,
27643
27956
  }, children: pad(n) }) }, n));
27644
- return (jsxs(View, { ref: containerRef, style: [containerStyles, inputWidth != null ? { width: inputWidth } : null, style], children: [jsx(Pressable, { onPress: handleOpen, disabled: disabled, accessibilityRole: "button", children: jsx(Input, { value: display, onChangeText: (text) => {
27957
+ return (jsxs(View, { ref: containerRef, style: [containerStyles, inputWidth != null ? { width: inputWidth } : null, style], children: [jsx(Pressable, { onPress: handleOpen, disabled: disabled, ...(Platform.OS === 'web' ? { role: 'group' } : { accessibilityRole: 'button' }), children: jsx(Input, { value: display, onChangeText: (text) => {
27645
27958
  var _a, _b;
27646
27959
  if (!allowInput)
27647
27960
  return;
@@ -30797,7 +31110,10 @@ const StepperStep = forwardRef(({ children, label, description, icon, completedI
30797
31110
  });
30798
31111
  // Completed Component
30799
31112
  const StepperCompleted = ({ children }) => {
30800
- return jsx(View, { children: children });
31113
+ const content = typeof children === 'string' || typeof children === 'number'
31114
+ ? jsx(Text$1, { children: children })
31115
+ : children;
31116
+ return jsx(View, { children: content });
30801
31117
  };
30802
31118
  // Main Stepper Component
30803
31119
  const Stepper = forwardRef(({ active, onStepClick, orientation = 'horizontal', iconPosition = 'left', iconSize, size = 'md', color, completedIcon, allowNextStepsSelect = true, children, 'aria-label': ariaLabel, ...props }, ref) => {
@@ -30866,7 +31182,10 @@ const Stepper = forwardRef(({ active, onStepClick, orientation = 'horizontal', i
30866
31182
  return completedContent;
30867
31183
  }
30868
31184
  if (currentStepContent) {
30869
- return jsx(View, { style: getContentStyles(), children: currentStepContent });
31185
+ const content = typeof currentStepContent === 'string' || typeof currentStepContent === 'number'
31186
+ ? jsx(Text$1, { children: currentStepContent })
31187
+ : currentStepContent;
31188
+ return jsx(View, { style: getContentStyles(), children: content });
30870
31189
  }
30871
31190
  return null;
30872
31191
  };
@@ -32950,8 +33269,6 @@ expandableRowRender, initialExpandedRows = [], expandedRows: controlledExpandedR
32950
33269
  const [editValue, setEditValue] = useState('');
32951
33270
  // Uncontrolled column filters state (used when onFilterChange not provided)
32952
33271
  const [internalFilters, setInternalFilters] = useState([]);
32953
- // Force re-render counter for portal components
32954
- const [forceUpdateCounter, setForceUpdateCounter] = useState(0);
32955
33272
  const [tempHeaderEdits, setTempHeaderEdits] = useState({});
32956
33273
  const [columnWidths, setColumnWidths] = useState(() => {
32957
33274
  const initial = {};
@@ -33179,7 +33496,6 @@ expandableRowRender, initialExpandedRows = [], expandedRows: controlledExpandedR
33179
33496
  paddingBottom: DESIGN_TOKENS.spacing.sm,
33180
33497
  }, children: jsxs(Flex, { direction: "column", gap: DESIGN_TOKENS.spacing.xs, children: [jsx(Text, { variant: "small", weight: "semibold", children: "Search" }), jsx(Input, { placeholder: searchPlaceholder, value: searchValue, onChangeText: handleSearchChange, startSection: jsx(Icon, { name: "menu", size: 16 }), size: "sm" })] }) })), columns.some(c => c.filterable) && (jsxs(Flex, { direction: "column", gap: DESIGN_TOKENS.spacing.sm, children: [jsxs(Flex, { direction: "row", justify: "space-between", align: "center", children: [jsx(Text, { variant: "small", weight: "semibold", children: "Filters" }), activeFilters.length > 0 && (jsx(Button, { variant: "ghost", size: "xs", onPress: () => {
33181
33498
  setInternalFilters([]);
33182
- setForceUpdateCounter(c => c + 1);
33183
33499
  }, children: "Clear all" }))] }), activeFilters.length > 0 && (jsx(Flex, { direction: "column", gap: DESIGN_TOKENS.spacing.xs, style: { marginBottom: DESIGN_TOKENS.spacing.sm }, children: activeFilters.map((filter, idx) => {
33184
33500
  const column = columns.find(c => c.key === filter.column);
33185
33501
  return (jsxs(View, { style: {
@@ -33192,7 +33508,6 @@ expandableRowRender, initialExpandedRows = [], expandedRows: controlledExpandedR
33192
33508
  gap: DESIGN_TOKENS.spacing.xs
33193
33509
  }, children: [jsxs(Text, { variant: "small", style: { color: theme.colors.primary[7] }, children: [(column === null || column === void 0 ? void 0 : column.header) || filter.column, ": ", filter.operator, " \"", filter.value, "\""] }), jsx(Pressable, { onPress: () => {
33194
33510
  setInternalFilters(filters => filters.filter((_, i) => i !== idx));
33195
- setForceUpdateCounter(c => c + 1);
33196
33511
  }, children: jsx(Icon, { name: "x", size: 12, color: theme.colors.primary[6] }) })] }, idx));
33197
33512
  }) })), jsx(Flex, { direction: "column", gap: DESIGN_TOKENS.spacing.sm, children: columns.filter(c => c.filterable).map(column => {
33198
33513
  const currentFilter = getColumnFilter(column.key);
@@ -33248,7 +33563,6 @@ expandableRowRender, initialExpandedRows = [], expandedRows: controlledExpandedR
33248
33563
  return next;
33249
33564
  });
33250
33565
  }
33251
- setForceUpdateCounter(c => c + 1);
33252
33566
  }, [onFilterChange, filters]);
33253
33567
  const clearFilter = useCallback((columnKey) => {
33254
33568
  updateFilter(columnKey, undefined);
@@ -39691,7 +40005,7 @@ function VideoControls({ config, state, onPlay, onPause, onSeek, onVolumeChange,
39691
40005
  const displayTime = isScrubbing
39692
40006
  ? (scrubbingValue / 100) * state.duration
39693
40007
  : state.currentTime;
39694
- const styles = StyleSheet.create({
40008
+ const styles = useMemo(() => StyleSheet.create({
39695
40009
  activeRate: {
39696
40010
  backgroundColor: theme.colors.primary[5],
39697
40011
  },
@@ -39772,7 +40086,7 @@ function VideoControls({ config, state, onPlay, onPause, onSeek, onVolumeChange,
39772
40086
  padding: 8,
39773
40087
  width: 120,
39774
40088
  },
39775
- });
40089
+ }), [theme.colors.primary, isRTL]);
39776
40090
  return (jsxs(View, { style: [styles.container, style], children: [config.progress && (jsx(View, { style: styles.topRow, children: jsx(View, { style: styles.progressContainer, children: jsx(Slider, { value: progressPercentage, min: 0, max: 100, step: 0.1, onChange: handleProgressChange, trackColor: "rgba(255, 255, 255, 0.3)", thumbColor: "white", activeTrackColor: theme.colors.primary[5] }) }) })), jsxs(View, { style: styles.bottomRow, children: [jsxs(View, { style: styles.leftControls, children: [(config.play || config.pause) && (jsx(TouchableOpacity, { style: styles.playPauseButton, onPress: state.playing ? onPause : onPlay, accessible: true, accessibilityLabel: state.playing ? 'Pause' : 'Play', children: jsx(Icon, { name: state.playing ? 'pause' : 'play', size: 24, color: "white" }) })), config.time && (jsxs(Text, { style: styles.timeText, children: [formatTime(displayTime), " / ", formatTime(state.duration)] }))] }), jsxs(View, { style: styles.rightControls, children: [config.playbackRate && (jsxs(View, { style: { position: 'relative' }, children: [jsx(TouchableOpacity, { style: styles.controlButton, onPress: () => setShowRateMenu(!showRateMenu), accessible: true, accessibilityLabel: "Playback speed", children: jsxs(Text, { style: styles.timeText, children: [state.playbackRate, "x"] }) }), showRateMenu && (jsx(View, { style: styles.rateMenu, children: PLAYBACK_RATES.map(rate => (jsx(TouchableOpacity, { style: [
39777
40091
  styles.rateMenuItem,
39778
40092
  state.playbackRate === rate && styles.activeRate
@@ -39797,13 +40111,10 @@ const EVENT_TYPE_COLORS = {
39797
40111
  function VideoTimeline({ timeline, duration, currentTime, onSeek, style }) {
39798
40112
  useTheme();
39799
40113
  const { isRTL } = useDirection();
39800
- if (duration === 0 || timeline.length === 0) {
39801
- return null;
39802
- }
39803
40114
  const handleMarkerPress = (event) => {
39804
40115
  onSeek(event.time);
39805
40116
  };
39806
- const styles = StyleSheet.create({
40117
+ const styles = useMemo(() => StyleSheet.create({
39807
40118
  activeMarker: {
39808
40119
  borderRadius: 2,
39809
40120
  height: 8,
@@ -39838,7 +40149,10 @@ function VideoTimeline({ timeline, duration, currentTime, onSeek, style }) {
39838
40149
  fontSize: 10,
39839
40150
  textAlign: 'center',
39840
40151
  },
39841
- });
40152
+ }), []);
40153
+ if (duration === 0 || timeline.length === 0) {
40154
+ return null;
40155
+ }
39842
40156
  return (jsx(View, { style: [styles.container, style], children: timeline.map((event, index) => {
39843
40157
  var _a;
39844
40158
  const position = (event.time / duration) * 100;
@@ -40950,5 +41264,5 @@ function withPressAnimation(Component, animationProps) {
40950
41264
  */
40951
41265
  const AnimatedPressable = PressAnimation;
40952
41266
 
40953
- export { AccessibilityProvider, Accordion, AmazonAppstoreBadge, AmazonAppstoreButton, AmazonMusicListenBadge, AmazonPrimeVideoBadge, AmazonStoreBadge, AnimatedPressable, AppLayoutProvider, AppLayoutRenderer, AppShell, AppShellAside, AppShellBottomNav, AppShellFooter, AppShellHeader, AppShellMain, AppShellNavbar, AppShellSection, AppStoreBadge, AppStoreButton, AppStoreDownloadBadge, AppleAppStoreButton, AppleMusicListenBadge, ApplePodcastsListenBadge, AutoComplete, Avatar, AvatarGroup, Badge, Block, Blockquote, Bold, BottomAppBar, BrandButton, BrandIcon, Breadcrumbs, Button, COMPONENT_SIZES$1 as COMPONENT_SIZES, COMPONENT_SIZE_ORDER, Calendar, Card, Carousel, Checkbox, Chip, ChromeWebStoreBadge, Cite, Code, CodeBlock, Collapse, ColorPicker, ColorSwatch, Column, ComponentWithDisclaimer, ContextMenu, CopyButton, DARK_THEME, DEFAULT_BREAKPOINTS, DEFAULT_COMPONENT_SIZE, DEFAULT_SOUND_IDS, DEFAULT_THEME, DataTable, DatePicker, DatePickerInput, Day, Dialog, DialogProvider, DialogRenderer, DirectionProvider, Disclaimer, DiscordJoinBadge, Divider, EmojiPicker, Emphasis, FDroidButton, FileInput, Flex, FloatingActions, Form, GalaxyStoreDownloadBadge, Gallery, Gauge, GitHubViewBadge, GooglePlayButton, GooglePlayDownloadBadge, Grid, GridItem, H1, H2, H3, H4, H5, H6, HapticsProvider, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, Highlight, HuaweiAppGalleryBadge, I18nProvider, Icon, IconButton, Image, Indicator, Input, Italic, Kbd, KeyCap, KeyboardAwareLayout, KeyboardManagerProvider, Knob, Link, ListGroup, ListGroupBody, ListGroupDivider, ListGroupItem, Loader, LoadingOverlay, MacAppStoreButton, Mark, Markdown, Masonry, Menu, MenuDivider, MenuDropdown, MenuItem, MenuItemButton, MenuLabel, MicrosoftStoreButton, MicrosoftStoreDownloadBadge, MiniCalendar, Month, MonthPicker, MonthPickerInput, Notice, NumberInput, Overlay, OverlayProvider, P, Pagination, PasswordInput, PhoneInput, PinInput, PlatformBlocksProvider, Popover, PressAnimation, Progress, QRCode, Radio, RadioGroup, RangeSlider, Rating, RedditJoinBadge, Ring, Row, SIZE_SCALES, Search, SegmentedControl, Select, ShimmerText, Skeleton, Slider, Small, SoundCloudListenBadge, SoundProvider, Space, Spoiler, SpotifyListenBadge, Spotlight, SpotlightProvider, StatusBarManager, StepperWithSubComponents as Stepper, Strong, Sub, Sup, Switch, Table, TableOfContents, Tabs, Text, TextArea, TextInputBase, ThemeModeProvider, TikTokWatchBadge, TimePickerInput as TimePicker, TimePickerInput, TimelineWithItems as Timeline, Title, TitleRegistryProvider, Toast, ToastProvider, ToggleBar, ToggleButton, ToggleGroup, Tooltip, Tree, TwitchWatchBadge, Underline, Video, Waveform, YearPicker, YearPickerInput, YouTubeMusicListenBadge, YouTubeWatchBadge, calculateOverlayPositionEnhanced, clampComponentSize, clearOverlayPositionCache, createSound, createSpotlightStore, createTheme, debounce$1 as debounce, defineAppLayout, directSpotlight, extractDisclaimerProps, factory, getAllSounds, getColor, getFontSize, getHeight, getIconSize$1 as getIconSize, getLineHeight, getRadius$1 as getRadius, getScrollPosition, getShadow$1 as getShadow, getSize, getSoundsByCategory, getSpacing, getViewport, globalHotkeys, measureAsyncPerformance, measureElement, measurePerformance, onDialogsRequested, onSpotlightRequested, onToastsRequested, pointInRect, polymorphicFactory, px, rem, resolveComponentSize, resolveResponsiveProp, resolveResponsiveValue, resolveSize, spotlight, throttle, useAccessibility, useAppLayoutContext, useAppShell, useAppShellApi, useAppShellLayout, useBreakpoint, useColorScheme, useDeviceInfo, useDialog, useDialogApi, useDialogs, useDirectSpotlightState, useDirection, useDirectionSafe, useDisclaimer, useDropdownPositioning, useEscapeKey, useFormContext, useGlobalHotkeys, useHaptics, useHapticsSettings, useHotkeys, useI18n, useKeyboardManager, useKeyboardManagerOptional, useNavbarHover, useOptionalFormContext, useOverlay, useOverlayApi, useOverlays, usePopoverPositioning, useSimpleDialog, useSound, useSpotlightStore, useSpotlightStoreInstance, useSpotlightToggle, useTheme, useThemeMode, useTitleRegistry, useTitleRegistryOptional, useToast, useToastApi, useToggleColorScheme, useTooltipPositioning, withDisclaimer, withPressAnimation };
41267
+ export { AccessibilityProvider, Accordion, AmazonAppstoreBadge, AmazonAppstoreButton, AmazonMusicListenBadge, AmazonPrimeVideoBadge, AmazonStoreBadge, AnimatedPressable, AppLayoutProvider, AppLayoutRenderer, AppShell, AppShellAside, AppShellBottomNav, AppShellFooter, AppShellHeader, AppShellMain, AppShellNavbar, AppShellSection, AppStoreBadge, AppStoreButton, AppStoreDownloadBadge, AppleAppStoreButton, AppleMusicListenBadge, ApplePodcastsListenBadge, AutoComplete, Avatar, AvatarGroup, Badge, Block, Blockquote, Bold, BottomAppBar, BrandButton, BrandIcon, Breadcrumbs, BreakpointProvider, Button, COMPONENT_SIZES$1 as COMPONENT_SIZES, COMPONENT_SIZE_ORDER, Calendar, Card, Carousel, Checkbox, Chip, ChromeWebStoreBadge, Cite, Code, CodeBlock, Collapse, ColorPicker, ColorSwatch, Column, ComponentWithDisclaimer, ContextMenu, CopyButton, DARK_THEME, DEFAULT_BREAKPOINTS, DEFAULT_COMPONENT_SIZE, DEFAULT_SOUND_IDS, DEFAULT_THEME, DataTable, DatePicker, DatePickerInput, Day, Dialog, DialogProvider, DialogRenderer, DirectionProvider, Disclaimer, DiscordJoinBadge, Divider, EmojiPicker, Emphasis, FDroidButton, FileInput, Flex, FloatingActions, Form, GalaxyStoreDownloadBadge, Gallery, Gauge, GitHubViewBadge, GooglePlayButton, GooglePlayDownloadBadge, Grid, GridItem, H1, H2, H3, H4, H5, H6, HapticsProvider, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, Highlight, HuaweiAppGalleryBadge, I18nProvider, Icon, IconButton, Image, Indicator, Input, Italic, Kbd, KeyCap, KeyboardAwareLayout, KeyboardManagerProvider, Knob, Link, ListGroup, ListGroupBody, ListGroupDivider, ListGroupItem, Loader, LoadingOverlay, MacAppStoreButton, Mark, Markdown, Masonry, Menu, MenuDivider, MenuDropdown, MenuItem, MenuItemButton, MenuLabel, MicrosoftStoreButton, MicrosoftStoreDownloadBadge, MiniCalendar, Month, MonthPicker, MonthPickerInput, Notice, NumberInput, Overlay, OverlayProvider, P, Pagination, PasswordInput, PhoneInput, PinInput, PlatformBlocksProvider, Popover, PressAnimation, Progress, QRCode, Radio, RadioGroup, RangeSlider, Rating, RedditJoinBadge, Ring, Row, SIZE_SCALES, Search, SegmentedControl, Select, ShimmerText, Skeleton, Slider, Small, SoundCloudListenBadge, SoundProvider, Space, Spoiler, SpotifyListenBadge, Spotlight, SpotlightProvider, StatusBarManager, StepperWithSubComponents as Stepper, Strong, Sub, Sup, Switch, Table, TableOfContents, Tabs, Text, TextArea, TextInputBase, ThemeModeProvider, TikTokWatchBadge, TimePickerInput as TimePicker, TimePickerInput, TimelineWithItems as Timeline, Title, TitleRegistryProvider, Toast, ToastProvider, ToggleBar, ToggleButton, ToggleGroup, Tooltip, Tree, TwitchWatchBadge, Underline, Video, Waveform, YearPicker, YearPickerInput, YouTubeMusicListenBadge, YouTubeWatchBadge, calculateOverlayPositionEnhanced, clampComponentSize, clearOverlayPositionCache, createSound, createSpotlightStore, createTheme, debounce$1 as debounce, defineAppLayout, directSpotlight, extractDisclaimerProps, factory, getAllSounds, getColor, getFontSize, getHeight, getIconSize$1 as getIconSize, getLineHeight, getRadius$1 as getRadius, getScrollPosition, getShadow$1 as getShadow, getSize, getSoundsByCategory, getSpacing, getViewport, globalHotkeys, measureAsyncPerformance, measureElement, measurePerformance, onDialogsRequested, onSpotlightRequested, onToastsRequested, pointInRect, polymorphicFactory, px, rem, resolveComponentSize, resolveResponsiveProp, resolveResponsiveValue, resolveSize, spotlight, throttle, useAccessibility, useAppLayoutContext, useAppShell, useAppShellApi, useAppShellLayout, useBreakpoint, useClipboard, useColorScheme, useDeviceInfo, useDialog, useDialogApi, useDialogs, useDirectSpotlightState, useDirection, useDirectionSafe, useDisclaimer, useDropdownPositioning, useEscapeKey, useFormContext, useGlobalHotkeys, useHaptics, useHapticsSettings, useHotkeys, useI18n, useKeyboardManager, useKeyboardManagerOptional, useMaskedInput, useNavbarHover, useOptionalFormContext, useOverlay, useOverlayApi, useOverlayMode, useOverlays, usePopoverPositioning, useScrollSpy, useSimpleDialog, useSound, useSpotlightStore, useSpotlightStoreInstance, useSpotlightToggle, useTheme, useThemeLayout, useThemeMode, useThemeVisuals, useTitleRegistration, useTitleRegistry, useTitleRegistryOptional, useToast, useToastApi, useToggleColorScheme, useTooltipPositioning, withDisclaimer, withPressAnimation };
40954
41268
  //# sourceMappingURL=index.js.map