@pallasoft/ps-mobile-components 1.11.0 → 1.13.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/dist/components/ps-action-sheet/ps-action-sheet.js +18 -2
- package/dist/components/ps-barcode-scanner-sheet/ps-barcode-scanner-sheet.js +2 -1
- package/dist/components/ps-document-highlight-layer/index.d.ts +2 -0
- package/dist/components/ps-document-highlight-layer/index.js +2 -0
- package/dist/components/ps-document-highlight-layer/ps-document-highlight-layer-props.d.ts +8 -0
- package/dist/components/ps-document-highlight-layer/ps-document-highlight-layer-props.js +1 -0
- package/dist/components/ps-document-highlight-layer/ps-document-highlight-layer.d.ts +2 -0
- package/dist/components/ps-document-highlight-layer/ps-document-highlight-layer.js +19 -0
- package/dist/components/ps-document-page/ps-document-page-props.d.ts +2 -0
- package/dist/components/ps-document-page/ps-document-page.d.ts +1 -1
- package/dist/components/ps-document-page/ps-document-page.js +3 -3
- package/dist/components/ps-document-search-bar/index.d.ts +2 -0
- package/dist/components/ps-document-search-bar/index.js +2 -0
- package/dist/components/ps-document-search-bar/ps-document-search-bar-props.d.ts +11 -0
- package/dist/components/ps-document-search-bar/ps-document-search-bar-props.js +1 -0
- package/dist/components/ps-document-search-bar/ps-document-search-bar.d.ts +2 -0
- package/dist/components/ps-document-search-bar/ps-document-search-bar.js +46 -0
- package/dist/components/ps-document-search-step/index.d.ts +2 -0
- package/dist/components/ps-document-search-step/index.js +2 -0
- package/dist/components/ps-document-search-step/ps-document-search-step-props.d.ts +7 -0
- package/dist/components/ps-document-search-step/ps-document-search-step-props.js +1 -0
- package/dist/components/ps-document-search-step/ps-document-search-step.d.ts +2 -0
- package/dist/components/ps-document-search-step/ps-document-search-step.js +30 -0
- package/dist/components/ps-document-text-page/ps-document-text-page-props.d.ts +2 -0
- package/dist/components/ps-document-text-page/ps-document-text-page.d.ts +1 -1
- package/dist/components/ps-document-text-page/ps-document-text-page.js +27 -3
- package/dist/components/ps-document-viewer/ps-document-viewer.js +53 -3
- package/dist/components/ps-document-zoom-image/ps-document-zoom-image-props.d.ts +4 -0
- package/dist/components/ps-document-zoom-image/ps-document-zoom-image.d.ts +1 -1
- package/dist/components/ps-document-zoom-image/ps-document-zoom-image.js +30 -3
- package/dist/components/ps-infinite-list/ps-infinite-list-props.d.ts +2 -0
- package/dist/components/ps-infinite-list/ps-infinite-list.d.ts +1 -1
- package/dist/components/ps-infinite-list/ps-infinite-list.js +12 -5
- package/dist/components/ps-list/ps-list-props.d.ts +2 -0
- package/dist/components/ps-list/ps-list.d.ts +1 -1
- package/dist/components/ps-list/ps-list.js +1 -1
- package/dist/components/ps-menu-popup/ps-menu-popup.js +3 -2
- package/dist/components/ps-screen/ps-screen-props.d.ts +2 -0
- package/dist/components/ps-screen/ps-screen.d.ts +1 -1
- package/dist/components/ps-screen/ps-screen.js +12 -5
- package/dist/components/ps-sheet-content/ps-sheet-content.js +4 -2
- package/dist/components/ps-sheet-presenter/ps-sheet-presenter.ios.js +15 -5
- package/dist/components/ps-sheet-presenter/ps-sheet-presenter.js +16 -3
- package/dist/components/ps-sortable-grid/ps-sortable-grid.js +4 -1
- package/dist/components/ps-text/ps-text-props.d.ts +1 -0
- package/dist/components/ps-text/ps-text.d.ts +1 -1
- package/dist/components/ps-text/ps-text.js +2 -2
- package/dist/components/ps-widget-list/ps-widget-grid.js +16 -7
- package/dist/components/ps-widget-list/ps-widget-list-props.d.ts +2 -0
- package/dist/components/ps-widget-list/ps-widget-list.d.ts +1 -1
- package/dist/components/ps-widget-list/ps-widget-list.js +12 -5
- package/dist/constants/ps-content-width.d.ts +6 -0
- package/dist/constants/ps-content-width.js +5 -0
- package/dist/constants/ps-document-search-config.d.ts +16 -0
- package/dist/constants/ps-document-search-config.js +16 -0
- package/dist/constants/ps-layout-class-config.d.ts +12 -0
- package/dist/constants/ps-layout-class-config.js +12 -0
- package/dist/constants/ps-layout-class.d.ts +5 -0
- package/dist/constants/ps-layout-class.js +4 -0
- package/dist/constants/ps-menu-popup-config.d.ts +1 -0
- package/dist/constants/ps-menu-popup-config.js +1 -0
- package/dist/constants/ps-orientation-config.d.ts +3 -0
- package/dist/constants/ps-orientation-config.js +3 -0
- package/dist/constants/ps-sheet-config.d.ts +1 -0
- package/dist/constants/ps-sheet-config.js +1 -0
- package/dist/constants/ps-sortable-grid-config.d.ts +2 -0
- package/dist/constants/ps-sortable-grid-config.js +2 -0
- package/dist/hooks/use-ps-document-search-texts.d.ts +2 -0
- package/dist/hooks/use-ps-document-search-texts.js +26 -0
- package/dist/hooks/use-ps-layout-class.d.ts +2 -0
- package/dist/hooks/use-ps-layout-class.js +6 -0
- package/dist/hooks/use-ps-menu-anchor.d.ts +1 -1
- package/dist/hooks/use-ps-menu-anchor.js +14 -2
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/interfaces/ps-document-highlight-rect.d.ts +5 -0
- package/dist/interfaces/ps-document-highlight-rect.js +1 -0
- package/dist/interfaces/ps-document-item.d.ts +3 -0
- package/dist/interfaces/ps-document-labels.d.ts +7 -0
- package/dist/interfaces/ps-document-point.d.ts +4 -0
- package/dist/interfaces/ps-document-point.js +1 -0
- package/dist/interfaces/ps-document-search-labels.d.ts +9 -0
- package/dist/interfaces/ps-document-search-labels.js +1 -0
- package/dist/interfaces/ps-document-search-location.d.ts +4 -0
- package/dist/interfaces/ps-document-search-location.js +1 -0
- package/dist/interfaces/ps-document-search-match.d.ts +2 -0
- package/dist/interfaces/ps-document-search-match.js +1 -0
- package/dist/interfaces/ps-document-search-state.d.ts +4 -0
- package/dist/interfaces/ps-document-search-state.js +1 -0
- package/dist/interfaces/ps-document-text-box.d.ts +2 -0
- package/dist/interfaces/ps-document-text-box.js +1 -0
- package/dist/interfaces/ps-document-text-layer-word.d.ts +2 -0
- package/dist/interfaces/ps-document-text-layer-word.js +1 -0
- package/dist/interfaces/ps-document-text-layer.d.ts +2 -0
- package/dist/interfaces/ps-document-text-layer.js +1 -0
- package/dist/interfaces/ps-document-text-line.d.ts +4 -0
- package/dist/interfaces/ps-document-text-line.js +1 -0
- package/dist/interfaces/ps-document-text-segment.d.ts +2 -0
- package/dist/interfaces/ps-document-text-segment.js +1 -0
- package/dist/interfaces/ps-document-word-match.d.ts +3 -0
- package/dist/interfaces/ps-document-word-match.js +1 -0
- package/dist/interfaces/ps-menu-popup-insets.d.ts +2 -0
- package/dist/interfaces/ps-menu-popup-layout.d.ts +1 -0
- package/dist/services/ps-document-search-service.d.ts +36 -0
- package/dist/services/ps-document-search-service.js +114 -0
- package/dist/services/ps-document-search-style-service.d.ts +7 -0
- package/dist/services/ps-document-search-style-service.js +22 -0
- package/dist/services/ps-document-viewer-entry-service.js +1 -0
- package/dist/services/ps-document-zoom-service.d.ts +1 -0
- package/dist/services/ps-document-zoom-service.js +3 -0
- package/dist/services/ps-layout-service.d.ts +12 -0
- package/dist/services/ps-layout-service.js +41 -0
- package/dist/services/ps-menu-popup-service.js +8 -4
- package/dist/services/ps-sheet-service.d.ts +5 -2
- package/dist/services/ps-sheet-service.js +17 -4
- package/dist/services/ps-sortable-grid-service.d.ts +3 -2
- package/dist/services/ps-sortable-grid-service.js +26 -22
- package/package.json +2 -2
|
@@ -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: {
|
|
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,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PsDocumentSearchState } from '../../interfaces/ps-document-search-state';
|
|
2
|
+
import type { PsDocumentTextLayer } from '../../interfaces/ps-document-text-layer';
|
|
3
|
+
import type { PsSize } from '../../interfaces/ps-size';
|
|
4
|
+
export interface PsDocumentHighlightLayerProps {
|
|
5
|
+
layer: PsDocumentTextLayer;
|
|
6
|
+
search: PsDocumentSearchState;
|
|
7
|
+
size: PsSize;
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
|
|
4
|
+
import { PsDocumentSearchService } from '../../services/ps-document-search-service';
|
|
5
|
+
import { PsDocumentSearchStyleService } from '../../services/ps-document-search-style-service';
|
|
6
|
+
export const PsDocumentHighlightLayer = ({ layer, search, size, }) => {
|
|
7
|
+
const theme = usePsMobileTheme();
|
|
8
|
+
const rects = PsDocumentSearchService.highlights(layer, search.query, size, search.activeIndex);
|
|
9
|
+
return (_jsx(View, { pointerEvents: 'none', style: { bottom: 0, left: 0, position: 'absolute', right: 0, top: 0 }, testID: 'ps-document-highlight-layer', children: rects.map((rect, index) => (_jsx(View, { style: [
|
|
10
|
+
PsDocumentSearchStyleService.rect(theme, rect.isActive),
|
|
11
|
+
{
|
|
12
|
+
height: rect.box.height,
|
|
13
|
+
left: rect.box.left,
|
|
14
|
+
position: 'absolute',
|
|
15
|
+
top: rect.box.top,
|
|
16
|
+
width: rect.box.width,
|
|
17
|
+
},
|
|
18
|
+
], testID: rect.isActive ? 'ps-document-highlight-active' : 'ps-document-highlight' }, index))) }));
|
|
19
|
+
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { PsDocumentLoadStatus } from '../../constants/ps-document-load-status';
|
|
2
2
|
import type { PsDocumentItem } from '../../interfaces/ps-document-item';
|
|
3
3
|
import type { PsDocumentLabels } from '../../interfaces/ps-document-labels';
|
|
4
|
+
import type { PsDocumentSearchState } from '../../interfaces/ps-document-search-state';
|
|
4
5
|
export interface PsDocumentPageProps {
|
|
5
6
|
item: PsDocumentItem;
|
|
6
7
|
labels: PsDocumentLabels;
|
|
7
8
|
onDismiss: () => void;
|
|
8
9
|
onRetry?: () => void;
|
|
9
10
|
onZoomChange: (isZoomed: boolean) => void;
|
|
11
|
+
search?: PsDocumentSearchState;
|
|
10
12
|
status?: PsDocumentLoadStatus;
|
|
11
13
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsDocumentPageProps } from './ps-document-page-props';
|
|
2
|
-
export declare const PsDocumentPage: ({ item, labels, onDismiss, onRetry, onZoomChange, status, }: PsDocumentPageProps) => import("react").JSX.Element;
|
|
2
|
+
export declare const PsDocumentPage: ({ item, labels, onDismiss, onRetry, onZoomChange, search, status, }: PsDocumentPageProps) => import("react").JSX.Element;
|
|
@@ -9,7 +9,7 @@ import { PsDocumentTextPage } from '../ps-document-text-page/ps-document-text-pa
|
|
|
9
9
|
import { PsDocumentUnsupported } from '../ps-document-unsupported/ps-document-unsupported';
|
|
10
10
|
import { PsDocumentZoomImage } from '../ps-document-zoom-image/ps-document-zoom-image';
|
|
11
11
|
import { PsSpinner } from '../ps-spinner/ps-spinner';
|
|
12
|
-
export const PsDocumentPage = ({ item, labels, onDismiss, onRetry, onZoomChange, status = PsDocumentLoadStatus.Ready, }) => {
|
|
12
|
+
export const PsDocumentPage = ({ item, labels, onDismiss, onRetry, onZoomChange, search, status = PsDocumentLoadStatus.Ready, }) => {
|
|
13
13
|
const [attempt, setAttempt] = useState(0);
|
|
14
14
|
const [hasFailed, setHasFailed] = useState(false);
|
|
15
15
|
if (item.kind === PsDocumentKind.Pdf) {
|
|
@@ -23,9 +23,9 @@ export const PsDocumentPage = ({ item, labels, onDismiss, onRetry, onZoomChange,
|
|
|
23
23
|
return hasFailed ? (_jsx(PsDocumentMessage, { actionLabel: labels.retry, iconKey: PsIconKey.Warning, isOnDark: true, onAction: () => {
|
|
24
24
|
setHasFailed(false);
|
|
25
25
|
setAttempt((current) => current + 1);
|
|
26
|
-
}, title: labels.loadFailed })) : (_jsx(PsDocumentZoomImage, { name: item.name, onDismiss: onDismiss, onError: () => setHasFailed(true), onZoomChange: onZoomChange, source: item.source }, attempt));
|
|
26
|
+
}, title: labels.loadFailed })) : (_jsx(PsDocumentZoomImage, { name: item.name, onDismiss: onDismiss, onError: () => setHasFailed(true), onZoomChange: onZoomChange, search: search, source: item.source, textLayer: item.textLayer }, attempt));
|
|
27
27
|
}
|
|
28
28
|
if (item.kind === PsDocumentKind.Text)
|
|
29
|
-
return _jsx(PsDocumentTextPage, { item: item, labels: labels });
|
|
29
|
+
return _jsx(PsDocumentTextPage, { item: item, labels: labels, search: search });
|
|
30
30
|
return _jsx(PsDocumentUnsupported, { isOnDark: true, item: item, labels: labels });
|
|
31
31
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PsDocumentSearchLabels } from '../../interfaces/ps-document-search-labels';
|
|
2
|
+
export interface PsDocumentSearchBarProps {
|
|
3
|
+
activeIndex: number;
|
|
4
|
+
labels: PsDocumentSearchLabels;
|
|
5
|
+
onChange: (query: string) => void;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
onNext: () => void;
|
|
8
|
+
onPrevious: () => void;
|
|
9
|
+
query: string;
|
|
10
|
+
total: number;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PsIconKey } from '@pallasoft/ps-icons';
|
|
3
|
+
import { Platform, Pressable, TextInput, View } from 'react-native';
|
|
4
|
+
import { PsDocumentConfig } from '../../constants/ps-document-config';
|
|
5
|
+
import { PsDocumentSearchConfig } from '../../constants/ps-document-search-config';
|
|
6
|
+
import { PsLayoutConfig } from '../../constants/ps-layout-config';
|
|
7
|
+
import { PsMobileControlConfig } from '../../constants/ps-mobile-control-config';
|
|
8
|
+
import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
|
|
9
|
+
import { PsDocumentSearchService } from '../../services/ps-document-search-service';
|
|
10
|
+
import { PsDocumentSearchStyleService } from '../../services/ps-document-search-style-service';
|
|
11
|
+
import { PsMobileColorService } from '../../services/ps-mobile-color-service';
|
|
12
|
+
import { PsDocumentSearchStep } from '../ps-document-search-step/ps-document-search-step';
|
|
13
|
+
import { PsIconButton } from '../ps-icon-button/ps-icon-button';
|
|
14
|
+
import { PsIcon } from '../ps-icon/ps-icon';
|
|
15
|
+
import { PsText } from '../ps-text/ps-text';
|
|
16
|
+
export const PsDocumentSearchBar = ({ activeIndex, labels, onChange, onClose, onNext, onPrevious, query, total, }) => {
|
|
17
|
+
const theme = usePsMobileTheme();
|
|
18
|
+
const isMaterial = Platform.OS === 'android';
|
|
19
|
+
const hasQuery = PsDocumentSearchService.terms(query).length > 0;
|
|
20
|
+
const status = !hasQuery
|
|
21
|
+
? null
|
|
22
|
+
: total === 0
|
|
23
|
+
? labels.noMatches
|
|
24
|
+
: PsDocumentSearchService.countLabel(labels.matchCount, activeIndex + 1, total);
|
|
25
|
+
return (_jsxs(View, { style: {
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
flexDirection: 'row',
|
|
28
|
+
gap: PsDocumentSearchConfig.barGap,
|
|
29
|
+
paddingHorizontal: PsDocumentConfig.barPaddingHorizontal,
|
|
30
|
+
paddingVertical: PsDocumentConfig.barPaddingVertical,
|
|
31
|
+
}, testID: 'ps-document-search-bar', children: [_jsxs(View, { style: {
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
backgroundColor: PsMobileColorService.withAlpha(theme.white, PsDocumentSearchConfig.fieldAlpha),
|
|
34
|
+
borderRadius: PsDocumentSearchStyleService.fieldRadius(isMaterial),
|
|
35
|
+
flex: 1,
|
|
36
|
+
flexDirection: 'row',
|
|
37
|
+
gap: PsMobileControlConfig.buttonIconSpacing,
|
|
38
|
+
height: PsMobileControlConfig.controlHeights.compact,
|
|
39
|
+
paddingHorizontal: PsDocumentSearchConfig.fieldPaddingX,
|
|
40
|
+
}, children: [_jsx(PsIcon, { color: theme.white, iconKey: PsIconKey.Search, size: PsMobileControlConfig.iconSizeInline }), _jsx(TextInput, { accessibilityLabel: labels.searchPlaceholder, autoCapitalize: 'none', autoCorrect: false, autoFocus: true, clearButtonMode: 'while-editing', onChangeText: onChange, onSubmitEditing: onNext, placeholder: labels.searchPlaceholder, placeholderTextColor: PsMobileColorService.withAlpha(theme.white, PsDocumentConfig.chromeAlpha), returnKeyType: 'search', selectionColor: theme.primary, style: {
|
|
41
|
+
color: theme.white,
|
|
42
|
+
flex: 1,
|
|
43
|
+
fontSize: PsMobileControlConfig.controlFontSize,
|
|
44
|
+
padding: 0,
|
|
45
|
+
}, testID: 'ps-document-search-input', value: query }), status === null ? null : (_jsx(PsText, { tone: 'white', variant: 'caption', children: status }))] }), _jsx(PsDocumentSearchStep, { isDisabled: total === 0, isUp: true, label: labels.previousMatch, onPress: onPrevious, testID: 'ps-document-search-previous' }), _jsx(PsDocumentSearchStep, { isDisabled: total === 0, isUp: false, label: labels.nextMatch, onPress: onNext, testID: 'ps-document-search-next' }), isMaterial ? (_jsx(PsIconButton, { color: theme.white, iconKey: PsIconKey.Close, label: labels.closeSearch, onPress: onClose, size: 'compact', testID: 'ps-document-search-close' })) : (_jsx(Pressable, { accessibilityLabel: labels.closeSearch, accessibilityRole: 'button', hitSlop: PsLayoutConfig.hitSlop, onPress: onClose, testID: 'ps-document-search-close', children: _jsx(PsText, { tone: 'white', weight: 'semibold', children: labels.closeSearch }) }))] }));
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { PsIconKey } from '@pallasoft/ps-icons';
|
|
3
|
+
import { Pressable, View } from 'react-native';
|
|
4
|
+
import { PsDocumentSearchConfig } from '../../constants/ps-document-search-config';
|
|
5
|
+
import { PsLayoutConfig } from '../../constants/ps-layout-config';
|
|
6
|
+
import { PsMobileControlConfig } from '../../constants/ps-mobile-control-config';
|
|
7
|
+
import { PsRippleKind } from '../../constants/ps-ripple-kind';
|
|
8
|
+
import { usePsHaptics } from '../../hooks/use-ps-haptics';
|
|
9
|
+
import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
|
|
10
|
+
import { PsRippleService } from '../../services/ps-ripple-service';
|
|
11
|
+
import { PsStateService } from '../../services/ps-state-service';
|
|
12
|
+
import { PsIcon } from '../ps-icon/ps-icon';
|
|
13
|
+
export const PsDocumentSearchStep = ({ isDisabled, isUp, label, onPress, testID, }) => {
|
|
14
|
+
const haptic = usePsHaptics();
|
|
15
|
+
const theme = usePsMobileTheme();
|
|
16
|
+
return (_jsx(Pressable, { android_ripple: PsRippleService.resolve(theme, PsRippleKind.Borderless), accessibilityLabel: label, accessibilityRole: 'button', accessibilityState: { disabled: isDisabled }, disabled: isDisabled, hitSlop: PsLayoutConfig.hitSlop, onPress: () => {
|
|
17
|
+
haptic('light');
|
|
18
|
+
onPress();
|
|
19
|
+
}, style: {
|
|
20
|
+
alignItems: 'center',
|
|
21
|
+
height: PsMobileControlConfig.controlHeights.compact,
|
|
22
|
+
justifyContent: 'center',
|
|
23
|
+
opacity: isDisabled ? PsStateService.disabledOpacity() : 1,
|
|
24
|
+
width: PsMobileControlConfig.controlHeights.compact,
|
|
25
|
+
}, testID: testID, children: _jsx(View, { style: {
|
|
26
|
+
transform: [
|
|
27
|
+
{ rotate: isUp ? PsDocumentSearchConfig.rotateUp : PsDocumentSearchConfig.rotateDown },
|
|
28
|
+
],
|
|
29
|
+
}, children: _jsx(PsIcon, { color: theme.white, iconKey: PsIconKey.Chevron }) }) }));
|
|
30
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { PsDocumentItem } from '../../interfaces/ps-document-item';
|
|
2
2
|
import type { PsDocumentLabels } from '../../interfaces/ps-document-labels';
|
|
3
|
+
import type { PsDocumentSearchState } from '../../interfaces/ps-document-search-state';
|
|
3
4
|
export interface PsDocumentTextPageProps {
|
|
4
5
|
item: PsDocumentItem;
|
|
5
6
|
labels: PsDocumentLabels;
|
|
7
|
+
search?: PsDocumentSearchState;
|
|
6
8
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsDocumentTextPageProps } from './ps-document-text-page-props';
|
|
2
|
-
export declare const PsDocumentTextPage: ({ item, labels }: PsDocumentTextPageProps) => import("react").JSX.Element;
|
|
2
|
+
export declare const PsDocumentTextPage: ({ item, labels, search }: PsDocumentTextPageProps) => import("react").JSX.Element;
|
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
3
|
import { PsIconKey } from '@pallasoft/ps-icons';
|
|
3
|
-
import { ScrollView, View } from 'react-native';
|
|
4
|
+
import { ScrollView, Text, View } from 'react-native';
|
|
4
5
|
import { PsDocumentConfig } from '../../constants/ps-document-config';
|
|
5
6
|
import { PsDocumentLoadStatus } from '../../constants/ps-document-load-status';
|
|
7
|
+
import { PsDocumentSearchConfig } from '../../constants/ps-document-search-config';
|
|
6
8
|
import { usePsDocumentText } from '../../hooks/use-ps-document-text';
|
|
9
|
+
import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
|
|
10
|
+
import { PsDocumentSearchService } from '../../services/ps-document-search-service';
|
|
11
|
+
import { PsDocumentSearchStyleService } from '../../services/ps-document-search-style-service';
|
|
7
12
|
import { PsDocumentMessage } from '../ps-document-message/ps-document-message';
|
|
8
13
|
import { PsDocumentUnsupported } from '../ps-document-unsupported/ps-document-unsupported';
|
|
9
14
|
import { PsSpinner } from '../ps-spinner/ps-spinner';
|
|
10
15
|
import { PsText } from '../ps-text/ps-text';
|
|
11
|
-
export const PsDocumentTextPage = ({ item, labels }) => {
|
|
16
|
+
export const PsDocumentTextPage = ({ item, labels, search }) => {
|
|
12
17
|
const loaded = usePsDocumentText(item);
|
|
18
|
+
const theme = usePsMobileTheme();
|
|
19
|
+
const scroll = useRef(null);
|
|
20
|
+
const [lines, setLines] = useState([]);
|
|
21
|
+
const matches = loaded.text === null || search === undefined
|
|
22
|
+
? []
|
|
23
|
+
: PsDocumentSearchService.matches(loaded.text, search.query);
|
|
24
|
+
const active = search === undefined ? undefined : matches[search.activeIndex];
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (active === undefined)
|
|
27
|
+
return;
|
|
28
|
+
scroll.current?.scrollTo({
|
|
29
|
+
animated: true,
|
|
30
|
+
y: Math.max(0, PsDocumentSearchService.scrollOffset(lines, active.start) +
|
|
31
|
+
PsDocumentConfig.pageGutter -
|
|
32
|
+
PsDocumentSearchConfig.scrollMargin),
|
|
33
|
+
});
|
|
34
|
+
}, [active?.start, lines, search?.query]);
|
|
13
35
|
if (loaded.status === PsDocumentLoadStatus.Loading)
|
|
14
36
|
return (_jsx(View, { style: { alignItems: 'center', flex: 1, justifyContent: 'center' }, children: _jsx(PsSpinner, {}) }));
|
|
15
37
|
if (loaded.text === null)
|
|
16
38
|
return item.source === null ? (_jsx(PsDocumentUnsupported, { isOnDark: true, item: item, labels: labels })) : (_jsx(PsDocumentMessage, { description: item.name, iconKey: PsIconKey.Warning, isOnDark: true, title: labels.loadFailed }));
|
|
17
|
-
return (_jsx(View, { style: { flex: 1 }, testID: 'ps-document-text-page', children: _jsx(ScrollView, { contentContainerStyle: { padding: PsDocumentConfig.pageGutter }, showsVerticalScrollIndicator: true, children: _jsx(PsText, { isSelectable: true, tone: 'white', children:
|
|
39
|
+
return (_jsx(View, { style: { flex: 1 }, testID: 'ps-document-text-page', children: _jsx(ScrollView, { contentContainerStyle: { padding: PsDocumentConfig.pageGutter }, ref: scroll, showsVerticalScrollIndicator: true, children: _jsx(PsText, { isSelectable: true, onTextLayout: (event) => setLines(event.nativeEvent.lines.map((line) => ({ text: line.text, y: line.y }))), tone: 'white', children: matches.length === 0
|
|
40
|
+
? loaded.text
|
|
41
|
+
: PsDocumentSearchService.segments(loaded.text, matches).map((segment, index) => segment.matchIndex === null ? (segment.text) : (_jsx(Text, { style: PsDocumentSearchStyleService.span(theme, segment.matchIndex === search?.activeIndex), children: segment.text }, index))) }) }) }));
|
|
18
42
|
};
|
|
@@ -5,13 +5,18 @@ import { FlatList, Modal, StatusBar, View } from 'react-native';
|
|
|
5
5
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
6
6
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
7
7
|
import { PsDocumentConfig } from '../../constants/ps-document-config';
|
|
8
|
+
import { PsDocumentSearchConfig } from '../../constants/ps-document-search-config';
|
|
8
9
|
import { PsMobileControlConfig } from '../../constants/ps-mobile-control-config';
|
|
10
|
+
import { PsOrientationConfig } from '../../constants/ps-orientation-config';
|
|
9
11
|
import { usePsDocumentPdfPages } from '../../hooks/use-ps-document-pdf-pages';
|
|
12
|
+
import { usePsDocumentSearchTexts } from '../../hooks/use-ps-document-search-texts';
|
|
10
13
|
import { usePsMobileTheme } from '../../hooks/use-ps-mobile-theme';
|
|
11
14
|
import { PsDocumentPagerService } from '../../services/ps-document-pager-service';
|
|
15
|
+
import { PsDocumentSearchService } from '../../services/ps-document-search-service';
|
|
12
16
|
import { PsDocumentViewerEntryService } from '../../services/ps-document-viewer-entry-service';
|
|
13
17
|
import { PsDocumentPageStrip } from '../ps-document-page-strip/ps-document-page-strip';
|
|
14
18
|
import { PsDocumentPage } from '../ps-document-page/ps-document-page';
|
|
19
|
+
import { PsDocumentSearchBar } from '../ps-document-search-bar/ps-document-search-bar';
|
|
15
20
|
import { PsIconButton } from '../ps-icon-button/ps-icon-button';
|
|
16
21
|
import { PsText } from '../ps-text/ps-text';
|
|
17
22
|
export const PsDocumentViewer = ({ initialIndex = 0, isOpen, items, labels, onClose, }) => {
|
|
@@ -27,11 +32,48 @@ export const PsDocumentViewer = ({ initialIndex = 0, isOpen, items, labels, onCl
|
|
|
27
32
|
? PsDocumentViewerEntryService.startIndex(items, entries, initialIndex)
|
|
28
33
|
: PsDocumentViewerEntryService.resolveIndex(currentEntry, entries, 0);
|
|
29
34
|
const current = entries[index];
|
|
35
|
+
const searchLabels = PsDocumentSearchService.labels(labels);
|
|
36
|
+
const [isSearching, setIsSearching] = useState(false);
|
|
37
|
+
const [query, setQuery] = useState('');
|
|
38
|
+
const [activeMatch, setActiveMatch] = useState(PsDocumentSearchConfig.noActive);
|
|
39
|
+
const searchTexts = usePsDocumentSearchTexts(items, isSearching);
|
|
40
|
+
const counts = PsDocumentSearchService.counts(entries, searchTexts, query);
|
|
41
|
+
const total = PsDocumentSearchService.total(counts);
|
|
42
|
+
const canSearch = searchLabels !== null && PsDocumentSearchService.isSearchable(items);
|
|
43
|
+
const showMatch = (match, nextCounts) => {
|
|
44
|
+
setActiveMatch(match);
|
|
45
|
+
const location = PsDocumentSearchService.locate(nextCounts, match);
|
|
46
|
+
if (location === null)
|
|
47
|
+
return;
|
|
48
|
+
const entry = entries[location.entryIndex];
|
|
49
|
+
if (entry !== undefined && location.entryIndex !== index)
|
|
50
|
+
setCurrentEntry(entry);
|
|
51
|
+
};
|
|
52
|
+
const closeSearch = () => {
|
|
53
|
+
setIsSearching(false);
|
|
54
|
+
setQuery('');
|
|
55
|
+
setActiveMatch(PsDocumentSearchConfig.noActive);
|
|
56
|
+
};
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!isSearching)
|
|
59
|
+
return;
|
|
60
|
+
if (total === 0) {
|
|
61
|
+
if (activeMatch !== PsDocumentSearchConfig.noActive)
|
|
62
|
+
setActiveMatch(PsDocumentSearchConfig.noActive);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (activeMatch < 0 || activeMatch >= total)
|
|
66
|
+
showMatch(PsDocumentSearchService.startIndex(counts, index), counts);
|
|
67
|
+
}, [isSearching, total, activeMatch]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!isOpen && isSearching)
|
|
70
|
+
closeSearch();
|
|
71
|
+
}, [isOpen]);
|
|
30
72
|
useEffect(() => {
|
|
31
73
|
if (width > 0 && entries.length > 0)
|
|
32
74
|
list.current?.scrollToIndex({ animated: false, index });
|
|
33
75
|
}, [entries.length, index, width]);
|
|
34
|
-
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: {
|
|
35
77
|
backgroundColor: PsDocumentConfig.backdropColor,
|
|
36
78
|
flex: 1,
|
|
37
79
|
paddingBottom: insets.bottom,
|
|
@@ -41,7 +83,10 @@ export const PsDocumentViewer = ({ initialIndex = 0, isOpen, items, labels, onCl
|
|
|
41
83
|
flexDirection: 'row',
|
|
42
84
|
paddingHorizontal: PsDocumentConfig.barPaddingHorizontal,
|
|
43
85
|
paddingVertical: PsDocumentConfig.barPaddingVertical,
|
|
44
|
-
}, children: [_jsx(PsIconButton, { color: theme.white, iconKey: PsIconKey.Close, label: labels.close, onPress: onClose }), _jsxs(View, { style: { alignItems: 'center', flex: 1 }, children: [PsDocumentPagerService.hasMany(entries.length) ? (_jsx(PsText, { tone: 'white', weight: 'semibold', children: PsDocumentPagerService.counter(index, entries.length) })) : null, current === undefined ? null : (_jsx(PsText, { numberOfLines: 1, tone: 'white', variant: 'caption', children: current.item.name }))] }), _jsx(
|
|
86
|
+
}, children: [_jsx(PsIconButton, { color: theme.white, iconKey: PsIconKey.Close, label: labels.close, onPress: onClose }), _jsxs(View, { style: { alignItems: 'center', flex: 1 }, children: [PsDocumentPagerService.hasMany(entries.length) ? (_jsx(PsText, { tone: 'white', weight: 'semibold', children: PsDocumentPagerService.counter(index, entries.length) })) : null, current === undefined ? null : (_jsx(PsText, { numberOfLines: 1, tone: 'white', variant: 'caption', children: current.item.name }))] }), canSearch && !isSearching ? (_jsx(PsIconButton, { color: theme.white, iconKey: PsIconKey.Search, label: searchLabels.search, onPress: () => setIsSearching(true), testID: 'ps-document-search-button' })) : (_jsx(View, { style: { width: PsMobileControlConfig.controlHeights.default } }))] }), canSearch && isSearching ? (_jsx(PsDocumentSearchBar, { activeIndex: activeMatch, labels: searchLabels, onChange: (value) => {
|
|
87
|
+
setQuery(value);
|
|
88
|
+
setActiveMatch(PsDocumentSearchConfig.noActive);
|
|
89
|
+
}, onClose: closeSearch, onNext: () => showMatch(PsDocumentSearchService.step(activeMatch, total, 1), counts), onPrevious: () => showMatch(PsDocumentSearchService.step(activeMatch, total, -1), counts), query: query, total: total })) : null, _jsx(View, { onLayout: (event) => setWidth(event.nativeEvent.layout.width), style: { flex: 1 }, testID: 'ps-document-viewer-pager', children: width === 0 ? null : (_jsx(FlatList, { data: entries, extraData: `${isSearching}|${query}|${activeMatch}|${total}`, getItemLayout: (_data, position) => ({
|
|
45
90
|
index: position,
|
|
46
91
|
length: width,
|
|
47
92
|
offset: PsDocumentPagerService.offset(position, width),
|
|
@@ -50,7 +95,12 @@ export const PsDocumentViewer = ({ initialIndex = 0, isOpen, items, labels, onCl
|
|
|
50
95
|
if (next !== undefined)
|
|
51
96
|
setCurrentEntry(next);
|
|
52
97
|
setIsZoomed(false);
|
|
53
|
-
}, pagingEnabled: true, ref: list, renderItem: ({ item: entry }) => (_jsx(View, { style: { width }, children: _jsx(PsDocumentPage, { item: entry.item, labels: labels, onDismiss: onClose, onRetry: pdf.retry, onZoomChange: setIsZoomed,
|
|
98
|
+
}, pagingEnabled: true, ref: list, renderItem: ({ index: position, item: entry }) => (_jsx(View, { style: { width }, children: _jsx(PsDocumentPage, { item: entry.item, labels: labels, onDismiss: onClose, onRetry: pdf.retry, onZoomChange: setIsZoomed, search: isSearching
|
|
99
|
+
? {
|
|
100
|
+
activeIndex: PsDocumentSearchService.activeFor(counts, activeMatch, position),
|
|
101
|
+
query,
|
|
102
|
+
}
|
|
103
|
+
: undefined, status: entry.status }) })), scrollEnabled: !isZoomed, showsHorizontalScrollIndicator: false, testID: 'ps-document-viewer-list' })) }), PsDocumentViewerEntryService.hasStrip(items, entries) ? (_jsx(PsDocumentPageStrip, { entries: entries, index: index, onSelect: (position) => {
|
|
54
104
|
const entry = entries[position];
|
|
55
105
|
if (entry !== undefined)
|
|
56
106
|
setCurrentEntry(entry);
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import type { PsDocumentSearchState } from '../../interfaces/ps-document-search-state';
|
|
1
2
|
import type { PsDocumentSource } from '../../interfaces/ps-document-source';
|
|
3
|
+
import type { PsDocumentTextLayer } from '../../interfaces/ps-document-text-layer';
|
|
2
4
|
export interface PsDocumentZoomImageProps {
|
|
3
5
|
name: string;
|
|
4
6
|
onDismiss: () => void;
|
|
5
7
|
onError: () => void;
|
|
6
8
|
onZoomChange: (isZoomed: boolean) => void;
|
|
9
|
+
search?: PsDocumentSearchState;
|
|
7
10
|
source: PsDocumentSource;
|
|
11
|
+
textLayer?: PsDocumentTextLayer;
|
|
8
12
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsDocumentZoomImageProps } from './ps-document-zoom-image-props';
|
|
2
|
-
export declare const PsDocumentZoomImage: ({ name, onDismiss, onError, onZoomChange, source, }: PsDocumentZoomImageProps) => import("react").JSX.Element;
|
|
2
|
+
export declare const PsDocumentZoomImage: ({ name, onDismiss, onError, onZoomChange, search, source, textLayer, }: PsDocumentZoomImageProps) => import("react").JSX.Element;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Image, View } from 'react-native';
|
|
4
4
|
import { GestureDetector, usePanGesture, usePinchGesture, useSimultaneousGestures, useTapGesture, } from 'react-native-gesture-handler';
|
|
5
5
|
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
|
6
6
|
import { PsDocumentConfig } from '../../constants/ps-document-config';
|
|
7
|
+
import { PsDocumentSearchService } from '../../services/ps-document-search-service';
|
|
7
8
|
import { PsDocumentZoomService } from '../../services/ps-document-zoom-service';
|
|
9
|
+
import { PsDocumentHighlightLayer } from '../ps-document-highlight-layer/ps-document-highlight-layer';
|
|
8
10
|
import { PsSpinner } from '../ps-spinner/ps-spinner';
|
|
9
|
-
export const PsDocumentZoomImage = ({ name, onDismiss, onError, onZoomChange, source, }) => {
|
|
11
|
+
export const PsDocumentZoomImage = ({ name, onDismiss, onError, onZoomChange, search, source, textLayer, }) => {
|
|
10
12
|
const [size, setSize] = useState({ height: 0, width: 0 });
|
|
11
13
|
const [isZoomed, setIsZoomed] = useState(false);
|
|
12
14
|
const [isLoaded, setIsLoaded] = useState(false);
|
|
@@ -83,6 +85,31 @@ export const PsDocumentZoomImage = ({ name, onDismiss, onError, onZoomChange, so
|
|
|
83
85
|
},
|
|
84
86
|
runOnJS: true,
|
|
85
87
|
});
|
|
88
|
+
const hasSearch = search !== undefined && textLayer !== undefined && search.activeIndex >= 0;
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (!hasSearch || !isZoomed || size.width === 0)
|
|
91
|
+
return;
|
|
92
|
+
const active = PsDocumentSearchService.highlights(textLayer, search.query, size, search.activeIndex).find((rect) => rect.isActive);
|
|
93
|
+
if (active === undefined)
|
|
94
|
+
return;
|
|
95
|
+
const center = PsDocumentSearchService.center(active.box);
|
|
96
|
+
const nextX = PsDocumentZoomService.centerOffset(center.x, size.width, scale.value);
|
|
97
|
+
const nextY = PsDocumentZoomService.centerOffset(center.y, size.height, scale.value);
|
|
98
|
+
x.value = withTiming(nextX, { duration: PsDocumentConfig.settleMs });
|
|
99
|
+
y.value = withTiming(nextY, { duration: PsDocumentConfig.settleMs });
|
|
100
|
+
baseX.value = nextX;
|
|
101
|
+
baseY.value = nextY;
|
|
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]);
|
|
86
113
|
const gesture = useSimultaneousGestures(pinch, move, dismiss, doubleTap);
|
|
87
114
|
const animatedStyle = useAnimatedStyle(() => ({
|
|
88
115
|
transform: [
|
|
@@ -92,7 +119,7 @@ export const PsDocumentZoomImage = ({ name, onDismiss, onError, onZoomChange, so
|
|
|
92
119
|
{ scale: scale.value },
|
|
93
120
|
],
|
|
94
121
|
}));
|
|
95
|
-
return (_jsx(GestureDetector, { gesture: gesture, children: _jsxs(View, { onLayout: (event) => setSize(event.nativeEvent.layout), style: { flex: 1, overflow: 'hidden' }, testID: 'ps-document-zoom-image', children: [
|
|
122
|
+
return (_jsx(GestureDetector, { gesture: gesture, children: _jsxs(View, { onLayout: (event) => setSize(event.nativeEvent.layout), style: { flex: 1, overflow: 'hidden' }, testID: 'ps-document-zoom-image', children: [_jsxs(Animated.View, { style: [{ flex: 1 }, animatedStyle], children: [_jsx(Image, { accessibilityLabel: name, onError: onError, onLoad: () => setIsLoaded(true), resizeMode: 'contain', source: { headers: source.headers, uri: source.uri }, style: { flex: 1 } }), search === undefined || textLayer === undefined ? null : (_jsx(PsDocumentHighlightLayer, { layer: textLayer, search: search, size: size }))] }), isLoaded ? null : (_jsx(View, { pointerEvents: 'none', style: {
|
|
96
123
|
alignItems: 'center',
|
|
97
124
|
bottom: 0,
|
|
98
125
|
justifyContent: 'center',
|
|
@@ -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;
|