@feugene/granularity-media 0.3.0 → 0.4.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.
Files changed (36) hide show
  1. package/README.md +23 -6
  2. package/dist/chunks/config-DfJkKtIh.js +27 -0
  3. package/dist/chunks/{en-C-Wc5fTB.js → en-BugobSAO.js} +12 -4
  4. package/dist/chunks/{es-BUPtE9_0.js → es-B7iA-ExH.js} +12 -4
  5. package/dist/chunks/ru-DSjpFh8N.js +4 -0
  6. package/dist/chunks/{ru-BqNILj54.js → ru-DpiW6Vax.js} +12 -4
  7. package/dist/chunks/useCameraStream-CPdm3r90.js +86 -0
  8. package/dist/components/GrCameraCapture/chunks/GrCameraCapture-t-uFTD9s.js +134 -0
  9. package/dist/components/GrCameraCapture/index.js +4 -3
  10. package/dist/components/GrCodeScanner/chunks/GrCodeScanner-BX147Gct.js +162 -0
  11. package/dist/components/GrCodeScanner/index.js +3 -0
  12. package/dist/granular-provider.js +13 -11
  13. package/dist/i18n/all.js +1 -1
  14. package/dist/i18n/index.js +4 -4
  15. package/dist/index.js +8 -5
  16. package/dist/resolver.js +5 -1
  17. package/dist/types/componentNames.d.ts +1 -1
  18. package/dist/types/components/GrCameraCapture/GrCameraCapture.vue.d.ts +3 -5
  19. package/dist/types/components/GrCodeScanner/GrCodeScanner.vue.d.ts +121 -0
  20. package/dist/types/components/GrCodeScanner/__tests__/GrCodeScanner.test.d.ts +1 -0
  21. package/dist/types/components/GrCodeScanner/__tests__/codeDetection.test.d.ts +1 -0
  22. package/dist/types/components/GrCodeScanner/codeDetection.d.ts +42 -0
  23. package/dist/types/components/GrCodeScanner/config.d.ts +5 -0
  24. package/dist/types/components/GrCodeScanner/defaults.d.ts +16 -0
  25. package/dist/types/components/GrCodeScanner/grCodeScannerStyles.d.ts +19 -0
  26. package/dist/types/components/GrCodeScanner/index.d.ts +9 -0
  27. package/dist/types/granular-provider/shared.d.ts +1 -0
  28. package/dist/types/i18n/index.d.ts +21 -0
  29. package/dist/types/i18n/messages/en.d.ts +14 -0
  30. package/dist/types/i18n/messages/es.d.ts +14 -0
  31. package/dist/types/i18n/messages/ru.d.ts +14 -0
  32. package/dist/types/index.d.ts +1 -0
  33. package/dist/types/internal/useCameraStream.d.ts +30 -0
  34. package/package.json +5 -1
  35. package/dist/chunks/ru-DN_wJDkP.js +0 -4
  36. package/dist/components/GrCameraCapture/chunks/GrCameraCapture-CoSuBEfS.js +0 -182
@@ -48,8 +48,6 @@ type __VLS_Slots = {
48
48
  capture: () => Promise<Blob | null>;
49
49
  }) => unknown;
50
50
  };
51
- declare function start(): Promise<void>;
52
- declare function stop(): void;
53
51
  /**
54
52
  * Снимок текущего кадра — целиком, без среза.
55
53
  *
@@ -74,8 +72,8 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
74
72
  disabled: boolean;
75
73
  ariaLabel: undefined;
76
74
  }>>, {
77
- start: typeof start;
78
- stop: typeof stop;
75
+ start: () => Promise<void>;
76
+ stop: () => void;
79
77
  capture: typeof capture;
80
78
  status: import("vue").Ref<GrCameraStatus, GrCameraStatus>;
81
79
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
@@ -99,9 +97,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
99
97
  onStop?: (() => any) | undefined;
100
98
  onStatusChange?: ((status: GrCameraStatus) => any) | undefined;
101
99
  }>, {
102
- facing: GrCameraFacing;
103
100
  deviceId: string;
104
101
  aspectRatio: number;
102
+ facing: GrCameraFacing;
105
103
  autoStart: boolean;
106
104
  mirror: boolean;
107
105
  output: GrCameraCaptureOutput;
@@ -0,0 +1,121 @@
1
+ import type { GrCameraStatus } from '../GrCameraCapture/cameraState';
2
+ import type { GrCodeDetector, GrCodeResult } from './codeDetection';
3
+ import type { GrCodeScannerSize } from './grCodeScannerStyles';
4
+ export interface GrCodeScannerProps {
5
+ /** Какая камера: `environment` — тыловая, ей и сканируют. */
6
+ facing?: 'user' | 'environment';
7
+ deviceId?: string;
8
+ /**
9
+ * Включать камеру сразу.
10
+ *
11
+ * По умолчанию `false`: запрос разрешения без действия пользователя
12
+ * отклоняют не глядя, а второй раз браузер уже не спросит.
13
+ */
14
+ autoStart?: boolean;
15
+ /** Символики: `qr_code`, `ean_13`, `code_128`, … Без списка — все, что умеет браузер. */
16
+ formats?: readonly string[];
17
+ /**
18
+ * Детектор для браузеров без `BarcodeDetector` — Safari и Firefox.
19
+ *
20
+ * Пакет своего декодера не несёт: у него нет ни одной зависимости, и ради
21
+ * одного компонента появилась бы самая тяжёлая. Рецепт подключения готовой
22
+ * библиотеки — на странице компонента.
23
+ */
24
+ detector?: GrCodeDetector;
25
+ /** Как часто разбирать кадр, мс. Чаще — горячее устройство, реже — вялый отклик. */
26
+ interval?: number;
27
+ /** Сообщать один и тот же код повторно: приёмка сканирует подряд. */
28
+ continuous?: boolean;
29
+ size?: GrCodeScannerSize;
30
+ disabled?: boolean;
31
+ ariaLabel?: string;
32
+ }
33
+ export interface GrCodeScannerEmits {
34
+ (e: 'detect', codes: GrCodeResult[]): void;
35
+ (e: 'start'): void;
36
+ (e: 'stop'): void;
37
+ (e: 'statusChange', status: GrCameraStatus): void;
38
+ }
39
+ type __VLS_Slots = {
40
+ /** Своя панель управления вместо встроенной. */
41
+ controls?: (props: {
42
+ status: GrCameraStatus;
43
+ start: () => void;
44
+ stop: () => void;
45
+ }) => unknown;
46
+ };
47
+ declare function stopAll(): void;
48
+ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<GrCodeScannerProps>, {
49
+ facing: string;
50
+ deviceId: undefined;
51
+ autoStart: boolean;
52
+ formats: undefined;
53
+ detector: undefined;
54
+ interval: number;
55
+ continuous: boolean;
56
+ size: undefined;
57
+ disabled: boolean;
58
+ ariaLabel: undefined;
59
+ }>>, {
60
+ start: () => Promise<void>;
61
+ stop: typeof stopAll;
62
+ status: import("vue").Ref<GrCameraStatus, GrCameraStatus>;
63
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
64
+ detect: (codes: GrCodeResult[]) => void;
65
+ start: () => void;
66
+ stop: () => void;
67
+ statusChange: (status: GrCameraStatus) => void;
68
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<GrCodeScannerProps>, {
69
+ facing: string;
70
+ deviceId: undefined;
71
+ autoStart: boolean;
72
+ formats: undefined;
73
+ detector: undefined;
74
+ interval: number;
75
+ continuous: boolean;
76
+ size: undefined;
77
+ disabled: boolean;
78
+ ariaLabel: undefined;
79
+ }>>> & Readonly<{
80
+ onStart?: (() => any) | undefined;
81
+ onStop?: (() => any) | undefined;
82
+ onStatusChange?: ((status: GrCameraStatus) => any) | undefined;
83
+ onDetect?: ((codes: GrCodeResult[]) => any) | undefined;
84
+ }>, {
85
+ deviceId: string;
86
+ facing: "user" | "environment";
87
+ autoStart: boolean;
88
+ size: GrCodeScannerSize;
89
+ disabled: boolean;
90
+ ariaLabel: string;
91
+ formats: readonly string[];
92
+ detector: GrCodeDetector;
93
+ interval: number;
94
+ continuous: boolean;
95
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
96
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
97
+ declare const _default: typeof __VLS_export;
98
+ export default _default;
99
+ type __VLS_TypePropsToOption<T> = {
100
+ [K in keyof T]-?: {} extends Pick<T, K> ? {
101
+ type: import('vue').PropType<Required<T>[K]>;
102
+ } : {
103
+ type: import('vue').PropType<T[K]>;
104
+ required: true;
105
+ };
106
+ };
107
+ type __VLS_WithDefaults<P, D> = {
108
+ [K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_PrettifyLocal<P[K] & {
109
+ default: D[K];
110
+ }> : P[K];
111
+ };
112
+ type __VLS_WithSlots<T, S> = T & {
113
+ new (): {
114
+ $slots: S;
115
+ };
116
+ };
117
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
118
+ [K in keyof T]: T[K];
119
+ } : {
120
+ [K in keyof T as K]: T[K];
121
+ }) & {};
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Распознавание кодов: нативный путь и точка подключения для остальных.
3
+ *
4
+ * `BarcodeDetector` есть в Chrome и Edge, но его нет ни в Safari, ни в Firefox —
5
+ * то есть на iPhone его нет вовсе, а там сканируют чаще всего. Тащить декодер в
6
+ * пакет нельзя: у него нет ни одной зависимости, и ради одного компонента
7
+ * появилась бы самая тяжёлая. Поэтому компонент умеет нативный путь сам, а
8
+ * остальные браузеры закрывает детектором, который передаёт приложение.
9
+ */
10
+ /** Один распознанный код. */
11
+ export interface GrCodeResult {
12
+ /** Содержимое: адрес, номер товара, текст. */
13
+ value: string;
14
+ /** Символика: `qr_code`, `ean_13`, `code_128`, … */
15
+ format: string;
16
+ }
17
+ /**
18
+ * Детектор кадра. Ровно та форма, в которую заворачивается любая библиотека:
19
+ * на вход — то, что умеет рисовать canvas, на выход — список кодов.
20
+ */
21
+ export type GrCodeDetector = (source: CanvasImageSource) => Promise<GrCodeResult[]>;
22
+ /** Есть ли нативный `BarcodeDetector`. */
23
+ export declare function nativeDetectorSupported(scope?: typeof globalThis): boolean;
24
+ /**
25
+ * Нативный детектор в общей форме.
26
+ *
27
+ * Формат приводится к нашему типу здесь, а не в компоненте: спецификация зовёт
28
+ * содержимое `rawValue`, и это единственное место, где чужое имя видно.
29
+ */
30
+ export declare function createNativeDetector(formats: readonly string[] | undefined, scope?: typeof globalThis): GrCodeDetector | null;
31
+ /**
32
+ * Что сообщать наружу из очередного кадра.
33
+ *
34
+ * Камера отдаёт десятки кадров в секунду, и один и тот же код распознаётся в
35
+ * каждом. Без фильтра приложение получило бы поток одинаковых событий и
36
+ * оформило бы двадцать заказов вместо одного. Поэтому по умолчанию сообщается
37
+ * только то, чего не было в предыдущем кадре.
38
+ *
39
+ * `continuous` нужен там, где сканируют подряд (приёмка на складе): там повтор
40
+ * того же кода — законное второе событие.
41
+ */
42
+ export declare function freshCodes(previous: readonly GrCodeResult[], next: readonly GrCodeResult[], continuous: boolean): GrCodeResult[];
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Кнопки состояния и управления — `GrButton` ядра, и ребро объявлено: пресет
3
+ * подмешивает safelist и CSS только тем компонентам, что попали в селекцию.
4
+ */
5
+ export declare const grCodeScannerConfig: import("@feugene/unocss-preset-granular/contract").GranularComponentDescriptor<"GrCodeScanner">;
@@ -0,0 +1,16 @@
1
+ import type { GrCodeScannerSize } from './grCodeScannerStyles';
2
+ /**
3
+ * Настраиваемые через `GrConfigProvider` пропы.
4
+ *
5
+ * Аугментация реестра идёт по месту объявления — через
6
+ * `@feugene/granularity/composables/useGrComponentConfig`, а не через реэкспорт:
7
+ * иначе слияние отваливается молча.
8
+ */
9
+ export interface GrCodeScannerConfigurableProps {
10
+ size?: GrCodeScannerSize;
11
+ }
12
+ declare module '@feugene/granularity/composables/useGrComponentConfig' {
13
+ interface GrComponentDefaultsRegistry {
14
+ GrCodeScanner?: GrCodeScannerConfigurableProps;
15
+ }
16
+ }
@@ -0,0 +1,19 @@
1
+ export type GrCodeScannerSize = 'xs' | 'sm' | 'md' | 'lg';
2
+ export declare const rootClass = "grid gap-2";
3
+ export declare const frameClass = "relative w-full overflow-hidden rounded-[var(--gr-radius-md)] border border-[var(--gr-brd)] bg-[var(--gr-muted)]";
4
+ export declare const videoClass = "h-full w-full object-contain";
5
+ /**
6
+ * Прицел: показывает, куда наводить. Рамка, а не затемнение вокруг, — код
7
+ * распознаётся по всему кадру, и затемнение обещало бы обратное.
8
+ */
9
+ export declare const reticleClass = "pointer-events-none absolute inset-[15%] rounded-[var(--gr-radius-md)] border-2 border-[var(--gr-code-scanner-reticle,var(--gr-primary))]";
10
+ export declare const stateLayerClass = "absolute inset-0 grid place-content-center gap-3 p-4 text-center transition-opacity duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]";
11
+ export declare const stateTextClass = "text-[var(--gr-muted-fg)]";
12
+ export declare const controlsClass = "flex flex-wrap items-center gap-2";
13
+ export declare const sizeTextClass: {
14
+ readonly xs: "text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]";
15
+ readonly sm: "text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]";
16
+ readonly md: "text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)]";
17
+ readonly lg: "text-[length:var(--gr-control-text-lg)] leading-[var(--gr-control-leading-lg)]";
18
+ };
19
+ export declare const grCodeScannerSafelist: string[];
@@ -0,0 +1,9 @@
1
+ export { default } from './GrCodeScanner.vue';
2
+ export { default as GrCodeScanner } from './GrCodeScanner.vue';
3
+ export type { GrCodeDetector, GrCodeResult } from './codeDetection';
4
+ export { createNativeDetector, freshCodes, nativeDetectorSupported } from './codeDetection';
5
+ export { grCodeScannerConfig } from './config';
6
+ export type { GrCodeScannerConfigurableProps } from './defaults';
7
+ export type { GrCodeScannerEmits, GrCodeScannerProps } from './GrCodeScanner.vue';
8
+ export { grCodeScannerSafelist } from './grCodeScannerStyles';
9
+ export type { GrCodeScannerSize } from './grCodeScannerStyles';
@@ -9,6 +9,7 @@ export declare const GRANULARITY_MEDIA_PROVIDER_ID = "@feugene/granularity-media
9
9
  */
10
10
  export declare const granularityMediaComponentConfigs: {
11
11
  GrCameraCapture: GranularComponentDescriptor<"GrCameraCapture">;
12
+ GrCodeScanner: GranularComponentDescriptor<"GrCodeScanner">;
12
13
  GrImageCrop: GranularComponentDescriptor<"GrImageCrop">;
13
14
  };
14
15
  export type GranularityMediaComponentName = keyof typeof granularityMediaComponentConfigs;
@@ -22,6 +22,13 @@ export declare const grMediaMessages: {
22
22
  live: string;
23
23
  captured: string;
24
24
  };
25
+ scanner: {
26
+ label: string;
27
+ start: string;
28
+ stop: string;
29
+ unsupported: string;
30
+ detected: string;
31
+ };
25
32
  };
26
33
  readonly es: {
27
34
  imageCrop: {
@@ -46,6 +53,13 @@ export declare const grMediaMessages: {
46
53
  live: string;
47
54
  captured: string;
48
55
  };
56
+ scanner: {
57
+ label: string;
58
+ start: string;
59
+ stop: string;
60
+ unsupported: string;
61
+ detected: string;
62
+ };
49
63
  };
50
64
  readonly ru: {
51
65
  imageCrop: {
@@ -70,6 +84,13 @@ export declare const grMediaMessages: {
70
84
  live: string;
71
85
  captured: string;
72
86
  };
87
+ scanner: {
88
+ label: string;
89
+ start: string;
90
+ stop: string;
91
+ unsupported: string;
92
+ detected: string;
93
+ };
73
94
  };
74
95
  };
75
96
  export type GrMediaLocale = keyof typeof grMediaMessages;
@@ -24,6 +24,13 @@ export declare const en: {
24
24
  live: string;
25
25
  captured: string;
26
26
  };
27
+ scanner: {
28
+ label: string;
29
+ start: string;
30
+ stop: string;
31
+ unsupported: string;
32
+ detected: string;
33
+ };
27
34
  };
28
35
  imageCrop: {
29
36
  label: string;
@@ -47,6 +54,13 @@ export declare const en: {
47
54
  live: string;
48
55
  captured: string;
49
56
  };
57
+ scanner: {
58
+ label: string;
59
+ start: string;
60
+ stop: string;
61
+ unsupported: string;
62
+ detected: string;
63
+ };
50
64
  }>;
51
65
  };
52
66
  };
@@ -24,6 +24,13 @@ export declare const es: {
24
24
  live: string;
25
25
  captured: string;
26
26
  };
27
+ scanner: {
28
+ label: string;
29
+ start: string;
30
+ stop: string;
31
+ unsupported: string;
32
+ detected: string;
33
+ };
27
34
  };
28
35
  imageCrop: {
29
36
  label: string;
@@ -47,6 +54,13 @@ export declare const es: {
47
54
  live: string;
48
55
  captured: string;
49
56
  };
57
+ scanner: {
58
+ label: string;
59
+ start: string;
60
+ stop: string;
61
+ unsupported: string;
62
+ detected: string;
63
+ };
50
64
  }>;
51
65
  };
52
66
  };
@@ -24,6 +24,13 @@ export declare const ru: {
24
24
  live: string;
25
25
  captured: string;
26
26
  };
27
+ scanner: {
28
+ label: string;
29
+ start: string;
30
+ stop: string;
31
+ unsupported: string;
32
+ detected: string;
33
+ };
27
34
  };
28
35
  imageCrop: {
29
36
  label: string;
@@ -47,6 +54,13 @@ export declare const ru: {
47
54
  live: string;
48
55
  captured: string;
49
56
  };
57
+ scanner: {
58
+ label: string;
59
+ start: string;
60
+ stop: string;
61
+ unsupported: string;
62
+ detected: string;
63
+ };
50
64
  }>;
51
65
  };
52
66
  };
@@ -1,2 +1,3 @@
1
1
  export * from './components/GrCameraCapture';
2
+ export * from './components/GrCodeScanner';
2
3
  export * from './components/GrImageCrop';
@@ -0,0 +1,30 @@
1
+ import type { Ref } from 'vue';
2
+ import type { GrCameraStatus } from '../components/GrCameraCapture/cameraState';
3
+ /**
4
+ * Поток камеры: запрос, состояния отказа, размеры кадра, остановка.
5
+ *
6
+ * Общий для съёмки и сканирования — у обоих одна и та же половина работы, и
7
+ * дважды написанная она разъехалась бы на первом же исправлении: отказ, забытая
8
+ * остановка дорожек, соотношение сторон до `loadedmetadata`. Компоненту
9
+ * остаётся то, ради чего он существует: снять кадр или распознать код.
10
+ */
11
+ export interface UseCameraStreamOptions {
12
+ video: Ref<HTMLVideoElement | null>;
13
+ disabled: () => boolean;
14
+ facing: () => 'user' | 'environment';
15
+ deviceId: () => string | undefined;
16
+ /** Пожелание камере, а не требование: см. `exact` ниже. */
17
+ aspectRatio?: () => number | undefined;
18
+ onStatus?: (status: GrCameraStatus) => void;
19
+ onStart?: () => void;
20
+ onStop?: () => void;
21
+ }
22
+ export declare function useCameraStream(options: UseCameraStreamOptions): {
23
+ status: Ref<GrCameraStatus, GrCameraStatus>;
24
+ streamRatio: Ref<number | null, number | null>;
25
+ frameRatio: import("vue").ComputedRef<number>;
26
+ isLive: import("vue").ComputedRef<boolean>;
27
+ message: import("vue").ComputedRef<string>;
28
+ start: () => Promise<void>;
29
+ stop: () => void;
30
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@feugene/granularity-media",
3
3
  "description": "Media components for @feugene/granularity: image cropping, camera capture, code scanning and video playback.",
4
- "version": "0.3.0",
4
+ "version": "0.4.0",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "Evgeniy Fureev",
@@ -51,6 +51,10 @@
51
51
  "types": "./dist/types/components/GrCameraCapture/index.d.ts",
52
52
  "import": "./dist/components/GrCameraCapture/index.js"
53
53
  },
54
+ "./components/GrCodeScanner": {
55
+ "types": "./dist/types/components/GrCodeScanner/index.d.ts",
56
+ "import": "./dist/components/GrCodeScanner/index.js"
57
+ },
54
58
  "./components/GrImageCrop": {
55
59
  "types": "./dist/types/components/GrImageCrop/index.d.ts",
56
60
  "import": "./dist/components/GrImageCrop/index.js"
@@ -1,4 +0,0 @@
1
- //#region src/i18n/messages/const.ts
2
- var e = "grMedia", t = { en: { [e]: async () => import("./en-C-Wc5fTB.js").then((e) => e.n) } }, n = { es: { [e]: async () => import("./es-BUPtE9_0.js").then((e) => e.n) } }, r = { ru: { [e]: async () => import("./ru-BqNILj54.js").then((e) => e.n) } };
3
- //#endregion
4
- export { e as i, n, t as r, r as t };
@@ -1,182 +0,0 @@
1
- import { t as e } from "../../../chunks/outputSize-D2FU4CbB.js";
2
- import { a as t, c as n, l as r, n as i, o as a, r as o, s, u as c } from "../../../chunks/config-DHLlFslI.js";
3
- import { computed as l, createBlock as u, createCommentVNode as d, createElementBlock as f, createElementVNode as p, createTextVNode as m, createVNode as h, defineComponent as g, normalizeClass as _, normalizeStyle as v, onBeforeUnmount as y, onMounted as b, openBlock as x, ref as S, renderSlot as ee, shallowRef as C, toDisplayString as w, unref as T, withCtx as E } from "vue";
4
- import D from "@feugene/granularity/components/GrButton";
5
- import { useAnnouncer as O } from "@feugene/granularity/composables/useAnnouncer";
6
- import { useGrComponentSize as te } from "@feugene/granularity/composables/useGrComponentConfig";
7
- import { useGranularityTranslations as ne } from "@feugene/granularity/composables/useGranularityTranslations";
8
- //#region src/components/GrCameraCapture/cameraState.ts
9
- function k(e) {
10
- switch (e instanceof Error ? e.name : "") {
11
- case "NotAllowedError":
12
- case "SecurityError":
13
- case "PermissionDeniedError": return "denied";
14
- case "NotFoundError":
15
- case "DevicesNotFoundError":
16
- case "OverconstrainedError": return "missing";
17
- case "NotReadableError":
18
- case "TrackStartError":
19
- case "AbortError": return "busy";
20
- default: return "error";
21
- }
22
- }
23
- function A(e) {
24
- return typeof e?.mediaDevices?.getUserMedia == "function";
25
- }
26
- function j(e, t) {
27
- return t ?? e === "user";
28
- }
29
- //#endregion
30
- //#region src/components/GrCameraCapture/GrCameraCapture.vue?vue&type=script&setup=true&lang.ts
31
- var M = ["aria-label"], N = /* @__PURE__ */ g({
32
- __name: "GrCameraCapture",
33
- props: {
34
- facing: { default: "user" },
35
- deviceId: { default: void 0 },
36
- aspectRatio: { default: void 0 },
37
- autoStart: {
38
- type: Boolean,
39
- default: !1
40
- },
41
- mirror: {
42
- type: Boolean,
43
- default: void 0
44
- },
45
- output: { default: void 0 },
46
- size: { default: void 0 },
47
- disabled: {
48
- type: Boolean,
49
- default: !1
50
- },
51
- ariaLabel: { default: void 0 }
52
- },
53
- emits: [
54
- "capture",
55
- "start",
56
- "stop",
57
- "statusChange"
58
- ],
59
- setup(g, { expose: N, emit: P }) {
60
- let F = g, I = P, { t: L } = ne(), { announce: R } = O(), z = te(() => F.size, { component: "GrCameraCapture" }), B = S(null), V = C(null), H = S("idle"), U = S(null), W = l(() => U.value ?? 4 / 3), G = l(() => j(F.facing, F.mirror)), K = l(() => H.value === "live"), q = l(() => {
61
- switch (H.value) {
62
- case "starting": return L("grMedia.camera.starting", "Requesting camera access…");
63
- case "denied": return L("grMedia.camera.denied", "Camera access is blocked. Allow it in the browser settings for this site.");
64
- case "missing": return L("grMedia.camera.missing", "No camera found on this device.");
65
- case "busy": return L("grMedia.camera.busy", "The camera is busy in another application.");
66
- case "insecure": return L("grMedia.camera.insecure", "The camera works only over HTTPS.");
67
- case "error": return L("grMedia.camera.error", "The camera could not be started.");
68
- default: return L("grMedia.camera.idle", "The camera is off.");
69
- }
70
- });
71
- function J(e) {
72
- H.value !== e && (H.value = e, I("statusChange", e));
73
- }
74
- function Y() {
75
- let e = B.value;
76
- !e || e.videoWidth <= 0 || e.videoHeight <= 0 || (U.value = e.videoWidth / e.videoHeight);
77
- }
78
- function X() {
79
- V.value?.getTracks().forEach((e) => e.stop()), V.value = null, U.value = null, B.value && (B.value.srcObject = null);
80
- }
81
- async function Z() {
82
- if (!(F.disabled || H.value === "starting" || H.value === "live")) {
83
- if (!A(navigator)) {
84
- J("insecure");
85
- return;
86
- }
87
- J("starting");
88
- try {
89
- let e = await navigator.mediaDevices.getUserMedia({
90
- video: {
91
- ...F.deviceId ? { deviceId: { exact: F.deviceId } } : { facingMode: F.facing },
92
- ...F.aspectRatio ? { aspectRatio: F.aspectRatio } : {}
93
- },
94
- audio: !1
95
- });
96
- V.value = e, B.value && (B.value.srcObject = e, B.value.addEventListener("loadedmetadata", Y, { once: !0 }), await B.value.play().catch(() => void 0), Y()), J("live"), I("start"), R(L("grMedia.camera.live", "Camera is on"));
97
- } catch (e) {
98
- X(), J(k(e)), R(q.value, { politeness: "assertive" });
99
- }
100
- }
101
- }
102
- function Q() {
103
- V.value && (X(), J("idle"), I("stop"));
104
- }
105
- async function $() {
106
- let t = B.value;
107
- if (!t || !K.value) return null;
108
- let n = {
109
- sx: 0,
110
- sy: 0,
111
- sw: t.videoWidth,
112
- sh: t.videoHeight
113
- };
114
- if (n.sw <= 0) return null;
115
- let r = document.createElement("canvas"), i = e(n, F.output);
116
- r.width = i.width, r.height = i.height;
117
- let a = r.getContext("2d");
118
- if (!a) return null;
119
- a.drawImage(t, n.sx, n.sy, n.sw, n.sh, 0, 0, r.width, r.height);
120
- let o = await new Promise((e) => {
121
- r.toBlob(e, F.output?.type ?? "image/jpeg", F.output?.quality ?? .92);
122
- });
123
- return o && (I("capture", o), R(L("grMedia.camera.captured", "Photo taken"))), o;
124
- }
125
- return b(() => {
126
- F.autoStart && Z();
127
- }), y(X), N({
128
- start: Z,
129
- stop: Q,
130
- capture: $,
131
- status: H
132
- }), (e, l) => (x(), f("div", {
133
- class: _(T(a)),
134
- role: "group",
135
- "aria-label": g.ariaLabel ?? T(L)("grMedia.camera.label", "Camera")
136
- }, [p("div", {
137
- class: _(T(o)),
138
- style: v({ aspectRatio: String(W.value) })
139
- }, [p("video", {
140
- ref_key: "videoEl",
141
- ref: B,
142
- class: _([T(c), G.value ? T(t) : ""]),
143
- playsinline: "",
144
- muted: "",
145
- autoplay: ""
146
- }, null, 2), K.value ? d("", !0) : (x(), f("div", {
147
- key: 0,
148
- class: _(T(n))
149
- }, [p("p", { class: _([T(r), T(s)[T(z)]]) }, w(q.value), 3), H.value !== "starting" && H.value !== "insecure" && H.value !== "missing" ? (x(), u(T(D), {
150
- key: 0,
151
- size: T(z),
152
- disabled: g.disabled,
153
- onClick: Z
154
- }, {
155
- default: E(() => [m(w(H.value === "idle" ? T(L)("grMedia.camera.start", "Turn on camera") : T(L)("grMedia.camera.retry", "Try again")), 1)]),
156
- _: 1
157
- }, 8, ["size", "disabled"])) : d("", !0)], 2))], 6), p("div", { class: _(T(i)) }, [ee(e.$slots, "controls", {
158
- status: H.value,
159
- start: Z,
160
- stop: Q,
161
- capture: $
162
- }, () => [h(T(D), {
163
- size: T(z),
164
- disabled: g.disabled || !K.value,
165
- onClick: $
166
- }, {
167
- default: E(() => [m(w(T(L)("grMedia.camera.capture", "Take photo")), 1)]),
168
- _: 1
169
- }, 8, ["size", "disabled"]), K.value ? (x(), u(T(D), {
170
- key: 0,
171
- variant: "outline",
172
- size: T(z),
173
- disabled: g.disabled,
174
- onClick: Q
175
- }, {
176
- default: E(() => [m(w(T(L)("grMedia.camera.stop", "Turn off")), 1)]),
177
- _: 1
178
- }, 8, ["size", "disabled"])) : d("", !0)])], 2)], 10, M));
179
- }
180
- });
181
- //#endregion
182
- export { j as i, k as n, A as r, N as t };