@webority-technologies/mobile-ui 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/DocumentViewer/DocumentViewer.js +70 -0
- package/lib/commonjs/components/DocumentViewer/index.js +13 -0
- package/lib/commonjs/components/ZoomableImage/ZoomableImage.js +204 -0
- package/lib/commonjs/components/ZoomableImage/index.js +13 -0
- package/lib/commonjs/components/index.js +125 -111
- package/lib/module/components/DocumentViewer/DocumentViewer.js +64 -0
- package/lib/module/components/DocumentViewer/index.js +4 -0
- package/lib/module/components/ZoomableImage/ZoomableImage.js +198 -0
- package/lib/module/components/ZoomableImage/index.js +4 -0
- package/lib/module/components/index.js +2 -0
- package/lib/typescript/commonjs/components/DocumentViewer/DocumentViewer.d.ts +27 -0
- package/lib/typescript/commonjs/components/DocumentViewer/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/ZoomableImage/ZoomableImage.d.ts +47 -0
- package/lib/typescript/commonjs/components/ZoomableImage/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/index.d.ts +4 -0
- package/lib/typescript/module/components/DocumentViewer/DocumentViewer.d.ts +27 -0
- package/lib/typescript/module/components/DocumentViewer/index.d.ts +3 -0
- package/lib/typescript/module/components/ZoomableImage/ZoomableImage.d.ts +47 -0
- package/lib/typescript/module/components/ZoomableImage/index.d.ts +3 -0
- package/lib/typescript/module/components/index.d.ts +4 -0
- package/package.json +5 -3
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.DocumentViewer = void 0;
|
|
7
|
+
var _mobileCore = require("@webority-technologies/mobile-core");
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _reactNativePdf = _interopRequireDefault(require("react-native-pdf"));
|
|
11
|
+
var _index = require("../Spinner/index.js");
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
const toError = value => value instanceof Error ? value : new Error(String(value));
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Renders a PDF only — the WebView fallback consumer apps use for non-PDF
|
|
18
|
+
* office documents is a distinct rendering strategy and stays app-side.
|
|
19
|
+
*/
|
|
20
|
+
const DocumentViewer = ({
|
|
21
|
+
source,
|
|
22
|
+
trustAllCerts = false,
|
|
23
|
+
onLoadComplete,
|
|
24
|
+
onPageChanged,
|
|
25
|
+
onError,
|
|
26
|
+
loadingIndicator,
|
|
27
|
+
style,
|
|
28
|
+
testID,
|
|
29
|
+
accessibilityLabel
|
|
30
|
+
}) => {
|
|
31
|
+
const renderActivityIndicator = (0, _react.useCallback)(() => loadingIndicator ? loadingIndicator() : /*#__PURE__*/(0, _jsxRuntime.jsx)(_index.Spinner, {
|
|
32
|
+
size: "lg"
|
|
33
|
+
}), [loadingIndicator]);
|
|
34
|
+
const handleError = (0, _react.useCallback)(error => {
|
|
35
|
+
const err = toError(error);
|
|
36
|
+
_mobileCore.Logger.error('[@webority-technologies/mobile-ui] DocumentViewer failed to load.', err);
|
|
37
|
+
onError?.(err);
|
|
38
|
+
}, [onError]);
|
|
39
|
+
const handleLoadComplete = (0, _react.useCallback)(numberOfPages => onLoadComplete?.(numberOfPages), [onLoadComplete]);
|
|
40
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
41
|
+
style: [styles.root, style],
|
|
42
|
+
testID: testID,
|
|
43
|
+
accessible: accessibilityLabel !== undefined,
|
|
44
|
+
accessibilityLabel: accessibilityLabel,
|
|
45
|
+
accessibilityRole: "none",
|
|
46
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativePdf.default, {
|
|
47
|
+
source: source,
|
|
48
|
+
trustAllCerts: trustAllCerts,
|
|
49
|
+
style: styles.pdf,
|
|
50
|
+
renderActivityIndicator: renderActivityIndicator,
|
|
51
|
+
onLoadComplete: handleLoadComplete,
|
|
52
|
+
onPageChanged: onPageChanged,
|
|
53
|
+
onError: handleError
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
exports.DocumentViewer = DocumentViewer;
|
|
58
|
+
DocumentViewer.displayName = 'DocumentViewer';
|
|
59
|
+
const styles = _reactNative.StyleSheet.create({
|
|
60
|
+
root: {
|
|
61
|
+
flex: 1
|
|
62
|
+
},
|
|
63
|
+
pdf: {
|
|
64
|
+
flex: 1,
|
|
65
|
+
width: '100%',
|
|
66
|
+
height: '100%'
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
var _default = exports.default = DocumentViewer;
|
|
70
|
+
//# sourceMappingURL=DocumentViewer.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "DocumentViewer", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _DocumentViewer.DocumentViewer;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _DocumentViewer = require("./DocumentViewer.js");
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.ZoomableImage = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _reactNativeGestureHandler = require("react-native-gesture-handler");
|
|
10
|
+
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
11
|
+
var _index = require("../../utils/index.js");
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
14
|
+
/**
|
|
15
|
+
* ZoomableImage — a standalone pinch-to-zoom / pan / double-tap-to-zoom
|
|
16
|
+
* wrapper for a single piece of content (typically one `<Image>`).
|
|
17
|
+
*
|
|
18
|
+
* Reuses the same Reanimated 3 gesture composition `ImageGallery`'s internal
|
|
19
|
+
* lightbox slide uses (pinch clamped and rubber-banded at its limits, pan
|
|
20
|
+
* gated to only engage once zoomed in, double-tap toggling between baseline
|
|
21
|
+
* and `doubleTapScale`), stripped of the carousel-slide concerns that
|
|
22
|
+
* component needs and this one does not — there is no `index`/`total`/
|
|
23
|
+
* `active` because a standalone image is always the one being viewed.
|
|
24
|
+
*
|
|
25
|
+
* USAGE:
|
|
26
|
+
*
|
|
27
|
+
* <ZoomableImage>
|
|
28
|
+
* <Image source={{ uri }} style={styles.image} resizeMode="contain" />
|
|
29
|
+
* </ZoomableImage>
|
|
30
|
+
*
|
|
31
|
+
* Takes `children` rather than a `source`/`uri` prop: every real consumer of
|
|
32
|
+
* this pattern in the fleet wraps an already-authenticated image component
|
|
33
|
+
* (headers, caching, its own loading state) rather than a bare `<Image>`, so
|
|
34
|
+
* a `source` prop would force re-implementing that per app. Wrapping keeps
|
|
35
|
+
* this component ignorant of how the image is fetched.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const DEFAULT_MAX_SCALE = 4;
|
|
39
|
+
const DEFAULT_MIN_SCALE = 1;
|
|
40
|
+
const DEFAULT_DOUBLE_TAP_SCALE = 2;
|
|
41
|
+
const SPRING_CONFIG = {
|
|
42
|
+
damping: 18,
|
|
43
|
+
stiffness: 220,
|
|
44
|
+
mass: 1
|
|
45
|
+
};
|
|
46
|
+
const ZoomableImage = ({
|
|
47
|
+
children,
|
|
48
|
+
maxScale = DEFAULT_MAX_SCALE,
|
|
49
|
+
minScale = DEFAULT_MIN_SCALE,
|
|
50
|
+
doubleTapScale = DEFAULT_DOUBLE_TAP_SCALE,
|
|
51
|
+
disabled = false,
|
|
52
|
+
haptic,
|
|
53
|
+
style,
|
|
54
|
+
accessibilityLabel,
|
|
55
|
+
testID
|
|
56
|
+
}) => {
|
|
57
|
+
const scale = (0, _reactNativeReanimated.useSharedValue)(1);
|
|
58
|
+
const savedScale = (0, _reactNativeReanimated.useSharedValue)(1);
|
|
59
|
+
const translateX = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
60
|
+
const translateY = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
61
|
+
const savedTranslateX = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
62
|
+
const savedTranslateY = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
63
|
+
const resetTransform = (0, _react.useCallback)(() => {
|
|
64
|
+
scale.value = (0, _reactNativeReanimated.withSpring)(1, SPRING_CONFIG);
|
|
65
|
+
savedScale.value = 1;
|
|
66
|
+
translateX.value = (0, _reactNativeReanimated.withSpring)(0, SPRING_CONFIG);
|
|
67
|
+
translateY.value = (0, _reactNativeReanimated.withSpring)(0, SPRING_CONFIG);
|
|
68
|
+
savedTranslateX.value = 0;
|
|
69
|
+
savedTranslateY.value = 0;
|
|
70
|
+
}, [scale, savedScale, translateX, translateY, savedTranslateX, savedTranslateY]);
|
|
71
|
+
|
|
72
|
+
// Disabling mid-zoom must not leave the content stranded scaled/panned —
|
|
73
|
+
// snap it back the same way ImageGallery's slide resets on losing focus.
|
|
74
|
+
(0, _react.useEffect)(() => {
|
|
75
|
+
if (disabled) {
|
|
76
|
+
resetTransform();
|
|
77
|
+
}
|
|
78
|
+
}, [disabled, resetTransform]);
|
|
79
|
+
const triggerImpact = (0, _react.useCallback)(() => {
|
|
80
|
+
const h = (0, _index.resolveHaptic)(haptic, 'impactLight');
|
|
81
|
+
if (h) {
|
|
82
|
+
(0, _index.triggerHaptic)(h);
|
|
83
|
+
}
|
|
84
|
+
}, [haptic]);
|
|
85
|
+
const pinch = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Pinch().enabled(!disabled).onStart(() => {
|
|
86
|
+
'worklet';
|
|
87
|
+
|
|
88
|
+
savedScale.value = scale.value;
|
|
89
|
+
}).onUpdate(e => {
|
|
90
|
+
'worklet';
|
|
91
|
+
|
|
92
|
+
const next = savedScale.value * e.scale;
|
|
93
|
+
// Clamp minScale..maxScale on the UI thread so pinch feels rubbery at limits.
|
|
94
|
+
if (next < minScale) {
|
|
95
|
+
scale.value = minScale + (next - minScale) * 0.3;
|
|
96
|
+
} else if (next > maxScale) {
|
|
97
|
+
scale.value = maxScale + (next - maxScale) * 0.2;
|
|
98
|
+
} else {
|
|
99
|
+
scale.value = next;
|
|
100
|
+
}
|
|
101
|
+
}).onEnd(() => {
|
|
102
|
+
'worklet';
|
|
103
|
+
|
|
104
|
+
if (scale.value < minScale) {
|
|
105
|
+
scale.value = (0, _reactNativeReanimated.withSpring)(minScale, SPRING_CONFIG);
|
|
106
|
+
translateX.value = (0, _reactNativeReanimated.withSpring)(0, SPRING_CONFIG);
|
|
107
|
+
translateY.value = (0, _reactNativeReanimated.withSpring)(0, SPRING_CONFIG);
|
|
108
|
+
savedScale.value = minScale;
|
|
109
|
+
savedTranslateX.value = 0;
|
|
110
|
+
savedTranslateY.value = 0;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (scale.value > maxScale) {
|
|
114
|
+
scale.value = (0, _reactNativeReanimated.withSpring)(maxScale, SPRING_CONFIG);
|
|
115
|
+
savedScale.value = maxScale;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
savedScale.value = scale.value;
|
|
119
|
+
}), [disabled, scale, savedScale, translateX, translateY, savedTranslateX, savedTranslateY, minScale, maxScale]);
|
|
120
|
+
|
|
121
|
+
// Pan only engages when zoomed in — otherwise a scroll view or carousel this
|
|
122
|
+
// is nested inside keeps owning the gesture.
|
|
123
|
+
const pan = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Pan().enabled(!disabled).minPointers(1).maxPointers(1).onStart(() => {
|
|
124
|
+
'worklet';
|
|
125
|
+
|
|
126
|
+
savedTranslateX.value = translateX.value;
|
|
127
|
+
savedTranslateY.value = translateY.value;
|
|
128
|
+
}).onUpdate(e => {
|
|
129
|
+
'worklet';
|
|
130
|
+
|
|
131
|
+
if (scale.value <= 1) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
translateX.value = savedTranslateX.value + e.translationX;
|
|
135
|
+
translateY.value = savedTranslateY.value + e.translationY;
|
|
136
|
+
}).onEnd(() => {
|
|
137
|
+
'worklet';
|
|
138
|
+
|
|
139
|
+
savedTranslateX.value = translateX.value;
|
|
140
|
+
savedTranslateY.value = translateY.value;
|
|
141
|
+
}), [disabled, scale, translateX, translateY, savedTranslateX, savedTranslateY]);
|
|
142
|
+
const doubleTap = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Tap().enabled(!disabled).numberOfTaps(2).onEnd(() => {
|
|
143
|
+
'worklet';
|
|
144
|
+
|
|
145
|
+
(0, _reactNativeReanimated.runOnJS)(triggerImpact)();
|
|
146
|
+
if (scale.value > 1) {
|
|
147
|
+
scale.value = (0, _reactNativeReanimated.withSpring)(1, SPRING_CONFIG);
|
|
148
|
+
translateX.value = (0, _reactNativeReanimated.withSpring)(0, SPRING_CONFIG);
|
|
149
|
+
translateY.value = (0, _reactNativeReanimated.withSpring)(0, SPRING_CONFIG);
|
|
150
|
+
savedScale.value = 1;
|
|
151
|
+
savedTranslateX.value = 0;
|
|
152
|
+
savedTranslateY.value = 0;
|
|
153
|
+
} else {
|
|
154
|
+
scale.value = (0, _reactNativeReanimated.withSpring)(doubleTapScale, SPRING_CONFIG);
|
|
155
|
+
savedScale.value = doubleTapScale;
|
|
156
|
+
}
|
|
157
|
+
}), [disabled, scale, savedScale, translateX, translateY, savedTranslateX, savedTranslateY, triggerImpact, doubleTapScale]);
|
|
158
|
+
|
|
159
|
+
// Race the gestures so they coordinate cleanly: a tap won't be misread as a
|
|
160
|
+
// tiny pan, and pan won't fire when the user actually starts a pinch.
|
|
161
|
+
const composed = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Race(pinch, pan, doubleTap), [pinch, pan, doubleTap]);
|
|
162
|
+
const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
|
|
163
|
+
transform: [{
|
|
164
|
+
translateX: translateX.value
|
|
165
|
+
}, {
|
|
166
|
+
translateY: translateY.value
|
|
167
|
+
}, {
|
|
168
|
+
scale: scale.value
|
|
169
|
+
}]
|
|
170
|
+
}));
|
|
171
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
|
|
172
|
+
gesture: composed,
|
|
173
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
174
|
+
style: [styles.container, style],
|
|
175
|
+
testID: testID,
|
|
176
|
+
accessibilityLabel: accessibilityLabel,
|
|
177
|
+
accessibilityState: {
|
|
178
|
+
disabled
|
|
179
|
+
},
|
|
180
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
181
|
+
style: [styles.content, animatedStyle],
|
|
182
|
+
children: children
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
exports.ZoomableImage = ZoomableImage;
|
|
188
|
+
ZoomableImage.displayName = 'ZoomableImage';
|
|
189
|
+
const styles = _reactNative.StyleSheet.create({
|
|
190
|
+
container: {
|
|
191
|
+
flex: 1,
|
|
192
|
+
alignItems: 'center',
|
|
193
|
+
justifyContent: 'center',
|
|
194
|
+
overflow: 'hidden'
|
|
195
|
+
},
|
|
196
|
+
content: {
|
|
197
|
+
width: '100%',
|
|
198
|
+
height: '100%',
|
|
199
|
+
alignItems: 'center',
|
|
200
|
+
justifyContent: 'center'
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
var _default = exports.default = ZoomableImage;
|
|
204
|
+
//# sourceMappingURL=ZoomableImage.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "ZoomableImage", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _ZoomableImage.ZoomableImage;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _ZoomableImage = require("./ZoomableImage.js");
|
|
13
|
+
//# sourceMappingURL=index.js.map
|