capacitor-camera-multicapture 0.12.2
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/CameraMultiCapture.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +551 -0
- package/android/build.gradle +69 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/CameraConfig.java +45 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/CameraConfigMapper.java +98 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/CameraMultiCapture.java +10 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/CameraMultiCapturePlugin.java +1340 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/ExifWrapper.java +108 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/GenericUploadWorker.java +173 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/ImageUtils.java +215 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/PluginVersion.java +6 -0
- package/android/src/main/java/dev/hemang/cameramulticapture/ThumbnailGenerator.java +48 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/esm/controllers/camera-controller.d.ts +127 -0
- package/dist/esm/controllers/camera-controller.js +388 -0
- package/dist/esm/controllers/camera-controller.js.map +1 -0
- package/dist/esm/controllers/gallery-controller.d.ts +66 -0
- package/dist/esm/controllers/gallery-controller.js +333 -0
- package/dist/esm/controllers/gallery-controller.js.map +1 -0
- package/dist/esm/definitions.d.ts +372 -0
- package/dist/esm/definitions.js +6 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/overlay-manager.d.ts +124 -0
- package/dist/esm/overlay-manager.js +732 -0
- package/dist/esm/overlay-manager.js.map +1 -0
- package/dist/esm/overlay.d.ts +2 -0
- package/dist/esm/overlay.js +39 -0
- package/dist/esm/overlay.js.map +1 -0
- package/dist/esm/registration.d.ts +5 -0
- package/dist/esm/registration.js +8 -0
- package/dist/esm/registration.js.map +1 -0
- package/dist/esm/types/ui-types.d.ts +78 -0
- package/dist/esm/types/ui-types.js +2 -0
- package/dist/esm/types/ui-types.js.map +1 -0
- package/dist/esm/ui/capture-gesture-handler.d.ts +15 -0
- package/dist/esm/ui/capture-gesture-handler.js +87 -0
- package/dist/esm/ui/capture-gesture-handler.js.map +1 -0
- package/dist/esm/ui/default-styles.d.ts +27 -0
- package/dist/esm/ui/default-styles.js +134 -0
- package/dist/esm/ui/default-styles.js.map +1 -0
- package/dist/esm/ui/image-editor.d.ts +10 -0
- package/dist/esm/ui/image-editor.js +103 -0
- package/dist/esm/ui/image-editor.js.map +1 -0
- package/dist/esm/ui/layout-manager.d.ts +42 -0
- package/dist/esm/ui/layout-manager.js +199 -0
- package/dist/esm/ui/layout-manager.js.map +1 -0
- package/dist/esm/ui/media-viewer.d.ts +2 -0
- package/dist/esm/ui/media-viewer.js +267 -0
- package/dist/esm/ui/media-viewer.js.map +1 -0
- package/dist/esm/ui/pinch-zoom-handler.d.ts +37 -0
- package/dist/esm/ui/pinch-zoom-handler.js +118 -0
- package/dist/esm/ui/pinch-zoom-handler.js.map +1 -0
- package/dist/esm/ui/ui-factory.d.ts +39 -0
- package/dist/esm/ui/ui-factory.js +272 -0
- package/dist/esm/ui/ui-factory.js.map +1 -0
- package/dist/esm/version.d.ts +1 -0
- package/dist/esm/version.js +3 -0
- package/dist/esm/version.js.map +1 -0
- package/dist/esm/web.d.ts +69 -0
- package/dist/esm/web.js +122 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +6274 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +6276 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CameraMultiCapturePlugin/CameraMultiCapture.swift +8 -0
- package/ios/Sources/CameraMultiCapturePlugin/CameraMultiCapturePlugin.swift +1551 -0
- package/ios/Sources/CameraMultiCapturePlugin/PluginVersion.swift +2 -0
- package/ios/Tests/CameraMultiCapturePluginTests/CameraMultiCapturePluginTests.swift +15 -0
- package/package.json +93 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../../../src/ui/media-viewer.ts"],"names":[],"mappings":"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","sourcesContent":["import BiggerPicture from 'bigger-picture';\n\nimport bpCss from 'bigger-picture/css-text';\n\nconst BP_STYLES_ID = 'cmmc-bigger-picture-styles';\n\ntype BiggerPictureInstance = ReturnType<typeof BiggerPicture>;\ntype BiggerPictureItem = {\n img?: string;\n thumb?: string;\n alt?: string;\n sources?: { src: string; type: string }[];\n width?: number;\n height?: number;\n};\n\nlet bpInstance: BiggerPictureInstance | null = null;\nlet activeBackHandler: (() => void) | null = null;\nconst DEFAULT_MEDIA_WIDTH = 1920;\nconst DEFAULT_MEDIA_HEIGHT = 1080;\n\nfunction ensureStyles(): void {\n const existing = document.getElementById(BP_STYLES_ID);\n if (existing && existing.tagName.toLowerCase() !== 'style') {\n existing.remove();\n }\n\n const style = document.getElementById(BP_STYLES_ID) as HTMLStyleElement | null;\n if (style) {\n return;\n }\n\n const styleEl = document.createElement('style');\n styleEl.id = BP_STYLES_ID;\n styleEl.textContent = `${bpCss}\n .bp-wrap,\n .bp-wrap * {\n animation-duration: 0ms !important;\n transition-duration: 0ms !important;\n transition-delay: 0ms !important;\n }\n .bp-wrap {\n --cmmc-safe-top-extra: 0px;\n --cmmc-safe-bottom-extra: 0px;\n }\n .bp-wrap > div:first-child {\n background: #000 !important;\n }\n .bp-x {\n top: env(safe-area-inset-top) !important;\n }\n .bp-img-wrap {\n top: calc(env(safe-area-inset-top) + var(--cmmc-safe-top-extra)) !important;\n bottom: calc(env(safe-area-inset-bottom) + var(--cmmc-safe-bottom-extra)) !important;\n height: auto !important;\n }\n .bp-vid,\n .bp-if {\n max-height: calc(100% - env(safe-area-inset-top) - env(safe-area-inset-bottom) - var(--cmmc-safe-top-extra) - var(--cmmc-safe-bottom-extra)) !important;\n margin-top: calc(env(safe-area-inset-top) + var(--cmmc-safe-top-extra)) !important;\n margin-bottom: calc(env(safe-area-inset-bottom) + var(--cmmc-safe-bottom-extra)) !important;\n }\n .bp-controls .cmmc-bp-edit {\n position: absolute;\n top: env(safe-area-inset-top);\n left: 0;\n z-index: 10;\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: #fff;\n padding: 0;\n -webkit-tap-highlight-color: transparent;\n }\n .bp-controls .cmmc-bp-edit:hover {\n background: none;\n }\n .bp-controls .cmmc-bp-edit svg {\n filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6));\n }\n `;\n document.head.appendChild(styleEl);\n}\n\nfunction getInstance(): BiggerPictureInstance {\n if (bpInstance) {\n return bpInstance;\n }\n\n bpInstance = BiggerPicture({\n target: document.body,\n });\n return bpInstance;\n}\n\nfunction getMimeType(src: string): string {\n const normalized = src.toLowerCase();\n if (normalized.endsWith('.webm')) return 'video/webm';\n if (normalized.endsWith('.mov')) return 'video/quicktime';\n return 'video/mp4';\n}\n\nfunction readSafeAreaInset(property: 'top' | 'bottom'): number {\n const probe = document.createElement('div');\n probe.style.position = 'fixed';\n probe.style.visibility = 'hidden';\n probe.style.pointerEvents = 'none';\n probe.style.zIndex = '-1';\n if (property === 'top') {\n probe.style.top = '0';\n probe.style.paddingTop = 'env(safe-area-inset-top)';\n } else {\n probe.style.bottom = '0';\n probe.style.paddingBottom = 'env(safe-area-inset-bottom)';\n }\n document.body.appendChild(probe);\n const computed = window.getComputedStyle(probe);\n const raw = property === 'top' ? computed.paddingTop : computed.paddingBottom;\n probe.remove();\n const parsed = Number.parseFloat(raw);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction applyDynamicSafeArea(container: HTMLElement): void {\n const closeButton = container.querySelector('.bp-x') as HTMLElement | null;\n const media = container.querySelector('.bp-vid, .bp-if, .bp-img') as HTMLElement | null;\n if (!closeButton || !media) {\n container.style.setProperty('--cmmc-safe-top-extra', '0px');\n container.style.setProperty('--cmmc-safe-bottom-extra', '0px');\n return;\n }\n\n const closeRect = closeButton.getBoundingClientRect();\n const mediaRect = media.getBoundingClientRect();\n const safeBottom = readSafeAreaInset('bottom');\n\n const topOverlap = Math.max(0, closeRect.bottom - mediaRect.top);\n const bottomLimit = window.innerHeight - safeBottom;\n const bottomOverlap = Math.max(0, mediaRect.bottom - bottomLimit);\n\n container.style.setProperty('--cmmc-safe-top-extra', `${topOverlap}px`);\n container.style.setProperty('--cmmc-safe-bottom-extra', `${bottomOverlap}px`);\n}\n\nfunction getImageDimensions(src: string): Promise<{ width: number; height: number } | null> {\n return new Promise((resolve) => {\n const img = new Image();\n img.onload = () => {\n if (img.naturalWidth > 0 && img.naturalHeight > 0) {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n } else {\n resolve(null);\n }\n };\n img.onerror = () => resolve(null);\n img.src = src;\n });\n}\n\nfunction getVideoDimensions(src: string): Promise<{ width: number; height: number } | null> {\n return new Promise((resolve) => {\n const video = document.createElement('video');\n video.preload = 'metadata';\n video.playsInline = true;\n\n const cleanup = () => {\n video.removeAttribute('src');\n video.load();\n };\n\n video.onloadedmetadata = () => {\n const width = video.videoWidth;\n const height = video.videoHeight;\n cleanup();\n if (width > 0 && height > 0) {\n resolve({ width, height });\n } else {\n resolve(null);\n }\n };\n video.onerror = () => {\n cleanup();\n resolve(null);\n };\n\n video.src = src;\n });\n}\n\nfunction injectEditButton(container: HTMLElement, onEdit: () => void): void {\n const controls = container.querySelector('.bp-controls');\n if (!controls || controls.querySelector('.cmmc-bp-edit')) return;\n\n const btn = document.createElement('button');\n btn.className = 'cmmc-bp-edit';\n btn.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/></svg>`;\n\n btn.onclick = (e) => {\n e.stopPropagation();\n getInstance().close();\n onEdit();\n };\n\n controls.appendChild(btn);\n}\n\nfunction registerBackHandler(): void {\n removeBackHandler();\n const handler = () => {\n getInstance().close();\n };\n activeBackHandler = handler;\n document.addEventListener('backbutton', handler);\n}\n\nfunction removeBackHandler(): void {\n if (activeBackHandler) {\n document.removeEventListener('backbutton', activeBackHandler);\n activeBackHandler = null;\n }\n}\n\nexport function openImagePreview(src: string, thumb?: string, onEdit?: () => void): void {\n void (async () => {\n ensureStyles();\n const dimensions = await getImageDimensions(src);\n\n const items: BiggerPictureItem[] = [\n {\n img: src,\n thumb: thumb || src,\n width: dimensions?.width ?? DEFAULT_MEDIA_WIDTH,\n height: dimensions?.height ?? DEFAULT_MEDIA_HEIGHT,\n },\n ];\n\n getInstance().open({\n items,\n el: document.body,\n scale: 1,\n intro: 'fadeup',\n onOpen: (container) => {\n applyDynamicSafeArea(container);\n registerBackHandler();\n if (onEdit) {\n injectEditButton(container, onEdit);\n }\n },\n onClose: () => {\n removeBackHandler();\n },\n onResize: (container) => {\n if (!container) return;\n applyDynamicSafeArea(container);\n },\n });\n })();\n}\n\nexport function openVideoPreview(src: string, thumb?: string): void {\n void (async () => {\n ensureStyles();\n const dimensions = await getVideoDimensions(src);\n\n const items: BiggerPictureItem[] = [\n {\n thumb: thumb || src,\n sources: [{ src, type: getMimeType(src) }],\n width: dimensions?.width ?? DEFAULT_MEDIA_WIDTH,\n height: dimensions?.height ?? DEFAULT_MEDIA_HEIGHT,\n },\n ];\n\n getInstance().open({\n items,\n el: document.body,\n scale: 1,\n intro: 'fadeup',\n onOpen: (container) => {\n applyDynamicSafeArea(container);\n registerBackHandler();\n },\n onClose: () => {\n removeBackHandler();\n },\n onResize: (container) => {\n if (!container) return;\n applyDynamicSafeArea(container);\n },\n });\n })();\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CameraOverlayUIOptions } from '../types/ui-types';
|
|
2
|
+
import { CameraController } from '../controllers/camera-controller';
|
|
3
|
+
export interface PinchZoomConfig {
|
|
4
|
+
options: CameraOverlayUIOptions;
|
|
5
|
+
}
|
|
6
|
+
export type ZoomSelectionUpdater = (zoom: number) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Encapsulates JavaScript pinch-to-zoom gesture handling for the camera overlay.
|
|
9
|
+
*/
|
|
10
|
+
export declare class PinchZoomHandler {
|
|
11
|
+
private readonly cameraController;
|
|
12
|
+
private readonly config;
|
|
13
|
+
private readonly updateZoomSelection;
|
|
14
|
+
private pinchElement;
|
|
15
|
+
private pinchStartDistance;
|
|
16
|
+
private pinchStartZoom;
|
|
17
|
+
private pinchMinZoom;
|
|
18
|
+
private pinchMaxZoom;
|
|
19
|
+
private pinchPresetLevels;
|
|
20
|
+
private isPinching;
|
|
21
|
+
private boundPinchStart;
|
|
22
|
+
private boundPinchMove;
|
|
23
|
+
private boundPinchEnd;
|
|
24
|
+
constructor(cameraController: CameraController, config: PinchZoomConfig, updateZoomSelection: ZoomSelectionUpdater);
|
|
25
|
+
/**
|
|
26
|
+
* Attach pinch handlers to the given container and initialise zoom ranges.
|
|
27
|
+
*/
|
|
28
|
+
attach(container: HTMLElement): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Detach pinch handlers and clear state.
|
|
31
|
+
*/
|
|
32
|
+
detach(): void;
|
|
33
|
+
private getPinchDistance;
|
|
34
|
+
private onPinchStart;
|
|
35
|
+
private onPinchMove;
|
|
36
|
+
private onPinchEnd;
|
|
37
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encapsulates JavaScript pinch-to-zoom gesture handling for the camera overlay.
|
|
3
|
+
*/
|
|
4
|
+
export class PinchZoomHandler {
|
|
5
|
+
constructor(cameraController, config, updateZoomSelection) {
|
|
6
|
+
this.cameraController = cameraController;
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.updateZoomSelection = updateZoomSelection;
|
|
9
|
+
this.pinchElement = null;
|
|
10
|
+
this.pinchStartDistance = 0;
|
|
11
|
+
this.pinchStartZoom = 1;
|
|
12
|
+
this.pinchMinZoom = 1;
|
|
13
|
+
this.pinchMaxZoom = 1;
|
|
14
|
+
this.pinchPresetLevels = [];
|
|
15
|
+
this.isPinching = false;
|
|
16
|
+
this.boundPinchStart = null;
|
|
17
|
+
this.boundPinchMove = null;
|
|
18
|
+
this.boundPinchEnd = null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Attach pinch handlers to the given container and initialise zoom ranges.
|
|
22
|
+
*/
|
|
23
|
+
async attach(container) {
|
|
24
|
+
var _a;
|
|
25
|
+
try {
|
|
26
|
+
const zoomInfo = await this.cameraController.getAvailableZoomLevels();
|
|
27
|
+
this.pinchMinZoom = zoomInfo.minZoom;
|
|
28
|
+
this.pinchMaxZoom = zoomInfo.maxZoom;
|
|
29
|
+
this.pinchPresetLevels = ((_a = zoomInfo.presetLevels) === null || _a === void 0 ? void 0 : _a.length)
|
|
30
|
+
? zoomInfo.presetLevels
|
|
31
|
+
: [zoomInfo.minZoom, 1, zoomInfo.maxZoom];
|
|
32
|
+
}
|
|
33
|
+
catch (_b) {
|
|
34
|
+
this.pinchMinZoom = 1;
|
|
35
|
+
this.pinchMaxZoom = 10;
|
|
36
|
+
this.pinchPresetLevels = [1, 2, 3, 5, 10];
|
|
37
|
+
}
|
|
38
|
+
this.pinchElement = container;
|
|
39
|
+
this.boundPinchStart = this.onPinchStart.bind(this);
|
|
40
|
+
this.boundPinchMove = this.onPinchMove.bind(this);
|
|
41
|
+
this.boundPinchEnd = this.onPinchEnd.bind(this);
|
|
42
|
+
container.addEventListener('touchstart', this.boundPinchStart, { passive: true });
|
|
43
|
+
container.addEventListener('touchmove', this.boundPinchMove, { passive: false });
|
|
44
|
+
container.addEventListener('touchend', this.boundPinchEnd, { passive: true });
|
|
45
|
+
container.addEventListener('touchcancel', this.boundPinchEnd, { passive: true });
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Detach pinch handlers and clear state.
|
|
49
|
+
*/
|
|
50
|
+
detach() {
|
|
51
|
+
const el = this.pinchElement;
|
|
52
|
+
if (!el || !this.boundPinchStart || !this.boundPinchMove || !this.boundPinchEnd)
|
|
53
|
+
return;
|
|
54
|
+
el.removeEventListener('touchstart', this.boundPinchStart);
|
|
55
|
+
el.removeEventListener('touchmove', this.boundPinchMove);
|
|
56
|
+
el.removeEventListener('touchend', this.boundPinchEnd);
|
|
57
|
+
el.removeEventListener('touchcancel', this.boundPinchEnd);
|
|
58
|
+
this.pinchElement = null;
|
|
59
|
+
this.boundPinchStart = null;
|
|
60
|
+
this.boundPinchMove = null;
|
|
61
|
+
this.boundPinchEnd = null;
|
|
62
|
+
}
|
|
63
|
+
getPinchDistance(touches) {
|
|
64
|
+
if (touches.length < 2)
|
|
65
|
+
return 0;
|
|
66
|
+
const a = touches[0];
|
|
67
|
+
const b = touches[1];
|
|
68
|
+
return Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY);
|
|
69
|
+
}
|
|
70
|
+
onPinchStart(e) {
|
|
71
|
+
if (e.touches.length === 2) {
|
|
72
|
+
this.isPinching = true;
|
|
73
|
+
this.pinchStartDistance = this.getPinchDistance(e.touches);
|
|
74
|
+
this.pinchStartZoom = this.cameraController.getCurrentZoom();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
onPinchMove(e) {
|
|
78
|
+
if (e.touches.length !== 2 || !this.isPinching || this.pinchStartDistance <= 0)
|
|
79
|
+
return;
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
const dist = this.getPinchDistance(e.touches);
|
|
82
|
+
const scale = dist / this.pinchStartDistance;
|
|
83
|
+
let zoom = this.pinchStartZoom * scale;
|
|
84
|
+
zoom = Math.max(this.pinchMinZoom, Math.min(this.pinchMaxZoom, zoom));
|
|
85
|
+
this.cameraController.setZoom(zoom).catch((err) => console.warn('Set zoom failed', err));
|
|
86
|
+
this.updateZoomSelection(zoom);
|
|
87
|
+
}
|
|
88
|
+
onPinchEnd(e) {
|
|
89
|
+
var _a;
|
|
90
|
+
if (e.touches.length >= 2)
|
|
91
|
+
return;
|
|
92
|
+
if (!this.isPinching)
|
|
93
|
+
return;
|
|
94
|
+
this.isPinching = false;
|
|
95
|
+
this.pinchStartDistance = 0;
|
|
96
|
+
const lockToNearest = ((_a = this.config.options.pinchToZoom) === null || _a === void 0 ? void 0 : _a.lockToNearestStep) === true;
|
|
97
|
+
if (lockToNearest && this.pinchPresetLevels.length > 0) {
|
|
98
|
+
const current = this.cameraController.getCurrentZoom();
|
|
99
|
+
let nearest = this.pinchPresetLevels[0];
|
|
100
|
+
let best = Math.abs(nearest - current);
|
|
101
|
+
for (const level of this.pinchPresetLevels) {
|
|
102
|
+
const d = Math.abs(level - current);
|
|
103
|
+
if (d < best) {
|
|
104
|
+
best = d;
|
|
105
|
+
nearest = level;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.cameraController.setZoom(nearest).catch((err) => console.warn('Set zoom failed', err));
|
|
109
|
+
this.updateZoomSelection(nearest);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// Even without snapping, move the highlight to the closest zoom button
|
|
113
|
+
const current = this.cameraController.getCurrentZoom();
|
|
114
|
+
this.updateZoomSelection(current);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=pinch-zoom-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinch-zoom-handler.js","sourceRoot":"","sources":["../../../src/ui/pinch-zoom-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CameraOverlayUIOptions } from '../types/ui-types';\nimport { CameraController } from '../controllers/camera-controller';\n\nexport interface PinchZoomConfig {\n options: CameraOverlayUIOptions;\n}\n\nexport type ZoomSelectionUpdater = (zoom: number) => void;\n\n/**\n * Encapsulates JavaScript pinch-to-zoom gesture handling for the camera overlay.\n */\nexport class PinchZoomHandler {\n private pinchElement: HTMLElement | null = null;\n private pinchStartDistance: number = 0;\n private pinchStartZoom: number = 1;\n private pinchMinZoom: number = 1;\n private pinchMaxZoom: number = 1;\n private pinchPresetLevels: number[] = [];\n private isPinching = false;\n private boundPinchStart: ((e: TouchEvent) => void) | null = null;\n private boundPinchMove: ((e: TouchEvent) => void) | null = null;\n private boundPinchEnd: ((e: TouchEvent) => void) | null = null;\n\n constructor(\n private readonly cameraController: CameraController,\n private readonly config: PinchZoomConfig,\n private readonly updateZoomSelection: ZoomSelectionUpdater\n ) {}\n\n /**\n * Attach pinch handlers to the given container and initialise zoom ranges.\n */\n async attach(container: HTMLElement): Promise<void> {\n try {\n const zoomInfo = await this.cameraController.getAvailableZoomLevels();\n this.pinchMinZoom = zoomInfo.minZoom;\n this.pinchMaxZoom = zoomInfo.maxZoom;\n this.pinchPresetLevels = zoomInfo.presetLevels?.length\n ? zoomInfo.presetLevels\n : [zoomInfo.minZoom, 1, zoomInfo.maxZoom];\n } catch {\n this.pinchMinZoom = 1;\n this.pinchMaxZoom = 10;\n this.pinchPresetLevels = [1, 2, 3, 5, 10];\n }\n\n this.pinchElement = container;\n this.boundPinchStart = this.onPinchStart.bind(this);\n this.boundPinchMove = this.onPinchMove.bind(this);\n this.boundPinchEnd = this.onPinchEnd.bind(this);\n container.addEventListener('touchstart', this.boundPinchStart, { passive: true });\n container.addEventListener('touchmove', this.boundPinchMove, { passive: false });\n container.addEventListener('touchend', this.boundPinchEnd, { passive: true });\n container.addEventListener('touchcancel', this.boundPinchEnd, { passive: true });\n }\n\n /**\n * Detach pinch handlers and clear state.\n */\n detach(): void {\n const el = this.pinchElement;\n if (!el || !this.boundPinchStart || !this.boundPinchMove || !this.boundPinchEnd) return;\n el.removeEventListener('touchstart', this.boundPinchStart);\n el.removeEventListener('touchmove', this.boundPinchMove);\n el.removeEventListener('touchend', this.boundPinchEnd);\n el.removeEventListener('touchcancel', this.boundPinchEnd);\n this.pinchElement = null;\n this.boundPinchStart = null;\n this.boundPinchMove = null;\n this.boundPinchEnd = null;\n }\n\n private getPinchDistance(touches: TouchList): number {\n if (touches.length < 2) return 0;\n const a = touches[0];\n const b = touches[1];\n return Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY);\n }\n\n private onPinchStart(e: TouchEvent): void {\n if (e.touches.length === 2) {\n this.isPinching = true;\n this.pinchStartDistance = this.getPinchDistance(e.touches);\n this.pinchStartZoom = this.cameraController.getCurrentZoom();\n }\n }\n\n private onPinchMove(e: TouchEvent): void {\n if (e.touches.length !== 2 || !this.isPinching || this.pinchStartDistance <= 0) return;\n e.preventDefault();\n const dist = this.getPinchDistance(e.touches);\n const scale = dist / this.pinchStartDistance;\n let zoom = this.pinchStartZoom * scale;\n zoom = Math.max(this.pinchMinZoom, Math.min(this.pinchMaxZoom, zoom));\n this.cameraController.setZoom(zoom).catch((err) => console.warn('Set zoom failed', err));\n this.updateZoomSelection(zoom);\n }\n\n private onPinchEnd(e: TouchEvent): void {\n if (e.touches.length >= 2) return;\n if (!this.isPinching) return;\n this.isPinching = false;\n this.pinchStartDistance = 0;\n const lockToNearest = this.config.options.pinchToZoom?.lockToNearestStep === true;\n if (lockToNearest && this.pinchPresetLevels.length > 0) {\n const current = this.cameraController.getCurrentZoom();\n let nearest = this.pinchPresetLevels[0];\n let best = Math.abs(nearest - current);\n for (const level of this.pinchPresetLevels) {\n const d = Math.abs(level - current);\n if (d < best) {\n best = d;\n nearest = level;\n }\n }\n this.cameraController.setZoom(nearest).catch((err) => console.warn('Set zoom failed', err));\n this.updateZoomSelection(nearest);\n } else {\n // Even without snapping, move the highlight to the closest zoom button\n const current = this.cameraController.getCurrentZoom();\n this.updateZoomSelection(current);\n }\n }\n}\n\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for creating UI elements
|
|
3
|
+
*/
|
|
4
|
+
import { ButtonStyle, IconButtonConfig } from '../types/ui-types';
|
|
5
|
+
/**
|
|
6
|
+
* Sets the icon for a button element, supporting both inline SVG and file paths
|
|
7
|
+
*/
|
|
8
|
+
export declare function setButtonIcon(element: HTMLButtonElement, iconSource: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Creates a button with the specified configuration
|
|
11
|
+
*/
|
|
12
|
+
export declare function createButton(config: IconButtonConfig, text?: string): HTMLButtonElement;
|
|
13
|
+
/**
|
|
14
|
+
* Applies styles to a button element
|
|
15
|
+
*/
|
|
16
|
+
export declare function applyButtonStyle(element: HTMLButtonElement, style: ButtonStyle): void;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a thumbnail image container
|
|
19
|
+
*/
|
|
20
|
+
export declare function createThumbnailContainer(thumbnailData: string, thumbnailStyle: {
|
|
21
|
+
width?: string;
|
|
22
|
+
height?: string;
|
|
23
|
+
}, onRemove: (() => void) | null, options?: {
|
|
24
|
+
isVideo?: boolean;
|
|
25
|
+
duration?: number;
|
|
26
|
+
onTap?: () => void;
|
|
27
|
+
}): HTMLElement;
|
|
28
|
+
/**
|
|
29
|
+
* Creates a remove button for thumbnails
|
|
30
|
+
*/
|
|
31
|
+
export declare function createRemoveButton(onRemove: (e: Event) => void): HTMLButtonElement;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a shot counter element with animation
|
|
34
|
+
*/
|
|
35
|
+
export declare function createShotCounter(): HTMLElement;
|
|
36
|
+
/**
|
|
37
|
+
* Updates the shot counter with animation
|
|
38
|
+
*/
|
|
39
|
+
export declare function updateShotCounter(counter: HTMLElement, count: number): void;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads an icon from either an inline SVG string or a file path
|
|
3
|
+
*/
|
|
4
|
+
async function loadIcon(iconSource) {
|
|
5
|
+
// Check if it's an inline SVG (starts with <svg)
|
|
6
|
+
if (iconSource.trim().startsWith('<svg')) {
|
|
7
|
+
return iconSource;
|
|
8
|
+
}
|
|
9
|
+
// Check if it's a file path (ends with .svg)
|
|
10
|
+
if (iconSource.endsWith('.svg')) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(iconSource);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`Failed to load SVG: ${response.status} ${response.statusText}`);
|
|
15
|
+
}
|
|
16
|
+
const svgContent = await response.text();
|
|
17
|
+
return svgContent;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.error(`Failed to load SVG from path: ${iconSource}`, error);
|
|
21
|
+
// Re-throw error - let the existing fallback system handle it
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// If it's neither, treat as inline SVG
|
|
26
|
+
return iconSource;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Sets the icon for a button element, supporting both inline SVG and file paths
|
|
30
|
+
*/
|
|
31
|
+
export async function setButtonIcon(element, iconSource) {
|
|
32
|
+
// Only proceed if iconSource is provided and not empty
|
|
33
|
+
if (!iconSource || iconSource.trim() === '') {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const svgContent = await loadIcon(iconSource);
|
|
37
|
+
element.innerHTML = svgContent;
|
|
38
|
+
// Apply size styling if specified
|
|
39
|
+
const style = element.__buttonStyle;
|
|
40
|
+
if ((style === null || style === void 0 ? void 0 : style.size) && element.querySelector('svg')) {
|
|
41
|
+
const svg = element.querySelector('svg');
|
|
42
|
+
svg === null || svg === void 0 ? void 0 : svg.setAttribute('width', `${style.size}px`);
|
|
43
|
+
svg === null || svg === void 0 ? void 0 : svg.setAttribute('height', `${style.size}px`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a button with the specified configuration
|
|
48
|
+
*/
|
|
49
|
+
export function createButton(config, text) {
|
|
50
|
+
const btn = document.createElement('button');
|
|
51
|
+
// Store style reference for later use in setButtonIcon
|
|
52
|
+
btn.__buttonStyle = config.style;
|
|
53
|
+
if (text) {
|
|
54
|
+
btn.textContent = text;
|
|
55
|
+
}
|
|
56
|
+
else if (config.text) {
|
|
57
|
+
btn.textContent = config.text;
|
|
58
|
+
}
|
|
59
|
+
else if (config.icon) {
|
|
60
|
+
// Only load icon if one is provided
|
|
61
|
+
setButtonIcon(btn, config.icon).catch(error => {
|
|
62
|
+
console.error('Failed to set button icon:', error);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const style = config.style || {};
|
|
66
|
+
applyButtonStyle(btn, style);
|
|
67
|
+
return btn;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Applies styles to a button element
|
|
71
|
+
*/
|
|
72
|
+
export function applyButtonStyle(element, style) {
|
|
73
|
+
Object.assign(element.style, {
|
|
74
|
+
borderRadius: style.radius ? `${style.radius}px` : '30px',
|
|
75
|
+
padding: style.padding || '10px',
|
|
76
|
+
backgroundColor: style.backgroundColor || '#ffffff',
|
|
77
|
+
color: style.color || '#000000',
|
|
78
|
+
border: style.border || 'none',
|
|
79
|
+
opacity: style.opacity !== undefined ? style.opacity.toString() : '1',
|
|
80
|
+
boxShadow: style.boxShadow || 'none',
|
|
81
|
+
filter: style.filter || 'none',
|
|
82
|
+
display: 'flex',
|
|
83
|
+
alignItems: 'center',
|
|
84
|
+
justifyContent: 'center',
|
|
85
|
+
cursor: 'pointer',
|
|
86
|
+
outline: 'none',
|
|
87
|
+
minWidth: '44px',
|
|
88
|
+
minHeight: '44px'
|
|
89
|
+
});
|
|
90
|
+
if (style.size && element.querySelector('svg')) {
|
|
91
|
+
const svg = element.querySelector('svg');
|
|
92
|
+
svg === null || svg === void 0 ? void 0 : svg.setAttribute('width', `${style.size}px`);
|
|
93
|
+
svg === null || svg === void 0 ? void 0 : svg.setAttribute('height', `${style.size}px`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates a thumbnail image container
|
|
98
|
+
*/
|
|
99
|
+
export function createThumbnailContainer(thumbnailData, thumbnailStyle, onRemove, options) {
|
|
100
|
+
const thumbnailContainer = document.createElement('div');
|
|
101
|
+
const width = (thumbnailStyle === null || thumbnailStyle === void 0 ? void 0 : thumbnailStyle.width) || '80px';
|
|
102
|
+
const height = (thumbnailStyle === null || thumbnailStyle === void 0 ? void 0 : thumbnailStyle.height) || '80px';
|
|
103
|
+
Object.assign(thumbnailContainer.style, {
|
|
104
|
+
position: 'relative',
|
|
105
|
+
display: 'inline-block',
|
|
106
|
+
width: width,
|
|
107
|
+
height: height,
|
|
108
|
+
marginRight: '10px',
|
|
109
|
+
borderRadius: '4px',
|
|
110
|
+
overflow: 'hidden',
|
|
111
|
+
flexShrink: '0',
|
|
112
|
+
boxSizing: 'border-box',
|
|
113
|
+
contentVisibility: 'auto',
|
|
114
|
+
containIntrinsicSize: `${width} ${height}`
|
|
115
|
+
});
|
|
116
|
+
const thumbnail = document.createElement('img');
|
|
117
|
+
thumbnail.src = thumbnailData;
|
|
118
|
+
thumbnail.loading = 'lazy';
|
|
119
|
+
thumbnail.decoding = 'async';
|
|
120
|
+
Object.assign(thumbnail.style, {
|
|
121
|
+
width: '100%',
|
|
122
|
+
height: '100%',
|
|
123
|
+
objectFit: 'cover',
|
|
124
|
+
borderRadius: '4px',
|
|
125
|
+
cursor: 'pointer'
|
|
126
|
+
});
|
|
127
|
+
if (options === null || options === void 0 ? void 0 : options.onTap) {
|
|
128
|
+
const tapHandler = options.onTap;
|
|
129
|
+
thumbnail.addEventListener('click', (e) => {
|
|
130
|
+
e.stopPropagation();
|
|
131
|
+
tapHandler();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
thumbnailContainer.appendChild(thumbnail);
|
|
135
|
+
if (options === null || options === void 0 ? void 0 : options.isVideo) {
|
|
136
|
+
const playBadge = document.createElement('div');
|
|
137
|
+
playBadge.innerHTML = '▶';
|
|
138
|
+
Object.assign(playBadge.style, {
|
|
139
|
+
position: 'absolute',
|
|
140
|
+
left: '50%',
|
|
141
|
+
top: '50%',
|
|
142
|
+
transform: 'translate(-50%, -50%)',
|
|
143
|
+
width: '28px',
|
|
144
|
+
height: '28px',
|
|
145
|
+
borderRadius: '50%',
|
|
146
|
+
background: 'rgba(0, 0, 0, 0.55)',
|
|
147
|
+
color: '#ffffff',
|
|
148
|
+
display: 'flex',
|
|
149
|
+
alignItems: 'center',
|
|
150
|
+
justifyContent: 'center',
|
|
151
|
+
fontSize: '14px',
|
|
152
|
+
zIndex: '2',
|
|
153
|
+
pointerEvents: 'none'
|
|
154
|
+
});
|
|
155
|
+
thumbnailContainer.appendChild(playBadge);
|
|
156
|
+
if (typeof options.duration === 'number') {
|
|
157
|
+
const durationBadge = document.createElement('div');
|
|
158
|
+
durationBadge.textContent = formatDuration(options.duration);
|
|
159
|
+
Object.assign(durationBadge.style, {
|
|
160
|
+
position: 'absolute',
|
|
161
|
+
left: '4px',
|
|
162
|
+
bottom: '4px',
|
|
163
|
+
background: 'rgba(0, 0, 0, 0.7)',
|
|
164
|
+
color: '#ffffff',
|
|
165
|
+
fontSize: '10px',
|
|
166
|
+
lineHeight: '14px',
|
|
167
|
+
borderRadius: '8px',
|
|
168
|
+
padding: '1px 6px',
|
|
169
|
+
zIndex: '2',
|
|
170
|
+
pointerEvents: 'none'
|
|
171
|
+
});
|
|
172
|
+
thumbnailContainer.appendChild(durationBadge);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (onRemove) {
|
|
176
|
+
const removeBtn = createRemoveButton(onRemove);
|
|
177
|
+
thumbnailContainer.appendChild(removeBtn);
|
|
178
|
+
}
|
|
179
|
+
return thumbnailContainer;
|
|
180
|
+
}
|
|
181
|
+
function formatDuration(seconds) {
|
|
182
|
+
const safe = Math.max(0, Math.floor(seconds));
|
|
183
|
+
const mins = Math.floor(safe / 60);
|
|
184
|
+
const secs = safe % 60;
|
|
185
|
+
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Creates a remove button for thumbnails
|
|
189
|
+
*/
|
|
190
|
+
export function createRemoveButton(onRemove) {
|
|
191
|
+
const removeBtn = document.createElement('button');
|
|
192
|
+
Object.assign(removeBtn.style, {
|
|
193
|
+
position: 'absolute',
|
|
194
|
+
top: '3px',
|
|
195
|
+
right: '3px',
|
|
196
|
+
width: '24px',
|
|
197
|
+
height: '24px',
|
|
198
|
+
borderRadius: '50%',
|
|
199
|
+
background: 'rgba(0, 0, 0, 0.5)',
|
|
200
|
+
color: 'white',
|
|
201
|
+
border: 'none',
|
|
202
|
+
cursor: 'pointer',
|
|
203
|
+
display: 'flex',
|
|
204
|
+
justifyContent: 'center',
|
|
205
|
+
alignItems: 'center',
|
|
206
|
+
padding: '0',
|
|
207
|
+
fontSize: '14px',
|
|
208
|
+
fontWeight: 'bold',
|
|
209
|
+
zIndex: '3'
|
|
210
|
+
});
|
|
211
|
+
removeBtn.innerHTML = '×';
|
|
212
|
+
removeBtn.onclick = (e) => {
|
|
213
|
+
e.stopPropagation();
|
|
214
|
+
onRemove(e);
|
|
215
|
+
};
|
|
216
|
+
return removeBtn;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Creates a shot counter element with animation
|
|
220
|
+
*/
|
|
221
|
+
export function createShotCounter() {
|
|
222
|
+
const counter = document.createElement('div');
|
|
223
|
+
counter.id = 'shot-counter';
|
|
224
|
+
Object.assign(counter.style, {
|
|
225
|
+
position: 'relative',
|
|
226
|
+
display: 'flex',
|
|
227
|
+
alignItems: 'center',
|
|
228
|
+
justifyContent: 'center',
|
|
229
|
+
width: '40px',
|
|
230
|
+
height: '40px',
|
|
231
|
+
borderRadius: '20px',
|
|
232
|
+
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
233
|
+
color: '#ffffff',
|
|
234
|
+
fontSize: '16px',
|
|
235
|
+
fontWeight: 'bold',
|
|
236
|
+
margin: '0', // Remove default margin since it's now handled by container gap
|
|
237
|
+
transition: 'all 0.3s ease',
|
|
238
|
+
transform: 'scale(1)',
|
|
239
|
+
opacity: '0',
|
|
240
|
+
pointerEvents: 'none',
|
|
241
|
+
zIndex: '3'
|
|
242
|
+
});
|
|
243
|
+
counter.textContent = '0';
|
|
244
|
+
return counter;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Updates the shot counter with animation
|
|
248
|
+
*/
|
|
249
|
+
export function updateShotCounter(counter, count) {
|
|
250
|
+
const displayCount = count > 99 ? '99+' : count.toString();
|
|
251
|
+
// Show counter if it's hidden and count > 0
|
|
252
|
+
if (count > 0 && counter.style.opacity === '0') {
|
|
253
|
+
counter.style.opacity = '1';
|
|
254
|
+
}
|
|
255
|
+
// Hide counter if count is 0
|
|
256
|
+
if (count === 0) {
|
|
257
|
+
counter.style.opacity = '0';
|
|
258
|
+
counter.textContent = '0';
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
// Animation effect
|
|
262
|
+
counter.style.transform = 'scale(1.2)';
|
|
263
|
+
counter.style.backgroundColor = 'rgba(40, 167, 69, 0.8)'; // Green flash
|
|
264
|
+
// Update the text
|
|
265
|
+
counter.textContent = displayCount;
|
|
266
|
+
// Reset animation after a short delay
|
|
267
|
+
setTimeout(() => {
|
|
268
|
+
counter.style.transform = 'scale(1)';
|
|
269
|
+
counter.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
|
|
270
|
+
}, 200);
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=ui-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-factory.js","sourceRoot":"","sources":["../../../src/ui/ui-factory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Factory for creating UI elements\n */\nimport { ButtonStyle, IconButtonConfig } from '../types/ui-types';\n\n/**\n * Loads an icon from either an inline SVG string or a file path\n */\nasync function loadIcon(iconSource: string): Promise<string> {\n // Check if it's an inline SVG (starts with <svg)\n if (iconSource.trim().startsWith('<svg')) {\n return iconSource;\n }\n \n // Check if it's a file path (ends with .svg)\n if (iconSource.endsWith('.svg')) {\n try {\n const response = await fetch(iconSource);\n if (!response.ok) {\n throw new Error(`Failed to load SVG: ${response.status} ${response.statusText}`);\n }\n const svgContent = await response.text();\n return svgContent;\n } catch (error) {\n console.error(`Failed to load SVG from path: ${iconSource}`, error);\n // Re-throw error - let the existing fallback system handle it\n throw error;\n }\n }\n \n // If it's neither, treat as inline SVG\n return iconSource;\n}\n\n/**\n * Sets the icon for a button element, supporting both inline SVG and file paths\n */\nexport async function setButtonIcon(element: HTMLButtonElement, iconSource: string): Promise<void> {\n // Only proceed if iconSource is provided and not empty\n if (!iconSource || iconSource.trim() === '') {\n return;\n }\n \n const svgContent = await loadIcon(iconSource);\n element.innerHTML = svgContent;\n \n // Apply size styling if specified\n const style = (element as any).__buttonStyle as ButtonStyle;\n if (style?.size && element.querySelector('svg')) {\n const svg = element.querySelector('svg');\n svg?.setAttribute('width', `${style.size}px`);\n svg?.setAttribute('height', `${style.size}px`);\n }\n}\n\n/**\n * Creates a button with the specified configuration\n */\nexport function createButton(config: IconButtonConfig, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n \n // Store style reference for later use in setButtonIcon\n (btn as any).__buttonStyle = config.style;\n \n if (text) {\n btn.textContent = text;\n } else if (config.text) {\n btn.textContent = config.text;\n } else if (config.icon) {\n // Only load icon if one is provided\n setButtonIcon(btn, config.icon).catch(error => {\n console.error('Failed to set button icon:', error);\n });\n }\n \n const style = config.style || {};\n \n applyButtonStyle(btn, style);\n \n return btn;\n}\n\n/**\n * Applies styles to a button element\n */\nexport function applyButtonStyle(element: HTMLButtonElement, style: ButtonStyle): void {\n Object.assign(element.style, {\n borderRadius: style.radius ? `${style.radius}px` : '30px',\n padding: style.padding || '10px',\n backgroundColor: style.backgroundColor || '#ffffff',\n color: style.color || '#000000',\n border: style.border || 'none',\n opacity: style.opacity !== undefined ? style.opacity.toString() : '1',\n boxShadow: style.boxShadow || 'none',\n filter: style.filter || 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n outline: 'none',\n minWidth: '44px',\n minHeight: '44px'\n });\n \n if (style.size && element.querySelector('svg')) {\n const svg = element.querySelector('svg');\n svg?.setAttribute('width', `${style.size}px`);\n svg?.setAttribute('height', `${style.size}px`);\n }\n}\n\n/**\n * Creates a thumbnail image container\n */\nexport function createThumbnailContainer(\n thumbnailData: string, \n thumbnailStyle: { width?: string; height?: string },\n onRemove: (() => void) | null,\n options?: { isVideo?: boolean; duration?: number; onTap?: () => void }\n): HTMLElement {\n const thumbnailContainer = document.createElement('div');\n const width = thumbnailStyle?.width || '80px';\n const height = thumbnailStyle?.height || '80px';\n Object.assign(thumbnailContainer.style, {\n position: 'relative',\n display: 'inline-block',\n width: width,\n height: height,\n marginRight: '10px',\n borderRadius: '4px',\n overflow: 'hidden',\n flexShrink: '0',\n boxSizing: 'border-box',\n contentVisibility: 'auto',\n containIntrinsicSize: `${width} ${height}`\n });\n \n const thumbnail = document.createElement('img');\n thumbnail.src = thumbnailData;\n thumbnail.loading = 'lazy';\n thumbnail.decoding = 'async';\n \n Object.assign(thumbnail.style, {\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n borderRadius: '4px',\n cursor: 'pointer'\n });\n \n if (options?.onTap) {\n const tapHandler = options.onTap;\n thumbnail.addEventListener('click', (e) => {\n e.stopPropagation();\n tapHandler();\n });\n }\n\n thumbnailContainer.appendChild(thumbnail);\n\n if (options?.isVideo) {\n const playBadge = document.createElement('div');\n playBadge.innerHTML = '▶';\n Object.assign(playBadge.style, {\n position: 'absolute',\n left: '50%',\n top: '50%',\n transform: 'translate(-50%, -50%)',\n width: '28px',\n height: '28px',\n borderRadius: '50%',\n background: 'rgba(0, 0, 0, 0.55)',\n color: '#ffffff',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '14px',\n zIndex: '2',\n pointerEvents: 'none'\n });\n thumbnailContainer.appendChild(playBadge);\n\n if (typeof options.duration === 'number') {\n const durationBadge = document.createElement('div');\n durationBadge.textContent = formatDuration(options.duration);\n Object.assign(durationBadge.style, {\n position: 'absolute',\n left: '4px',\n bottom: '4px',\n background: 'rgba(0, 0, 0, 0.7)',\n color: '#ffffff',\n fontSize: '10px',\n lineHeight: '14px',\n borderRadius: '8px',\n padding: '1px 6px',\n zIndex: '2',\n pointerEvents: 'none'\n });\n thumbnailContainer.appendChild(durationBadge);\n }\n }\n\n if (onRemove) {\n const removeBtn = createRemoveButton(onRemove);\n thumbnailContainer.appendChild(removeBtn);\n }\n \n return thumbnailContainer;\n}\n\nfunction formatDuration(seconds: number): string {\n const safe = Math.max(0, Math.floor(seconds));\n const mins = Math.floor(safe / 60);\n const secs = safe % 60;\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n}\n\n/**\n * Creates a remove button for thumbnails\n */\nexport function createRemoveButton(onRemove: (e: Event) => void): HTMLButtonElement {\n const removeBtn = document.createElement('button');\n Object.assign(removeBtn.style, {\n position: 'absolute',\n top: '3px',\n right: '3px',\n width: '24px',\n height: '24px',\n borderRadius: '50%',\n background: 'rgba(0, 0, 0, 0.5)',\n color: 'white',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '0',\n fontSize: '14px',\n fontWeight: 'bold',\n zIndex: '3'\n });\n \n removeBtn.innerHTML = '×';\n \n removeBtn.onclick = (e) => {\n e.stopPropagation();\n onRemove(e);\n };\n \n return removeBtn;\n}\n\n/**\n * Creates a shot counter element with animation\n */\nexport function createShotCounter(): HTMLElement {\n const counter = document.createElement('div');\n counter.id = 'shot-counter';\n \n Object.assign(counter.style, {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '40px',\n height: '40px',\n borderRadius: '20px',\n backgroundColor: 'rgba(0, 0, 0, 0.6)',\n color: '#ffffff',\n fontSize: '16px',\n fontWeight: 'bold',\n margin: '0', // Remove default margin since it's now handled by container gap\n transition: 'all 0.3s ease',\n transform: 'scale(1)',\n opacity: '0',\n pointerEvents: 'none',\n zIndex: '3'\n });\n \n counter.textContent = '0';\n \n return counter;\n}\n\n/**\n * Updates the shot counter with animation\n */\nexport function updateShotCounter(counter: HTMLElement, count: number): void {\n const displayCount = count > 99 ? '99+' : count.toString();\n \n // Show counter if it's hidden and count > 0\n if (count > 0 && counter.style.opacity === '0') {\n counter.style.opacity = '1';\n }\n \n // Hide counter if count is 0\n if (count === 0) {\n counter.style.opacity = '0';\n counter.textContent = '0';\n return;\n }\n \n // Animation effect\n counter.style.transform = 'scale(1.2)';\n counter.style.backgroundColor = 'rgba(40, 167, 69, 0.8)'; // Green flash\n \n // Update the text\n counter.textContent = displayCount;\n \n // Reset animation after a short delay\n setTimeout(() => {\n counter.style.transform = 'scale(1)';\n counter.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';\n }, 200);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PLUGIN_VERSION = "0.12.2";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC","sourcesContent":["// Auto-generated from package.json — do not edit. Run npm run build to update.\nexport const PLUGIN_VERSION = '0.12.2';\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import type { CameraImageData, CameraMultiCapturePlugin, CameraOverlayOptions, CameraOverlayResult, CameraPreviewRect, CameraVideoData, PermissionStatus } from './definitions';
|
|
3
|
+
export declare class CameraMultiCaptureWeb extends WebPlugin implements CameraMultiCapturePlugin {
|
|
4
|
+
capture(): Promise<{
|
|
5
|
+
value: CameraImageData;
|
|
6
|
+
}>;
|
|
7
|
+
stop(): Promise<void>;
|
|
8
|
+
start(_options?: CameraOverlayOptions): Promise<CameraOverlayResult>;
|
|
9
|
+
startVideoRecording(): Promise<void>;
|
|
10
|
+
stopVideoRecording(): Promise<{
|
|
11
|
+
value: CameraVideoData;
|
|
12
|
+
}>;
|
|
13
|
+
switchCamera(): Promise<void>;
|
|
14
|
+
setZoom(_options: {
|
|
15
|
+
zoom: number;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
getFlash(): Promise<{
|
|
18
|
+
flashMode: 'on' | 'off' | 'auto';
|
|
19
|
+
}>;
|
|
20
|
+
setFlash(_options: {
|
|
21
|
+
flashMode: 'on' | 'off' | 'auto';
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
setTorch(_options: {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
getTorch(): Promise<{
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
getAvailableZoomLevels(): Promise<{
|
|
30
|
+
minZoom: number;
|
|
31
|
+
maxZoom: number;
|
|
32
|
+
presetLevels: number[];
|
|
33
|
+
}>;
|
|
34
|
+
updatePreviewRect(_options: CameraPreviewRect): Promise<void>;
|
|
35
|
+
getAvailableCameras(): Promise<{
|
|
36
|
+
hasUltrawide: boolean;
|
|
37
|
+
hasWide: boolean;
|
|
38
|
+
hasTelephoto: boolean;
|
|
39
|
+
ultrawideZoomFactor?: number;
|
|
40
|
+
wideZoomFactor: number;
|
|
41
|
+
telephotoZoomFactor?: number;
|
|
42
|
+
}>;
|
|
43
|
+
switchToPhysicalCamera(_options: {
|
|
44
|
+
zoomFactor: number;
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
checkPermissions(): Promise<PermissionStatus>;
|
|
47
|
+
requestPermissions(): Promise<PermissionStatus>;
|
|
48
|
+
queueBackgroundUpload(_options: {
|
|
49
|
+
imageUri: string;
|
|
50
|
+
uploadEndpoint: string;
|
|
51
|
+
headers: Record<string, string>;
|
|
52
|
+
formData?: Record<string, string>;
|
|
53
|
+
method?: 'POST' | 'PUT';
|
|
54
|
+
deleteAfterUpload?: boolean;
|
|
55
|
+
}): Promise<{
|
|
56
|
+
jobId: string;
|
|
57
|
+
}>;
|
|
58
|
+
getUploadStatus(_options: {
|
|
59
|
+
jobId: string;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
status: 'pending' | 'uploading' | 'completed' | 'failed';
|
|
62
|
+
error?: string;
|
|
63
|
+
}>;
|
|
64
|
+
getVersion(): Promise<{
|
|
65
|
+
version: string;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
declare const CameraMultiCapture: CameraMultiCaptureWeb;
|
|
69
|
+
export { CameraMultiCapture };
|