@pallasoft/ps-mobile-components 1.12.0 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/components/ps-action-sheet/ps-action-sheet.js +18 -2
  2. package/dist/components/ps-barcode-scanner-sheet/ps-barcode-scanner-sheet.js +2 -1
  3. package/dist/components/ps-chart-bar/ps-chart-bar.js +11 -4
  4. package/dist/components/ps-document-viewer/ps-document-viewer.js +2 -1
  5. package/dist/components/ps-document-zoom-image/ps-document-zoom-image.js +10 -0
  6. package/dist/components/ps-donut-chart/ps-donut-chart.js +5 -4
  7. package/dist/components/ps-donut-reveal/index.d.ts +1 -0
  8. package/dist/components/ps-donut-reveal/index.js +1 -0
  9. package/dist/components/ps-donut-reveal/ps-donut-reveal-props.d.ts +6 -0
  10. package/dist/components/ps-donut-reveal/ps-donut-reveal.d.ts +2 -0
  11. package/dist/components/ps-donut-reveal/ps-donut-reveal.js +14 -0
  12. package/dist/components/ps-donut-slice/ps-donut-slice-props.d.ts +0 -4
  13. package/dist/components/ps-donut-slice/ps-donut-slice.d.ts +1 -1
  14. package/dist/components/ps-donut-slice/ps-donut-slice.js +2 -14
  15. package/dist/components/ps-infinite-list/ps-infinite-list-props.d.ts +2 -0
  16. package/dist/components/ps-infinite-list/ps-infinite-list.d.ts +1 -1
  17. package/dist/components/ps-infinite-list/ps-infinite-list.js +12 -5
  18. package/dist/components/ps-list/ps-list-props.d.ts +2 -0
  19. package/dist/components/ps-list/ps-list.d.ts +1 -1
  20. package/dist/components/ps-list/ps-list.js +1 -1
  21. package/dist/components/ps-menu-popup/ps-menu-popup.js +3 -2
  22. package/dist/components/ps-screen/ps-screen-props.d.ts +2 -0
  23. package/dist/components/ps-screen/ps-screen.d.ts +1 -1
  24. package/dist/components/ps-screen/ps-screen.js +12 -5
  25. package/dist/components/ps-sheet-content/ps-sheet-content.js +4 -2
  26. package/dist/components/ps-sheet-presenter/ps-sheet-presenter.ios.js +15 -5
  27. package/dist/components/ps-sheet-presenter/ps-sheet-presenter.js +16 -3
  28. package/dist/components/ps-sortable-grid/ps-sortable-grid.js +7 -4
  29. package/dist/components/ps-sortable-grid/ps-sortable-item-props.d.ts +1 -1
  30. package/dist/components/ps-sortable-grid/ps-sortable-item.js +19 -5
  31. package/dist/components/ps-widget-list/ps-widget-grid.js +16 -7
  32. package/dist/components/ps-widget-list/ps-widget-list-props.d.ts +2 -0
  33. package/dist/components/ps-widget-list/ps-widget-list.d.ts +1 -1
  34. package/dist/components/ps-widget-list/ps-widget-list.js +12 -5
  35. package/dist/constants/ps-chart-bar-corner.d.ts +6 -0
  36. package/dist/constants/ps-chart-bar-corner.js +5 -0
  37. package/dist/constants/ps-chart-config.d.ts +4 -1
  38. package/dist/constants/ps-chart-config.js +4 -1
  39. package/dist/constants/ps-content-width.d.ts +6 -0
  40. package/dist/constants/ps-content-width.js +5 -0
  41. package/dist/constants/ps-document-search-config.d.ts +0 -3
  42. package/dist/constants/ps-document-search-config.js +0 -3
  43. package/dist/constants/ps-layout-class-config.d.ts +12 -0
  44. package/dist/constants/ps-layout-class-config.js +12 -0
  45. package/dist/constants/ps-layout-class.d.ts +5 -0
  46. package/dist/constants/ps-layout-class.js +4 -0
  47. package/dist/constants/ps-menu-popup-config.d.ts +1 -0
  48. package/dist/constants/ps-menu-popup-config.js +1 -0
  49. package/dist/constants/ps-orientation-config.d.ts +3 -0
  50. package/dist/constants/ps-orientation-config.js +3 -0
  51. package/dist/constants/ps-sheet-config.d.ts +1 -0
  52. package/dist/constants/ps-sheet-config.js +1 -0
  53. package/dist/constants/ps-sortable-grid-config.d.ts +4 -0
  54. package/dist/constants/ps-sortable-grid-config.js +4 -0
  55. package/dist/hooks/use-ps-layout-class.d.ts +2 -0
  56. package/dist/hooks/use-ps-layout-class.js +6 -0
  57. package/dist/hooks/use-ps-menu-anchor.d.ts +1 -1
  58. package/dist/hooks/use-ps-menu-anchor.js +14 -2
  59. package/dist/index.d.ts +4 -0
  60. package/dist/index.js +4 -0
  61. package/dist/interfaces/ps-chart-arc-data.d.ts +9 -0
  62. package/dist/interfaces/ps-chart-arc-data.js +1 -0
  63. package/dist/interfaces/ps-chart-bar-rect.d.ts +2 -0
  64. package/dist/interfaces/ps-document-search-match.d.ts +2 -4
  65. package/dist/interfaces/ps-document-text-box.d.ts +2 -6
  66. package/dist/interfaces/ps-document-text-layer-word.d.ts +2 -5
  67. package/dist/interfaces/ps-document-text-layer.d.ts +2 -6
  68. package/dist/interfaces/ps-document-text-segment.d.ts +2 -4
  69. package/dist/interfaces/ps-donut-slice-data.d.ts +1 -3
  70. package/dist/interfaces/ps-menu-popup-insets.d.ts +2 -0
  71. package/dist/interfaces/ps-menu-popup-layout.d.ts +1 -0
  72. package/dist/interfaces/ps-sortable-measure-data.d.ts +4 -0
  73. package/dist/interfaces/ps-sortable-measure-data.js +1 -0
  74. package/dist/services/ps-chart-arc-service.d.ts +14 -0
  75. package/dist/services/ps-chart-arc-service.js +83 -0
  76. package/dist/services/ps-chart-bar-service.d.ts +2 -0
  77. package/dist/services/ps-chart-bar-service.js +18 -3
  78. package/dist/services/ps-chart-donut-service.d.ts +2 -0
  79. package/dist/services/ps-chart-donut-service.js +42 -12
  80. package/dist/services/ps-document-search-service.d.ts +0 -4
  81. package/dist/services/ps-document-search-service.js +9 -96
  82. package/dist/services/ps-layout-service.d.ts +12 -0
  83. package/dist/services/ps-layout-service.js +41 -0
  84. package/dist/services/ps-menu-popup-service.js +8 -4
  85. package/dist/services/ps-sheet-service.d.ts +5 -2
  86. package/dist/services/ps-sheet-service.js +17 -4
  87. package/dist/services/ps-sortable-grid-service.d.ts +3 -2
  88. package/dist/services/ps-sortable-grid-service.js +26 -22
  89. package/dist/services/ps-waterfall-service.js +2 -0
  90. package/package.json +2 -2
  91. package/dist/interfaces/ps-document-normalized-text.d.ts +0 -5
  92. /package/dist/{interfaces/ps-document-normalized-text.js → components/ps-donut-reveal/ps-donut-reveal-props.js} +0 -0
@@ -1,35 +1,51 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRef } from 'react';
3
3
  import { PsIconKey } from '@pallasoft/ps-icons';
4
- import { Modal, Pressable, StyleSheet, View } from 'react-native';
4
+ import { Modal, Pressable, StyleSheet, View, useWindowDimensions } from 'react-native';
5
5
  import Animated, { FadeInDown } from 'react-native-reanimated';
6
6
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
+ import { PsLayoutClass } from '../../constants/ps-layout-class';
7
8
  import { PsLayoutConfig } from '../../constants/ps-layout-config';
8
9
  import { PsMobileControlConfig } from '../../constants/ps-mobile-control-config';
10
+ import { PsOrientationConfig } from '../../constants/ps-orientation-config';
9
11
  import { PsRippleKind } from '../../constants/ps-ripple-kind';
12
+ import { usePsLayoutClass } from '../../hooks/use-ps-layout-class';
10
13
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
11
14
  import { PsActionSheetService } from '../../services/ps-action-sheet-service';
12
15
  import { PsRippleService } from '../../services/ps-ripple-service';
16
+ import { PsSheetService } from '../../services/ps-sheet-service';
13
17
  import { PsStateService } from '../../services/ps-state-service';
14
18
  import { PsIcon } from '../ps-icon/ps-icon';
15
19
  import { PsText } from '../ps-text/ps-text';
16
20
  export const PsActionSheet = ({ cancelLabel, isOpen, items, onClose, title, }) => {
17
21
  const theme = usePsMobileTheme();
18
22
  const insets = useSafeAreaInsets();
23
+ const layoutClass = usePsLayoutClass();
24
+ const { width: windowWidth } = useWindowDimensions();
25
+ const isRegular = layoutClass === PsLayoutClass.regular;
19
26
  const pending = useRef(undefined);
20
27
  const runPending = () => {
21
28
  const action = pending.current;
22
29
  pending.current = undefined;
23
30
  action?.();
24
31
  };
25
- return (_jsxs(Modal, { animationType: 'fade', onDismiss: runPending, onRequestClose: onClose, statusBarTranslucent: true, transparent: true, visible: isOpen, children: [_jsx(Pressable, { accessibilityElementsHidden: true, onPress: onClose, style: [StyleSheet.absoluteFill, { backgroundColor: theme.overlay }] }), _jsx(View, { pointerEvents: 'box-none', style: { flex: 1, justifyContent: 'flex-end' }, children: _jsxs(Animated.View, { entering: FadeInDown, style: {
32
+ return (_jsxs(Modal, { animationType: 'fade', onDismiss: runPending, onRequestClose: onClose, statusBarTranslucent: true, supportedOrientations: PsOrientationConfig.all, transparent: true, visible: isOpen, children: [_jsx(Pressable, { accessibilityElementsHidden: true, onPress: onClose, style: [StyleSheet.absoluteFill, { backgroundColor: theme.overlay }] }), _jsx(View, { pointerEvents: 'box-none', style: {
33
+ alignItems: isRegular ? 'center' : undefined,
34
+ flex: 1,
35
+ justifyContent: 'flex-end',
36
+ }, children: _jsxs(Animated.View, { entering: FadeInDown, style: {
26
37
  backgroundColor: theme.surface,
27
38
  borderColor: theme.line,
39
+ borderBottomLeftRadius: isRegular ? PsLayoutConfig.actionSheetRadius : 0,
40
+ borderBottomRightRadius: isRegular ? PsLayoutConfig.actionSheetRadius : 0,
28
41
  borderTopLeftRadius: PsLayoutConfig.actionSheetRadius,
29
42
  borderTopRightRadius: PsLayoutConfig.actionSheetRadius,
30
43
  borderWidth: 1,
44
+ marginBottom: isRegular ? PsLayoutConfig.gutter : 0,
45
+ maxWidth: PsSheetService.fittedMaxWidth(windowWidth, layoutClass),
31
46
  paddingBottom: insets.bottom + PsLayoutConfig.sheetFooterGap,
32
47
  paddingTop: PsLayoutConfig.sheetFooterGap,
48
+ width: isRegular ? '100%' : undefined,
33
49
  }, testID: 'ps-action-sheet', children: [title === undefined ? null : (_jsx(View, { style: { paddingHorizontal: PsLayoutConfig.gutter, paddingVertical: 8 }, children: _jsx(PsText, { tone: 'muted', variant: 'label', children: title }) })), items.map((item) => (_jsxs(Pressable, { android_ripple: PsRippleService.resolve(theme, PsRippleKind.Surface), accessibilityRole: 'menuitem', accessibilityState: { disabled: item.isDisabled === true }, disabled: item.isDisabled, onPress: () => {
34
50
  pending.current = item.onSelect;
35
51
  onClose();
@@ -6,6 +6,7 @@ import { Linking, Modal, StatusBar, StyleSheet, View, useWindowDimensions } from
6
6
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
7
  import { PsBarcodePermission } from '../../constants/ps-barcode-permission';
8
8
  import { PsBarcodeScannerConfig } from '../../constants/ps-barcode-scanner-config';
9
+ import { PsOrientationConfig } from '../../constants/ps-orientation-config';
9
10
  import { usePsHaptics } from '../../hooks/use-ps-haptics';
10
11
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
11
12
  import { PsBarcodeScannerService } from '../../services/ps-barcode-scanner-service';
@@ -41,7 +42,7 @@ export const PsBarcodeScannerSheet = ({ barcodeTypes, isContinuous = false, isOp
41
42
  if (!isContinuous)
42
43
  onClose();
43
44
  };
44
- return (_jsxs(Modal, { animationType: 'fade', onRequestClose: onClose, presentationStyle: 'fullScreen', visible: isOpen, children: [_jsx(StatusBar, { barStyle: 'light-content' }), _jsxs(View, { style: { backgroundColor: PsBarcodeScannerConfig.backdropColor, flex: 1 }, testID: 'ps-barcode-scanner', children: [Camera !== null && permission === PsBarcodePermission.granted ? (_jsx(Camera, { barcodeScannerSettings: {
45
+ return (_jsxs(Modal, { animationType: 'fade', onRequestClose: onClose, presentationStyle: 'fullScreen', supportedOrientations: PsOrientationConfig.all, visible: isOpen, children: [_jsx(StatusBar, { barStyle: 'light-content' }), _jsxs(View, { style: { backgroundColor: PsBarcodeScannerConfig.backdropColor, flex: 1 }, testID: 'ps-barcode-scanner', children: [Camera !== null && permission === PsBarcodePermission.granted ? (_jsx(Camera, { barcodeScannerSettings: {
45
46
  barcodeTypes: PsBarcodeScannerService.types(barcodeTypes),
46
47
  }, enableTorch: isTorchOn, facing: PsBarcodeScannerConfig.facing, onBarcodeScanned: read, style: StyleSheet.absoluteFill })) : null, permission === PsBarcodePermission.granted ? (_jsx(View, { pointerEvents: 'none', style: [
47
48
  StyleSheet.absoluteFill,
@@ -1,12 +1,19 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Animated, { useAnimatedProps } from 'react-native-reanimated';
3
- import { Rect } from 'react-native-svg';
4
- import { PsChartConfig } from '../../constants/ps-chart-config';
3
+ import { G, Rect } from 'react-native-svg';
4
+ import { PsChartBarCorner } from '../../constants/ps-chart-bar-corner';
5
+ import { PsChartBarService } from '../../services/ps-chart-bar-service';
5
6
  const AnimatedRect = Animated.createAnimatedComponent(Rect);
6
7
  export const PsChartBar = ({ progress, rect }) => {
8
+ const radius = PsChartBarService.radius(rect);
9
+ const hasFlatBase = rect.corner === PsChartBarCorner.Value && radius > 0;
7
10
  const animatedProps = useAnimatedProps(() => ({
8
11
  height: rect.height * progress.value,
9
12
  y: rect.isNegative ? rect.y : rect.y + rect.height * (1 - progress.value),
10
13
  }));
11
- return (_jsx(AnimatedRect, { animatedProps: animatedProps, fill: rect.color, height: rect.height, rx: Math.min(PsChartConfig.barRadius, rect.width / 2), width: rect.width, x: rect.x, y: rect.y }));
14
+ const baseProps = useAnimatedProps(() => {
15
+ const height = Math.min(radius, rect.height * progress.value);
16
+ return { height, y: rect.isNegative ? rect.y : rect.y + rect.height - height };
17
+ });
18
+ return (_jsxs(G, { children: [_jsx(AnimatedRect, { animatedProps: animatedProps, fill: rect.color, height: rect.height, rx: radius, width: rect.width, x: rect.x, y: rect.y }), hasFlatBase ? (_jsx(AnimatedRect, { animatedProps: baseProps, fill: rect.color, height: Math.min(radius, rect.height), width: rect.width, x: rect.x, y: rect.isNegative ? rect.y : rect.y + rect.height - Math.min(radius, rect.height) })) : null] }));
12
19
  };
@@ -7,6 +7,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
7
  import { PsDocumentConfig } from '../../constants/ps-document-config';
8
8
  import { PsDocumentSearchConfig } from '../../constants/ps-document-search-config';
9
9
  import { PsMobileControlConfig } from '../../constants/ps-mobile-control-config';
10
+ import { PsOrientationConfig } from '../../constants/ps-orientation-config';
10
11
  import { usePsDocumentPdfPages } from '../../hooks/use-ps-document-pdf-pages';
11
12
  import { usePsDocumentSearchTexts } from '../../hooks/use-ps-document-search-texts';
12
13
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
@@ -72,7 +73,7 @@ export const PsDocumentViewer = ({ initialIndex = 0, isOpen, items, labels, onCl
72
73
  if (width > 0 && entries.length > 0)
73
74
  list.current?.scrollToIndex({ animated: false, index });
74
75
  }, [entries.length, index, width]);
75
- return (_jsxs(Modal, { animationType: 'fade', onRequestClose: onClose, presentationStyle: 'fullScreen', visible: isOpen, children: [_jsx(StatusBar, { barStyle: 'light-content' }), _jsxs(GestureHandlerRootView, { style: {
76
+ return (_jsxs(Modal, { animationType: 'fade', onRequestClose: onClose, presentationStyle: 'fullScreen', supportedOrientations: PsOrientationConfig.all, visible: isOpen, children: [_jsx(StatusBar, { barStyle: 'light-content' }), _jsxs(GestureHandlerRootView, { style: {
76
77
  backgroundColor: PsDocumentConfig.backdropColor,
77
78
  flex: 1,
78
79
  paddingBottom: insets.bottom,
@@ -100,6 +100,16 @@ export const PsDocumentZoomImage = ({ name, onDismiss, onError, onZoomChange, se
100
100
  baseX.value = nextX;
101
101
  baseY.value = nextY;
102
102
  }, [search?.activeIndex, search?.query]);
103
+ useEffect(() => {
104
+ scale.value = PsDocumentConfig.minZoom;
105
+ baseScale.value = PsDocumentConfig.minZoom;
106
+ x.value = 0;
107
+ y.value = 0;
108
+ baseX.value = 0;
109
+ baseY.value = 0;
110
+ setIsZoomed(false);
111
+ onZoomChange(false);
112
+ }, [size.height, size.width]);
103
113
  const gesture = useSimultaneousGestures(pinch, move, dismiss, doubleTap);
104
114
  const animatedStyle = useAnimatedStyle(() => ({
105
115
  transform: [
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useId, useState } from 'react';
3
3
  import { View } from 'react-native';
4
- import Svg from 'react-native-svg';
4
+ import Svg, { G } from 'react-native-svg';
5
5
  import { PsChartConfig } from '../../constants/ps-chart-config';
6
6
  import { PsChartSkeletonVariant } from '../../constants/ps-chart-skeleton-variant';
7
7
  import { usePsChartReveal } from '../../hooks/use-ps-chart-reveal';
@@ -12,6 +12,7 @@ import { PsChartDonutService } from '../../services/ps-chart-donut-service';
12
12
  import { PsChartEmptyState } from '../ps-chart-empty-state/ps-chart-empty-state';
13
13
  import { PsChartLegend } from '../ps-chart-legend/ps-chart-legend';
14
14
  import { PsChartSkeleton } from '../ps-chart-skeleton/ps-chart-skeleton';
15
+ import { PsDonutReveal } from '../ps-donut-reveal/ps-donut-reveal';
15
16
  import { PsDonutSlice } from '../ps-donut-slice/ps-donut-slice';
16
17
  import { PsText } from '../ps-text/ps-text';
17
18
  export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, formatShare, height = PsChartConfig.donutHeight, isLoading = false, revealKey, }) => {
@@ -19,6 +20,7 @@ export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, forma
19
20
  const { onLayout, width } = usePsChartWidth();
20
21
  const fill = usePsWidgetFill();
21
22
  const [selected, setSelected] = useState(null);
23
+ const maskId = `donut${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
22
24
  const progress = usePsChartReveal(!isLoading && width > 0, revealKey);
23
25
  if (isLoading)
24
26
  return _jsx(PsChartSkeleton, { height: height, variant: PsChartSkeletonVariant.Donut });
@@ -27,7 +29,6 @@ export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, forma
27
29
  const slices = PsChartDonutService.slices(data, theme.chart, size);
28
30
  if (slices.length === 0)
29
31
  return _jsx(PsChartEmptyState, { height: height, label: emptyLabel });
30
- const circumference = PsChartDonutService.circumference(size);
31
32
  const chosen = selected === null ? null : data[selected];
32
33
  const readValue = chosen === null
33
34
  ? centerValue
@@ -47,7 +48,7 @@ export const PsDonutChart = ({ centerLabel, centerValue, data, emptyLabel, forma
47
48
  justifyContent: 'center',
48
49
  minHeight: height + fill,
49
50
  rowGap: PsChartConfig.donutGap,
50
- }, testID: 'ps-donut-chart', children: [_jsxs(View, { style: { height: size, width: size }, children: [_jsx(Svg, { height: size, width: size, children: slices.map((slice, index) => (_jsx(PsDonutSlice, { circumference: circumference, isDimmed: selected !== null && selected !== index, onPress: () => setSelected(selected === index ? null : index), progress: progress, size: size, slice: slice }, slice.id))) }), _jsxs(View, { pointerEvents: 'none', style: {
51
+ }, testID: 'ps-donut-chart', children: [_jsxs(View, { style: { height: size, width: size }, children: [_jsxs(Svg, { height: size, width: size, children: [_jsx(PsDonutReveal, { id: maskId, progress: progress, size: size }), _jsx(G, { mask: `url(#${maskId})`, children: slices.map((slice, index) => slice.path === '' ? null : (_jsx(PsDonutSlice, { isDimmed: selected !== null && selected !== index, onPress: () => setSelected(selected === index ? null : index), slice: slice }, slice.id))) })] }), _jsxs(View, { pointerEvents: 'none', style: {
51
52
  alignItems: 'center',
52
53
  bottom: 0,
53
54
  justifyContent: 'center',
@@ -0,0 +1 @@
1
+ export * from './ps-donut-reveal';
@@ -0,0 +1 @@
1
+ export * from './ps-donut-reveal';
@@ -0,0 +1,6 @@
1
+ import type { SharedValue } from 'react-native-reanimated';
2
+ export interface PsDonutRevealProps {
3
+ id: string;
4
+ progress: SharedValue<number>;
5
+ size: number;
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { PsDonutRevealProps } from './ps-donut-reveal-props';
2
+ export declare const PsDonutReveal: ({ id, progress, size }: PsDonutRevealProps) => import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Animated, { useAnimatedProps } from 'react-native-reanimated';
3
+ import { Circle, Defs, Mask } from 'react-native-svg';
4
+ import { PsChartConfig } from '../../constants/ps-chart-config';
5
+ import { PsChartDonutService } from '../../services/ps-chart-donut-service';
6
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
7
+ export const PsDonutReveal = ({ id, progress, size }) => {
8
+ const centre = size / 2;
9
+ const circumference = PsChartDonutService.circumference(size);
10
+ const animatedProps = useAnimatedProps(() => ({
11
+ strokeDasharray: [progress.value * circumference, circumference],
12
+ }));
13
+ return (_jsx(Defs, { children: _jsx(Mask, { height: size, id: id, maskUnits: 'userSpaceOnUse', width: size, x: 0, y: 0, children: _jsx(AnimatedCircle, { animatedProps: animatedProps, cx: centre, cy: centre, fill: 'none', origin: `${centre}, ${centre}`, r: PsChartDonutService.radius(size), rotation: -90, stroke: 'white', strokeDasharray: [0, circumference], strokeWidth: PsChartConfig.donutThickness + PsChartConfig.donutRevealOverlap }) }) }));
14
+ };
@@ -1,10 +1,6 @@
1
- import type { SharedValue } from 'react-native-reanimated';
2
1
  import type { PsDonutSliceData } from '../../interfaces/ps-donut-slice-data';
3
2
  export interface PsDonutSliceProps {
4
- circumference: number;
5
3
  isDimmed: boolean;
6
4
  onPress: () => void;
7
- progress: SharedValue<number>;
8
- size: number;
9
5
  slice: PsDonutSliceData;
10
6
  }
@@ -1,2 +1,2 @@
1
1
  import type { PsDonutSliceProps } from './ps-donut-slice-props';
2
- export declare const PsDonutSlice: ({ circumference, isDimmed, onPress, progress, size, slice, }: PsDonutSliceProps) => import("react").JSX.Element;
2
+ export declare const PsDonutSlice: ({ isDimmed, onPress, slice }: PsDonutSliceProps) => import("react").JSX.Element;
@@ -1,16 +1,4 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import Animated, { useAnimatedProps } from 'react-native-reanimated';
3
- import { Circle } from 'react-native-svg';
2
+ import { Path } from 'react-native-svg';
4
3
  import { PsChartConfig } from '../../constants/ps-chart-config';
5
- import { PsChartDonutService } from '../../services/ps-chart-donut-service';
6
- const AnimatedCircle = Animated.createAnimatedComponent(Circle);
7
- export const PsDonutSlice = ({ circumference, isDimmed, onPress, progress, size, slice, }) => {
8
- const centre = size / 2;
9
- const animatedProps = useAnimatedProps(() => ({
10
- strokeDasharray: [
11
- Math.max(0, Math.min(slice.dashLength, progress.value * circumference - slice.start)),
12
- circumference,
13
- ],
14
- }));
15
- return (_jsx(AnimatedCircle, { animatedProps: animatedProps, cx: centre, cy: centre, fill: 'none', onPress: onPress, opacity: isDimmed ? 0.4 : 1, origin: `${centre}, ${centre}`, r: PsChartDonutService.radius(size), rotation: -90, stroke: slice.color, strokeDasharray: [slice.dashLength, circumference], strokeDashoffset: slice.dashOffset, strokeWidth: PsChartConfig.donutThickness }));
16
- };
4
+ export const PsDonutSlice = ({ isDimmed, onPress, slice }) => (_jsx(Path, { d: slice.path, fill: slice.color, fillRule: 'evenodd', onPress: onPress, opacity: isDimmed ? PsChartConfig.donutDimmedOpacity : 1 }));
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement, ReactNode } from 'react';
2
2
  import type { StyleProp, ViewStyle } from 'react-native';
3
+ import type { PsContentWidth } from '../../constants/ps-content-width';
3
4
  import type { PsListVariant } from '../../services/ps-list-service';
4
5
  import type { PsListSkeletonLayout } from '../ps-list/ps-list-props';
5
6
  export interface PsInfiniteSection<T> {
@@ -9,6 +10,7 @@ export interface PsInfiniteSection<T> {
9
10
  title?: string;
10
11
  }
11
12
  export interface PsInfiniteListProps<T> {
13
+ contentWidth?: PsContentWidth;
12
14
  empty?: ReactNode;
13
15
  hasMore: boolean;
14
16
  header?: ReactNode;
@@ -1,2 +1,2 @@
1
1
  import type { PsInfiniteListProps } from './ps-infinite-list-props';
2
- export declare const PsInfiniteList: <T>({ empty, hasMore, header, isLoading, isLoadingMore, items, keyExtractor, onEndReached, onRefresh, renderItem, sections, skeleton, style, variant, }: PsInfiniteListProps<T>) => import("react").JSX.Element;
2
+ export declare const PsInfiniteList: <T>({ contentWidth, empty, hasMore, header, isLoading, isLoadingMore, items, keyExtractor, onEndReached, onRefresh, renderItem, sections, skeleton, style, variant, }: PsInfiniteListProps<T>) => import("react").JSX.Element;
@@ -1,34 +1,41 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Platform, RefreshControl, SectionList, View } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
+ import { PsContentWidth } from '../../constants/ps-content-width';
4
5
  import { PsInfiniteConfig } from '../../constants/ps-infinite-config';
5
6
  import { PsLayoutConfig } from '../../constants/ps-layout-config';
6
7
  import { PsSelectionBarConfig } from '../../constants/ps-selection-bar-config';
7
8
  import { PsScrollContext } from '../../contexts/ps-scroll-context';
9
+ import { usePsLayoutClass } from '../../hooks/use-ps-layout-class';
8
10
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
9
11
  import { usePsRefresh } from '../../hooks/use-ps-refresh';
10
12
  import { usePsScrollHost } from '../../hooks/use-ps-scroll-host';
11
13
  import { usePsTabBar } from '../../hooks/use-ps-tab-bar';
12
14
  import { usePsTopInset } from '../../hooks/use-ps-top-inset';
13
15
  import { PsInfiniteListService } from '../../services/ps-infinite-list-service';
16
+ import { PsLayoutService } from '../../services/ps-layout-service';
14
17
  import { PsListService } from '../../services/ps-list-service';
15
18
  import { PsScreenService } from '../../services/ps-screen-service';
16
19
  import { PsSkeletonList } from '../ps-skeleton/ps-skeleton-list';
17
20
  import { PsText } from '../ps-text/ps-text';
18
21
  import { PsInfiniteListFooter } from './ps-infinite-list-footer';
19
22
  import { PsInfiniteListItem } from './ps-infinite-list-item';
20
- export const PsInfiniteList = ({ empty, hasMore, header, isLoading = false, isLoadingMore = false, items, keyExtractor, onEndReached, onRefresh, renderItem, sections, skeleton, style, variant = 'grouped', }) => {
23
+ export const PsInfiniteList = ({ contentWidth = PsContentWidth.readable, empty, hasMore, header, isLoading = false, isLoadingMore = false, items, keyExtractor, onEndReached, onRefresh, renderItem, sections, skeleton, style, variant = 'grouped', }) => {
21
24
  const theme = usePsMobileTheme();
22
25
  const insets = useSafeAreaInsets();
23
26
  const topInset = usePsTopInset();
24
27
  const { hasTabBar, isHidden: isTabBarHidden } = usePsTabBar();
25
28
  const { isRefreshing, refresh } = usePsRefresh(onRefresh);
26
29
  const host = usePsScrollHost();
30
+ const layoutClass = usePsLayoutClass();
27
31
  const shown = isLoading ? [] : PsInfiniteListService.sections(items, sections);
28
- return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(PsScrollContext, { value: host.context, children: _jsx(SectionList, { ListEmptyComponent: isLoading ? _jsx(PsSkeletonList, { layout: skeleton }) : _jsx(_Fragment, { children: empty }), ListFooterComponent: _jsx(PsInfiniteListFooter, { isLoadingMore: isLoadingMore }), ListHeaderComponent: header === undefined ? undefined : (_jsx(View, { style: { marginBottom: PsLayoutConfig.listGap }, children: header })), contentContainerStyle: PsScreenService.content({
29
- bottom: PsScreenService.bottomInset(insets.bottom, hasTabBar, isTabBarHidden),
30
- top: topInset,
31
- }, Platform.OS === 'ios', PsListService.hasGutter(variant), 0, isTabBarHidden ? PsSelectionBarConfig.height : 0), contentInsetAdjustmentBehavior: 'automatic', initialNumToRender: PsInfiniteConfig.initialNumToRender, keyExtractor: keyExtractor, keyboardDismissMode: 'interactive', keyboardShouldPersistTaps: 'handled', maxToRenderPerBatch: PsInfiniteConfig.maxToRenderPerBatch, onContentSizeChange: host.onContentSizeChange, onEndReached: () => {
32
+ return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(PsScrollContext, { value: host.context, children: _jsx(SectionList, { ListEmptyComponent: isLoading ? _jsx(PsSkeletonList, { layout: skeleton }) : _jsx(_Fragment, { children: empty }), ListFooterComponent: _jsx(PsInfiniteListFooter, { isLoadingMore: isLoadingMore }), ListHeaderComponent: header === undefined ? undefined : (_jsx(View, { style: { marginBottom: PsLayoutConfig.listGap }, children: header })), contentContainerStyle: [
33
+ PsScreenService.content({
34
+ bottom: PsScreenService.bottomInset(insets.bottom, hasTabBar, isTabBarHidden),
35
+ top: topInset,
36
+ }, Platform.OS === 'ios', PsListService.hasGutter(variant), 0, isTabBarHidden ? PsSelectionBarConfig.height : 0),
37
+ PsLayoutService.frame(layoutClass, contentWidth),
38
+ ], contentInsetAdjustmentBehavior: 'automatic', initialNumToRender: PsInfiniteConfig.initialNumToRender, keyExtractor: keyExtractor, keyboardDismissMode: 'interactive', keyboardShouldPersistTaps: 'handled', maxToRenderPerBatch: PsInfiniteConfig.maxToRenderPerBatch, onContentSizeChange: host.onContentSizeChange, onEndReached: () => {
32
39
  if (PsInfiniteListService.shouldLoadMore(hasMore, isLoading, isLoadingMore))
33
40
  onEndReached();
34
41
  }, onEndReachedThreshold: PsInfiniteConfig.endReachedThreshold, onLayout: host.onLayout, onScroll: host.onScroll, refreshControl: onRefresh === undefined ? undefined : (_jsx(RefreshControl, { colors: [theme.primaryText], onRefresh: () => void refresh(), progressBackgroundColor: theme.surface, refreshing: isRefreshing, tintColor: theme.primary })), renderItem: ({ index, item, section }) => (_jsx(PsInfiniteListItem, { count: section.data.length, index: index, children: renderItem(item) })), renderSectionFooter: ({ section }) => (_jsx(View, { style: {
@@ -1,6 +1,7 @@
1
1
  import type { ReactElement, ReactNode } from 'react';
2
2
  import type { PsIconKey } from '@pallasoft/ps-icons';
3
3
  import type { StyleProp, ViewStyle } from 'react-native';
4
+ import type { PsContentWidth } from '../../constants/ps-content-width';
4
5
  import type { PsMenuItem } from '../../interfaces/ps-menu-item';
5
6
  import type { PsBadgeTone } from '../../services/ps-badge-tone-service';
6
7
  import type { PsListVariant } from '../../services/ps-list-service';
@@ -26,6 +27,7 @@ export interface PsListSkeletonResolved {
26
27
  }
27
28
  export interface PsListProps {
28
29
  children: ReactNode;
30
+ contentWidth?: PsContentWidth;
29
31
  header?: ReactNode;
30
32
  isLoading?: boolean;
31
33
  onRefresh?: () => Promise<void>;
@@ -1,2 +1,2 @@
1
1
  import type { PsListProps } from './ps-list-props';
2
- export declare const PsList: ({ children, header, isLoading, onRefresh, skeleton, style, variant, }: PsListProps) => import("react").JSX.Element;
2
+ export declare const PsList: ({ children, contentWidth, header, isLoading, onRefresh, skeleton, style, variant, }: PsListProps) => import("react").JSX.Element;
@@ -3,4 +3,4 @@ import { PsLayoutConfig } from '../../constants/ps-layout-config';
3
3
  import { PsListService } from '../../services/ps-list-service';
4
4
  import { PsScreen } from '../ps-screen/ps-screen';
5
5
  import { PsSkeletonList } from '../ps-skeleton/ps-skeleton-list';
6
- export const PsList = ({ children, header, isLoading = false, onRefresh, skeleton, style, variant = 'grouped', }) => (_jsx(PsScreen, { gap: PsLayoutConfig.listGap, hasGutter: PsListService.hasGutter(variant), header: header, onRefresh: onRefresh, style: style, children: isLoading ? _jsx(PsSkeletonList, { layout: skeleton }) : children }));
6
+ export const PsList = ({ children, contentWidth, header, isLoading = false, onRefresh, skeleton, style, variant = 'grouped', }) => (_jsx(PsScreen, { contentWidth: contentWidth, gap: PsLayoutConfig.listGap, hasGutter: PsListService.hasGutter(variant), header: header, onRefresh: onRefresh, style: style, children: isLoading ? _jsx(PsSkeletonList, { layout: skeleton }) : children }));
@@ -4,6 +4,7 @@ import { Modal, Pressable, ScrollView, StyleSheet, useWindowDimensions } from 'r
4
4
  import Animated from 'react-native-reanimated';
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import { PsMenuPopupConfig } from '../../constants/ps-menu-popup-config';
7
+ import { PsOrientationConfig } from '../../constants/ps-orientation-config';
7
8
  import { usePsMaterialScheme } from '../../hooks/use-ps-material-scheme';
8
9
  import { usePsMenuPopupReveal } from '../../hooks/use-ps-menu-popup-reveal';
9
10
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
@@ -21,7 +22,7 @@ export const PsMenuPopup = ({ anchor, children, isFullWidth = false, onClose, })
21
22
  : PsMenuPopupService.layout(anchor, window, insets, size.height, size.width, isFullWidth);
22
23
  const container = PsMenuPopupStyleService.container(theme, scheme, isMaterial);
23
24
  const reveal = usePsMenuPopupReveal(size !== undefined, layout.isBelow, isMaterial);
24
- return (_jsxs(Modal, { animationType: isMaterial ? 'none' : 'fade', navigationBarTranslucent: true, onRequestClose: onClose, statusBarTranslucent: true, transparent: true, visible: true, children: [_jsx(Pressable, { accessibilityElementsHidden: true, onPress: onClose, style: StyleSheet.absoluteFill, testID: 'ps-menu-popup-backdrop' }), _jsx(Animated.View, { onLayout: (event) => {
25
+ return (_jsxs(Modal, { animationType: isMaterial ? 'none' : 'fade', navigationBarTranslucent: true, onRequestClose: onClose, statusBarTranslucent: true, supportedOrientations: PsOrientationConfig.all, transparent: true, visible: true, children: [_jsx(Pressable, { accessibilityElementsHidden: true, onPress: onClose, style: StyleSheet.absoluteFill, testID: 'ps-menu-popup-backdrop' }), _jsx(Animated.View, { onLayout: (event) => {
25
26
  if (size === undefined)
26
27
  setSize({
27
28
  height: event.nativeEvent.layout.height,
@@ -36,7 +37,7 @@ export const PsMenuPopup = ({ anchor, children, isFullWidth = false, onClose, })
36
37
  elevation: container.elevation,
37
38
  left: layout.left,
38
39
  maxHeight: layout.maxHeight,
39
- maxWidth: PsMenuPopupConfig.maxWidth,
40
+ maxWidth: Math.min(PsMenuPopupConfig.maxWidth, layout.maxWidth),
40
41
  minWidth: isFullWidth ? undefined : PsMenuPopupConfig.minWidth,
41
42
  overflow: 'hidden',
42
43
  position: 'absolute',
@@ -1,7 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { StyleProp, ViewStyle } from 'react-native';
3
+ import type { PsContentWidth } from '../../constants/ps-content-width';
3
4
  export interface PsScreenProps {
4
5
  children: ReactNode;
6
+ contentWidth?: PsContentWidth;
5
7
  gap?: number;
6
8
  hasGutter?: boolean;
7
9
  header?: ReactNode;
@@ -1,2 +1,2 @@
1
1
  import type { PsScreenProps } from './ps-screen-props';
2
- export declare const PsScreen: ({ children, gap, hasGutter, header, onRefresh, style, }: PsScreenProps) => import("react").JSX.Element;
2
+ export declare const PsScreen: ({ children, contentWidth, gap, hasGutter, header, onRefresh, style, }: PsScreenProps) => import("react").JSX.Element;
@@ -1,24 +1,31 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Platform, RefreshControl, ScrollView, View } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
+ import { PsContentWidth } from '../../constants/ps-content-width';
4
5
  import { PsLayoutConfig } from '../../constants/ps-layout-config';
5
6
  import { PsSelectionBarConfig } from '../../constants/ps-selection-bar-config';
6
7
  import { PsScrollContext } from '../../contexts/ps-scroll-context';
8
+ import { usePsLayoutClass } from '../../hooks/use-ps-layout-class';
7
9
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
8
10
  import { usePsRefresh } from '../../hooks/use-ps-refresh';
9
11
  import { usePsScrollHost } from '../../hooks/use-ps-scroll-host';
10
12
  import { usePsTabBar } from '../../hooks/use-ps-tab-bar';
11
13
  import { usePsTopInset } from '../../hooks/use-ps-top-inset';
14
+ import { PsLayoutService } from '../../services/ps-layout-service';
12
15
  import { PsScreenService } from '../../services/ps-screen-service';
13
- export const PsScreen = ({ children, gap = PsLayoutConfig.listGap, hasGutter = true, header, onRefresh, style, }) => {
16
+ export const PsScreen = ({ children, contentWidth = PsContentWidth.readable, gap = PsLayoutConfig.listGap, hasGutter = true, header, onRefresh, style, }) => {
14
17
  const theme = usePsMobileTheme();
15
18
  const insets = useSafeAreaInsets();
16
19
  const topInset = usePsTopInset();
17
20
  const { hasTabBar, isHidden: isTabBarHidden } = usePsTabBar();
18
21
  const { isRefreshing, refresh } = usePsRefresh(onRefresh);
19
22
  const host = usePsScrollHost();
20
- return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(ScrollView, { contentContainerStyle: PsScreenService.content({
21
- bottom: PsScreenService.bottomInset(insets.bottom, hasTabBar, isTabBarHidden),
22
- top: topInset,
23
- }, Platform.OS === 'ios', hasGutter, gap, isTabBarHidden ? PsSelectionBarConfig.height : 0), contentInsetAdjustmentBehavior: 'automatic', keyboardDismissMode: 'interactive', onContentSizeChange: host.onContentSizeChange, onLayout: host.onLayout, onScroll: host.onScroll, keyboardShouldPersistTaps: 'handled', refreshControl: onRefresh === undefined ? undefined : (_jsx(RefreshControl, { colors: [theme.primaryText], progressBackgroundColor: theme.surface, onRefresh: () => void refresh(), refreshing: isRefreshing, tintColor: theme.primary })), ref: host.scrollRef, scrollEnabled: !host.isLocked, scrollEventThrottle: PsLayoutConfig.scrollEventThrottle, showsVerticalScrollIndicator: false, style: [{ backgroundColor: theme.background, flex: 1 }, style], testID: 'ps-screen', children: _jsxs(PsScrollContext, { value: host.context, children: [header, children] }) }), header === undefined ? null : (_jsx(View, { pointerEvents: 'none', style: [PsScreenService.statusBarScrim(topInset), { backgroundColor: theme.background }], testID: 'ps-screen-status-bar-scrim' }))] }));
23
+ const layoutClass = usePsLayoutClass();
24
+ return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(ScrollView, { contentContainerStyle: [
25
+ PsScreenService.content({
26
+ bottom: PsScreenService.bottomInset(insets.bottom, hasTabBar, isTabBarHidden),
27
+ top: topInset,
28
+ }, Platform.OS === 'ios', hasGutter, gap, isTabBarHidden ? PsSelectionBarConfig.height : 0),
29
+ PsLayoutService.frame(layoutClass, contentWidth),
30
+ ], contentInsetAdjustmentBehavior: 'automatic', keyboardDismissMode: 'interactive', onContentSizeChange: host.onContentSizeChange, onLayout: host.onLayout, onScroll: host.onScroll, keyboardShouldPersistTaps: 'handled', refreshControl: onRefresh === undefined ? undefined : (_jsx(RefreshControl, { colors: [theme.primaryText], progressBackgroundColor: theme.surface, onRefresh: () => void refresh(), refreshing: isRefreshing, tintColor: theme.primary })), ref: host.scrollRef, scrollEnabled: !host.isLocked, scrollEventThrottle: PsLayoutConfig.scrollEventThrottle, showsVerticalScrollIndicator: false, style: [{ backgroundColor: theme.background, flex: 1 }, style], testID: 'ps-screen', children: _jsxs(PsScrollContext, { value: host.context, children: [header, children] }) }), header === undefined ? null : (_jsx(View, { pointerEvents: 'none', style: [PsScreenService.statusBarScrim(topInset), { backgroundColor: theme.background }], testID: 'ps-screen-status-bar-scrim' }))] }));
24
31
  };
@@ -4,6 +4,7 @@ import { Platform, ScrollView, View, useWindowDimensions } from 'react-native';
4
4
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
5
  import { PsLayoutConfig } from '../../constants/ps-layout-config';
6
6
  import { PsSheetMeasureContext } from '../../contexts/ps-sheet-measure-context';
7
+ import { usePsLayoutClass } from '../../hooks/use-ps-layout-class';
7
8
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
8
9
  import { PsSheetService } from '../../services/ps-sheet-service';
9
10
  import { PsSheetHeader } from '../ps-sheet-header/ps-sheet-header';
@@ -11,11 +12,12 @@ export const PsSheetContent = ({ children, footer, header, isScrollable }) => {
11
12
  const theme = usePsMobileTheme();
12
13
  const insets = useSafeAreaInsets();
13
14
  const { height } = useWindowDimensions();
15
+ const layoutClass = usePsLayoutClass();
14
16
  const report = useContext(PsSheetMeasureContext);
15
17
  const [headerHeight, setHeaderHeight] = useState(0);
16
18
  const [bodyHeight, setBodyHeight] = useState(0);
17
19
  const fitted = headerHeight > 0 && bodyHeight > 0
18
- ? PsSheetService.fittedHeight(headerHeight, bodyHeight, height)
20
+ ? PsSheetService.fittedHeight(headerHeight, bodyHeight, height, layoutClass)
19
21
  : null;
20
22
  useEffect(() => {
21
23
  if (fitted !== null)
@@ -35,6 +37,6 @@ export const PsSheetContent = ({ children, footer, header, isScrollable }) => {
35
37
  return (_jsxs(View, { style: {
36
38
  backgroundColor: theme.background,
37
39
  height: fitted === null ? undefined : fitted,
38
- maxHeight: PsSheetService.maxHeight(height),
40
+ maxHeight: PsSheetService.maxHeight(height, layoutClass),
39
41
  }, testID: 'ps-sheet', children: [_jsx(View, { onLayout: (event) => setHeaderHeight(event.nativeEvent.layout.height), children: _jsx(PsSheetHeader, { ...header }) }), isScrollable ? (_jsx(ScrollView, { automaticallyAdjustKeyboardInsets: true, contentContainerStyle: contentStyle, keyboardDismissMode: 'interactive', keyboardShouldPersistTaps: 'handled', onContentSizeChange: (_width, contentHeight) => setBodyHeight(contentHeight), style: { flexGrow: 0 }, children: body })) : (_jsx(View, { onLayout: (event) => setBodyHeight(event.nativeEvent.layout.height), style: contentStyle, children: body }))] }));
40
42
  };
@@ -1,12 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  import { BottomSheet, Group, RNHostView } from '@expo/ui/swift-ui';
4
- import { frame, interactiveDismissDisabled, presentationBackground, presentationDetents, presentationDragIndicator, } from '@expo/ui/swift-ui/modifiers';
5
- import { useWindowDimensions } from 'react-native';
4
+ import { frame, interactiveDismissDisabled, presentationBackground, presentationDetents, presentationDragIndicator, presentationSizing, } from '@expo/ui/swift-ui/modifiers';
5
+ import { Platform, useWindowDimensions } from 'react-native';
6
6
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
+ import { PsLayoutClass } from '../../constants/ps-layout-class';
7
8
  import { PsNativeTreeContext } from '../../contexts/ps-native-tree-context';
8
9
  import { PsSheetMeasureContext } from '../../contexts/ps-sheet-measure-context';
9
10
  import { usePsKeyboardHeight } from '../../hooks/use-ps-keyboard-height';
11
+ import { usePsLayoutClass } from '../../hooks/use-ps-layout-class';
10
12
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
11
13
  import { PsSheetService } from '../../services/ps-sheet-service';
12
14
  import { PsHost } from '../ps-host/ps-host';
@@ -18,6 +20,8 @@ export const PsSheetPresenter = ({ children, isDismissBlocked, isOpen, onClose,
18
20
  const keyboardHeight = usePsKeyboardHeight();
19
21
  const window = useWindowDimensions();
20
22
  const insets = useSafeAreaInsets();
23
+ const isForm = usePsLayoutClass() === PsLayoutClass.regular &&
24
+ PsSheetService.supportsFormSizing(Platform.Version);
21
25
  const detentHeight = height > 0 ? PsSheetService.raisedHeight(height, keyboardHeight, window.height, insets.top) : 0;
22
26
  isOpenRef.current = isOpen;
23
27
  isBlockedRef.current = isDismissBlocked;
@@ -33,9 +37,15 @@ export const PsSheetPresenter = ({ children, isDismissBlocked, isOpen, onClose,
33
37
  onClose();
34
38
  }
35
39
  }, children: _jsx(Group, { modifiers: [
36
- frame({ alignment: 'top', maxHeight: Infinity, maxWidth: Infinity }),
37
- presentationDetents(detentHeight > 0 ? [{ height: detentHeight }] : ['medium']),
38
- presentationDragIndicator('visible'),
40
+ isForm
41
+ ? frame({ alignment: 'top', maxWidth: Infinity })
42
+ : frame({ alignment: 'top', maxHeight: Infinity, maxWidth: Infinity }),
43
+ ...(isForm
44
+ ? [presentationSizing('form', { fitted: { horizontal: false, vertical: true } })]
45
+ : [
46
+ presentationDetents(detentHeight > 0 ? [{ height: detentHeight }] : ['medium']),
47
+ presentationDragIndicator('visible'),
48
+ ]),
39
49
  presentationBackground(theme.background),
40
50
  interactiveDismissDisabled(isDismissBlocked),
41
51
  ], children: _jsx(RNHostView, { matchContents: { vertical: true }, children: _jsx(PsNativeTreeContext.Provider, { value: false, children: _jsx(PsSheetMeasureContext.Provider, { value: measure, children: children }) }) }) }) }) }));
@@ -1,17 +1,30 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Modal, Pressable, StyleSheet, View } from 'react-native';
2
+ import { Modal, Pressable, StyleSheet, View, useWindowDimensions } from 'react-native';
3
+ import { PsLayoutClass } from '../../constants/ps-layout-class';
4
+ import { PsOrientationConfig } from '../../constants/ps-orientation-config';
3
5
  import { PsSheetConfig } from '../../constants/ps-sheet-config';
6
+ import { usePsLayoutClass } from '../../hooks/use-ps-layout-class';
4
7
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
8
+ import { PsSheetService } from '../../services/ps-sheet-service';
5
9
  export const PsSheetPresenter = ({ children, isDismissBlocked, isOpen, onClose, }) => {
6
10
  const theme = usePsMobileTheme();
7
- return (_jsx(Modal, { animationType: 'slide', onRequestClose: isDismissBlocked ? undefined : onClose, statusBarTranslucent: true, transparent: true, visible: isOpen, children: _jsxs(View, { style: {
11
+ const layoutClass = usePsLayoutClass();
12
+ const window = useWindowDimensions();
13
+ const isRegular = layoutClass === PsLayoutClass.regular;
14
+ const maxWidth = PsSheetService.fittedMaxWidth(window.width, layoutClass);
15
+ return (_jsx(Modal, { animationType: 'slide', onRequestClose: isDismissBlocked ? undefined : onClose, statusBarTranslucent: true, supportedOrientations: PsOrientationConfig.all, transparent: true, visible: isOpen, children: _jsxs(View, { style: {
8
16
  backgroundColor: theme.overlay,
17
+ alignItems: isRegular ? 'center' : undefined,
9
18
  flex: 1,
10
- justifyContent: 'flex-end',
19
+ justifyContent: isRegular ? 'center' : 'flex-end',
11
20
  }, children: [_jsx(Pressable, { onPress: isDismissBlocked ? undefined : onClose, style: StyleSheet.absoluteFill, testID: 'ps-sheet-scrim' }), _jsx(View, { style: {
12
21
  backgroundColor: theme.background,
22
+ borderBottomLeftRadius: isRegular ? PsSheetConfig.cornerRadius : 0,
23
+ borderBottomRightRadius: isRegular ? PsSheetConfig.cornerRadius : 0,
13
24
  borderTopLeftRadius: PsSheetConfig.cornerRadius,
14
25
  borderTopRightRadius: PsSheetConfig.cornerRadius,
26
+ maxWidth,
15
27
  overflow: 'hidden',
28
+ width: isRegular ? '100%' : undefined,
16
29
  }, children: children })] }) }));
17
30
  };
@@ -5,14 +5,17 @@ import { useSharedValue } from 'react-native-reanimated';
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import { PsSortableGridConfig } from '../../constants/ps-sortable-grid-config';
7
7
  import { PsScrollContext } from '../../contexts/ps-scroll-context';
8
+ import { usePsLayoutClass } from '../../hooks/use-ps-layout-class';
8
9
  import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
9
10
  import { PsHapticService } from '../../services/ps-haptic-service';
11
+ import { PsLayoutService } from '../../services/ps-layout-service';
10
12
  import { PsSortableGridService } from '../../services/ps-sortable-grid-service';
11
13
  import { PsSortableItem } from './ps-sortable-item';
12
14
  export const PsSortableGrid = ({ getItemSpan, isDisabled = false, items, labels, onReorder, renderItem, }) => {
13
15
  const scroll = useContext(PsScrollContext);
14
16
  const { hasHaptics } = usePsMobileTheme();
15
17
  const insets = useSafeAreaInsets();
18
+ const capacity = PsLayoutService.gridUnits(usePsLayoutClass());
16
19
  const { height: windowHeight } = useWindowDimensions();
17
20
  const [order, setOrder] = useState(items);
18
21
  const [activeId, setActiveId] = useState(null);
@@ -30,7 +33,7 @@ export const PsSortableGrid = ({ getItemSpan, isDisabled = false, items, labels,
30
33
  translationY: 0,
31
34
  });
32
35
  const ids = useMemo(() => PsSortableGridService.ids(order), [order]);
33
- const rects = useMemo(() => PsSortableGridService.layout(order.map((item) => getItemSpan(item)), PsSortableGridService.heights(ids, measured), width), [getItemSpan, ids, measured, order, width]);
36
+ const rects = useMemo(() => PsSortableGridService.layout(order.map((item) => getItemSpan(item)), PsSortableGridService.heights(ids, measured), width, capacity), [capacity, getItemSpan, ids, measured, order, width]);
34
37
  const extras = useMemo(() => PsSortableGridService.extras(rects, PsSortableGridService.heights(ids, measured)), [ids, measured, rects]);
35
38
  const isReady = PsSortableGridService.isMeasured(ids, measured, width);
36
39
  const latest = useRef({ items, onReorder, order, rects, scroll, windowHeight, insets });
@@ -132,12 +135,12 @@ export const PsSortableGrid = ({ getItemSpan, isDisabled = false, items, labels,
132
135
  notify(next);
133
136
  PsHapticService.play('selection', hasHaptics);
134
137
  }, [hasHaptics]);
135
- const measure = useCallback((id, total, renderedExtra) => {
136
- const natural = PsSortableGridService.natural(total, renderedExtra);
138
+ const measure = useCallback((id, total, fill) => {
139
+ const natural = PsSortableGridService.natural(total, fill);
137
140
  setMeasured((current) => current[id] !== undefined &&
138
141
  Math.abs(current[id] - natural) < PsSortableGridConfig.measureTolerance
139
142
  ? current
140
143
  : { ...current, [id]: natural });
141
144
  }, []);
142
- return (_jsx(View, { onLayout: (event) => setWidth(event.nativeEvent.layout.width), style: { height: PsSortableGridService.contentHeight(rects) }, children: order.map((item, index) => (_jsx(PsSortableItem, { canMoveDown: index < order.length - 1, canMoveUp: index > 0, dragX: dragX, dragY: dragY, extra: extras[index], id: item.id, isActive: item.id === activeId, isDisabled: isDisabled, isReady: isReady, label: item.label, labels: labels, onEnd: end, onMeasure: measure, onMove: moveBy, onStart: start, onUpdate: update, rect: rects[index], children: renderItem({ isActive: item.id === activeId, item }) }, item.id))) }));
145
+ return (_jsx(View, { onLayout: (event) => setWidth(event.nativeEvent.layout.width), style: { height: PsSortableGridService.contentHeight(rects) }, testID: PsSortableGridConfig.testId, children: order.map((item, index) => (_jsx(PsSortableItem, { canMoveDown: index < order.length - 1, canMoveUp: index > 0, dragX: dragX, dragY: dragY, extra: extras[index], id: item.id, isActive: item.id === activeId, isDisabled: isDisabled, isReady: isReady, label: item.label, labels: labels, onEnd: end, onMeasure: measure, onMove: moveBy, onStart: start, onUpdate: update, rect: rects[index], children: renderItem({ isActive: item.id === activeId, item }) }, item.id))) }));
143
146
  };