@feugene/granularity-media 0.1.0 → 0.2.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/README.md +27 -4
- package/dist/chunks/classTokens-bAzTjLuQ.js +6 -0
- package/dist/chunks/{config-CrV_FDnM.js → config-Be_Mh801.js} +13 -17
- package/dist/chunks/config-CZ4ZDG0_.js +27 -0
- package/dist/chunks/en-C-Wc5fTB.js +32 -0
- package/dist/chunks/es-BUPtE9_0.js +32 -0
- package/dist/chunks/ru-BqNILj54.js +32 -0
- package/dist/chunks/ru-DN_wJDkP.js +4 -0
- package/dist/components/GrCameraCapture/chunks/GrCameraCapture-DJKFrVfw.js +197 -0
- package/dist/components/GrCameraCapture/index.js +3 -0
- package/dist/components/GrImageCrop/chunks/{GrImageCrop-DYM2vZHV.js → GrImageCrop-Fo2qrX2n.js} +8 -8
- package/dist/components/GrImageCrop/index.js +2 -2
- package/dist/granular-provider.js +15 -11
- package/dist/i18n/all.js +1 -1
- package/dist/i18n/index.js +4 -4
- package/dist/index.js +5 -3
- package/dist/resolver.js +1 -1
- package/dist/types/componentNames.d.ts +1 -1
- package/dist/types/components/GrCameraCapture/GrCameraCapture.vue.d.ts +125 -0
- package/dist/types/components/GrCameraCapture/__tests__/GrCameraCapture.test.d.ts +1 -0
- package/dist/types/components/GrCameraCapture/__tests__/cameraState.test.d.ts +1 -0
- package/dist/types/components/GrCameraCapture/cameraState.d.ts +67 -0
- package/dist/types/components/GrCameraCapture/config.d.ts +6 -0
- package/dist/types/components/GrCameraCapture/defaults.d.ts +16 -0
- package/dist/types/components/GrCameraCapture/grCameraCaptureStyles.d.ts +21 -0
- package/dist/types/components/GrCameraCapture/index.d.ts +9 -0
- package/dist/types/components/GrImageCrop/GrImageCrop.vue.d.ts +5 -5
- package/dist/types/granular-provider/shared.d.ts +1 -0
- package/dist/types/i18n/index.d.ts +48 -0
- package/dist/types/i18n/messages/en.d.ts +32 -0
- package/dist/types/i18n/messages/es.d.ts +32 -0
- package/dist/types/i18n/messages/ru.d.ts +32 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +5 -1
- package/dist/chunks/en-BrjpU7lq.js +0 -13
- package/dist/chunks/es-BdTkuOLs.js +0 -13
- package/dist/chunks/ru-ByR24Aqk.js +0 -13
- package/dist/chunks/ru-C4IsSzR5.js +0 -4
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# `@feugene/granularity-media`
|
|
2
2
|
|
|
3
|
-
Работа с изображением и камерой для `@feugene/granularity`.
|
|
4
|
-
|
|
3
|
+
Работа с изображением и камерой для `@feugene/granularity`. Два компонента, и они
|
|
4
|
+
стоят рядом по сценарию: `GrCameraCapture` снимает кадр камерой,
|
|
5
|
+
`GrImageCrop` выбирает кадр из уже существующей картинки. Оба отдают готовый
|
|
6
|
+
`Blob` — можно сразу отправлять.
|
|
5
7
|
|
|
6
8
|
```bash
|
|
7
9
|
yarn add @feugene/granularity-media
|
|
@@ -29,7 +31,9 @@ async function save() {
|
|
|
29
31
|
|
|
30
32
|
<template>
|
|
31
33
|
<GrImageCrop ref="cropper" :src="file" shape="circle" :output="{ width: 256, height: 256 }" />
|
|
32
|
-
<button @click="save"
|
|
34
|
+
<button @click="save">
|
|
35
|
+
Сохранить
|
|
36
|
+
</button>
|
|
33
37
|
</template>
|
|
34
38
|
```
|
|
35
39
|
|
|
@@ -73,8 +77,27 @@ presetGranularNode({
|
|
|
73
77
|
Локали подключаются тем же `createFintI18n`, что и словари ядра — блок
|
|
74
78
|
`GR_MEDIA_I18N_BLOCK`.
|
|
75
79
|
|
|
80
|
+
## Снимок камерой
|
|
81
|
+
|
|
82
|
+
```vue
|
|
83
|
+
<GrCameraCapture :aspect-ratio="1" @capture="blob => upload(blob)" />
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Камера включается только по кнопке: запрос разрешения, всплывший сам по себе,
|
|
87
|
+
отклоняют не глядя — а второй раз браузер уже не спросит, потому что решение
|
|
88
|
+
запоминается для сайта целиком.
|
|
89
|
+
|
|
90
|
+
Отказ разведён на четыре состояния (`denied`, `missing`, `busy`, `insecure`),
|
|
91
|
+
потому что действия пользователя в них разные: «разрешите доступ» при
|
|
92
|
+
отсутствующей камере отправляет искать настройку, которой нет, а на `http://`
|
|
93
|
+
объекта `navigator.mediaDevices` не существует вовсе.
|
|
94
|
+
|
|
95
|
+
Превью фронтальной камеры зеркальное, снимок — нет: иначе текст на визитке или в
|
|
96
|
+
документе уехал бы в зазеркалье.
|
|
97
|
+
|
|
76
98
|
## Документация
|
|
77
99
|
|
|
78
100
|
- [`docs/components.md`](./docs/components.md) — состав пакета;
|
|
79
|
-
- [`docs/components/GrImageCrop.md`](./docs/components/GrImageCrop.md)
|
|
101
|
+
- [`docs/components/GrImageCrop.md`](./docs/components/GrImageCrop.md) и
|
|
102
|
+
[`docs/components/GrCameraCapture.md`](./docs/components/GrCameraCapture.md) — страницы компонентов;
|
|
80
103
|
- [`docs/keyboard.md`](./docs/keyboard.md), [`docs/ssr.md`](./docs/ssr.md) — сквозные правила пакета.
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
function t(e) {
|
|
4
|
-
return e.split(/\s+/).filter(Boolean);
|
|
5
|
-
}
|
|
6
|
-
//#endregion
|
|
1
|
+
import { t as e } from "./classTokens-bAzTjLuQ.js";
|
|
2
|
+
import { defineGranularComponent as t } from "@feugene/unocss-preset-granular/contract";
|
|
7
3
|
//#region src/components/GrImageCrop/grImageCropStyles.ts
|
|
8
4
|
var n = "grid gap-2", r = "relative w-full select-none overflow-hidden rounded-[var(--gr-radius-md)] border border-[var(--gr-brd)] bg-[var(--gr-muted)] touch-none focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]", i = "pointer-events-none absolute left-1/2 top-1/2 max-w-none origin-center", a = "pointer-events-none absolute inset-0 m-auto aspect-square rounded-[var(--gr-radius-full)] border border-[var(--gr-image-crop-guide,var(--gr-brd))] shadow-[0_0_0_9999px_var(--gr-image-crop-mask,rgb(0_0_0/0.55))]", o = "pointer-events-none absolute inset-0 border border-[var(--gr-image-crop-guide,var(--gr-brd))]", s = "transition-transform duration-[var(--gr-duration-fast)] ease-[var(--gr-ease-out)]", c = "flex items-center gap-3", l = "flex items-center justify-center text-[var(--gr-muted-fg)]", u = {
|
|
9
5
|
xs: "text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]",
|
|
@@ -16,17 +12,17 @@ var n = "grid gap-2", r = "relative w-full select-none overflow-hidden rounded-[
|
|
|
16
12
|
md: "min-h-48",
|
|
17
13
|
lg: "min-h-56"
|
|
18
14
|
}, f = [
|
|
19
|
-
...
|
|
20
|
-
...
|
|
21
|
-
...
|
|
22
|
-
...
|
|
23
|
-
...
|
|
24
|
-
...
|
|
25
|
-
...
|
|
26
|
-
...
|
|
27
|
-
...Object.values(u).flatMap(
|
|
28
|
-
...Object.values(d).flatMap(
|
|
29
|
-
], p =
|
|
15
|
+
...e(n),
|
|
16
|
+
...e(r),
|
|
17
|
+
...e(i),
|
|
18
|
+
...e(s),
|
|
19
|
+
...e(a),
|
|
20
|
+
...e(o),
|
|
21
|
+
...e(c),
|
|
22
|
+
...e(l),
|
|
23
|
+
...Object.values(u).flatMap(e),
|
|
24
|
+
...Object.values(d).flatMap(e)
|
|
25
|
+
], p = t(import.meta.url, {
|
|
30
26
|
name: "GrImageCrop",
|
|
31
27
|
safelist: f,
|
|
32
28
|
dependencies: [{
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { t as e } from "./classTokens-bAzTjLuQ.js";
|
|
2
|
+
import { defineGranularComponent as t } from "@feugene/unocss-preset-granular/contract";
|
|
3
|
+
//#region src/components/GrCameraCapture/grCameraCaptureStyles.ts
|
|
4
|
+
var n = "grid gap-2", r = "relative w-full overflow-hidden rounded-[var(--gr-radius-md)] border border-[var(--gr-brd)] bg-[var(--gr-muted)]", i = "h-full w-full object-cover", a = "scale-x-[-1]", o = "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)]", s = "text-[var(--gr-muted-fg)]", c = "flex flex-wrap items-center gap-2", l = {
|
|
5
|
+
xs: "text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]",
|
|
6
|
+
sm: "text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]",
|
|
7
|
+
md: "text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)]",
|
|
8
|
+
lg: "text-[length:var(--gr-control-text-lg)] leading-[var(--gr-control-leading-lg)]"
|
|
9
|
+
}, u = [
|
|
10
|
+
...e(n),
|
|
11
|
+
...e(r),
|
|
12
|
+
...e(i),
|
|
13
|
+
...e(a),
|
|
14
|
+
...e(o),
|
|
15
|
+
...e(s),
|
|
16
|
+
...e(c),
|
|
17
|
+
...Object.values(l).flatMap(e)
|
|
18
|
+
], d = t(import.meta.url, {
|
|
19
|
+
name: "GrCameraCapture",
|
|
20
|
+
safelist: u,
|
|
21
|
+
dependencies: [{
|
|
22
|
+
provider: "@feugene/granularity",
|
|
23
|
+
components: ["GrButton"]
|
|
24
|
+
}]
|
|
25
|
+
});
|
|
26
|
+
//#endregion
|
|
27
|
+
export { a, o as c, u as i, s as l, c as n, n as o, r, l as s, d as t, i as u };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
+
//#region src/i18n/locales/en.json
|
|
3
|
+
var t = /* @__PURE__ */ e({
|
|
4
|
+
camera: () => r,
|
|
5
|
+
default: () => i,
|
|
6
|
+
imageCrop: () => n
|
|
7
|
+
}), n = {
|
|
8
|
+
label: "Image crop",
|
|
9
|
+
frame: "Frame: arrow keys move the image, plus and minus zoom",
|
|
10
|
+
empty: "No image selected",
|
|
11
|
+
zoom: "Zoom"
|
|
12
|
+
}, r = {
|
|
13
|
+
label: "Camera",
|
|
14
|
+
idle: "The camera is off.",
|
|
15
|
+
starting: "Requesting camera access…",
|
|
16
|
+
denied: "Camera access is blocked. Allow it in the browser settings for this site.",
|
|
17
|
+
missing: "No camera found on this device.",
|
|
18
|
+
busy: "The camera is busy in another application.",
|
|
19
|
+
insecure: "The camera works only over HTTPS.",
|
|
20
|
+
error: "The camera could not be started.",
|
|
21
|
+
start: "Turn on camera",
|
|
22
|
+
retry: "Try again",
|
|
23
|
+
capture: "Take photo",
|
|
24
|
+
stop: "Turn off",
|
|
25
|
+
live: "Camera is on",
|
|
26
|
+
captured: "Photo taken"
|
|
27
|
+
}, i = {
|
|
28
|
+
imageCrop: n,
|
|
29
|
+
camera: r
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { t as n, i as t };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
+
//#region src/i18n/locales/es.json
|
|
3
|
+
var t = /* @__PURE__ */ e({
|
|
4
|
+
camera: () => r,
|
|
5
|
+
default: () => i,
|
|
6
|
+
imageCrop: () => n
|
|
7
|
+
}), n = {
|
|
8
|
+
label: "Recorte de imagen",
|
|
9
|
+
frame: "Marco: las flechas mueven la imagen, más y menos ajustan el zoom",
|
|
10
|
+
empty: "Ninguna imagen seleccionada",
|
|
11
|
+
zoom: "Zoom"
|
|
12
|
+
}, r = {
|
|
13
|
+
label: "Cámara",
|
|
14
|
+
idle: "La cámara está apagada.",
|
|
15
|
+
starting: "Solicitando acceso a la cámara…",
|
|
16
|
+
denied: "El acceso a la cámara está bloqueado. Permítelo en la configuración del navegador para este sitio.",
|
|
17
|
+
missing: "No se encontró ninguna cámara en este dispositivo.",
|
|
18
|
+
busy: "La cámara está ocupada por otra aplicación.",
|
|
19
|
+
insecure: "La cámara solo funciona por HTTPS.",
|
|
20
|
+
error: "No se pudo iniciar la cámara.",
|
|
21
|
+
start: "Encender cámara",
|
|
22
|
+
retry: "Reintentar",
|
|
23
|
+
capture: "Tomar foto",
|
|
24
|
+
stop: "Apagar",
|
|
25
|
+
live: "Cámara encendida",
|
|
26
|
+
captured: "Foto tomada"
|
|
27
|
+
}, i = {
|
|
28
|
+
imageCrop: n,
|
|
29
|
+
camera: r
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { t as n, i as t };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
+
//#region src/i18n/locales/ru.json
|
|
3
|
+
var t = /* @__PURE__ */ e({
|
|
4
|
+
camera: () => r,
|
|
5
|
+
default: () => i,
|
|
6
|
+
imageCrop: () => n
|
|
7
|
+
}), n = {
|
|
8
|
+
label: "Кадрирование изображения",
|
|
9
|
+
frame: "Кадр: стрелки двигают изображение, плюс и минус меняют увеличение",
|
|
10
|
+
empty: "Изображение не выбрано",
|
|
11
|
+
zoom: "Увеличение"
|
|
12
|
+
}, r = {
|
|
13
|
+
label: "Камера",
|
|
14
|
+
idle: "Камера выключена.",
|
|
15
|
+
starting: "Запрашиваем доступ к камере…",
|
|
16
|
+
denied: "Доступ к камере запрещён. Разрешите его в настройках браузера для этого сайта.",
|
|
17
|
+
missing: "Камера на устройстве не найдена.",
|
|
18
|
+
busy: "Камера занята другим приложением.",
|
|
19
|
+
insecure: "Камера работает только по HTTPS.",
|
|
20
|
+
error: "Не удалось включить камеру.",
|
|
21
|
+
start: "Включить камеру",
|
|
22
|
+
retry: "Повторить",
|
|
23
|
+
capture: "Сделать снимок",
|
|
24
|
+
stop: "Выключить",
|
|
25
|
+
live: "Камера включена",
|
|
26
|
+
captured: "Снимок сделан"
|
|
27
|
+
}, i = {
|
|
28
|
+
imageCrop: n,
|
|
29
|
+
camera: r
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { t as n, i as t };
|
|
@@ -0,0 +1,4 @@
|
|
|
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 };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { a as e, c as t, l as n, n as r, o as i, r as a, s as o, u as s } from "../../../chunks/config-CZ4ZDG0_.js";
|
|
2
|
+
import { computed as c, createBlock as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, createTextVNode as p, createVNode as m, defineComponent as h, normalizeClass as g, normalizeStyle as _, onBeforeUnmount as v, onMounted as y, openBlock as b, ref as x, renderSlot as S, shallowRef as C, toDisplayString as w, unref as T, withCtx as E } from "vue";
|
|
3
|
+
import D from "@feugene/granularity/components/GrButton";
|
|
4
|
+
import { useAnnouncer as O } from "@feugene/granularity/composables/useAnnouncer";
|
|
5
|
+
import { useGrComponentSize as k } from "@feugene/granularity/composables/useGrComponentConfig";
|
|
6
|
+
import { useGranularityTranslations as A } from "@feugene/granularity/composables/useGranularityTranslations";
|
|
7
|
+
//#region src/components/GrCameraCapture/cameraState.ts
|
|
8
|
+
function j(e) {
|
|
9
|
+
switch (e instanceof Error ? e.name : "") {
|
|
10
|
+
case "NotAllowedError":
|
|
11
|
+
case "SecurityError":
|
|
12
|
+
case "PermissionDeniedError": return "denied";
|
|
13
|
+
case "NotFoundError":
|
|
14
|
+
case "DevicesNotFoundError":
|
|
15
|
+
case "OverconstrainedError": return "missing";
|
|
16
|
+
case "NotReadableError":
|
|
17
|
+
case "TrackStartError":
|
|
18
|
+
case "AbortError": return "busy";
|
|
19
|
+
default: return "error";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function M(e) {
|
|
23
|
+
return typeof e?.mediaDevices?.getUserMedia == "function";
|
|
24
|
+
}
|
|
25
|
+
function N(e, t) {
|
|
26
|
+
return t ?? e === "user";
|
|
27
|
+
}
|
|
28
|
+
function P(e, t) {
|
|
29
|
+
let n = t > 0 ? t : 1;
|
|
30
|
+
if (e.width <= 0 || e.height <= 0) return {
|
|
31
|
+
sx: 0,
|
|
32
|
+
sy: 0,
|
|
33
|
+
sw: 0,
|
|
34
|
+
sh: 0
|
|
35
|
+
};
|
|
36
|
+
if (e.width / e.height > n) {
|
|
37
|
+
let t = e.height * n;
|
|
38
|
+
return {
|
|
39
|
+
sx: (e.width - t) / 2,
|
|
40
|
+
sy: 0,
|
|
41
|
+
sw: t,
|
|
42
|
+
sh: e.height
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
let r = e.width / n;
|
|
46
|
+
return {
|
|
47
|
+
sx: 0,
|
|
48
|
+
sy: (e.height - r) / 2,
|
|
49
|
+
sw: e.width,
|
|
50
|
+
sh: r
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/components/GrCameraCapture/GrCameraCapture.vue?vue&type=script&setup=true&lang.ts
|
|
55
|
+
var F = ["aria-label"], I = /* @__PURE__ */ h({
|
|
56
|
+
__name: "GrCameraCapture",
|
|
57
|
+
props: {
|
|
58
|
+
facing: { default: "user" },
|
|
59
|
+
deviceId: { default: void 0 },
|
|
60
|
+
autoStart: {
|
|
61
|
+
type: Boolean,
|
|
62
|
+
default: !1
|
|
63
|
+
},
|
|
64
|
+
aspectRatio: { default: 4 / 3 },
|
|
65
|
+
mirror: {
|
|
66
|
+
type: Boolean,
|
|
67
|
+
default: void 0
|
|
68
|
+
},
|
|
69
|
+
output: { default: void 0 },
|
|
70
|
+
size: { default: void 0 },
|
|
71
|
+
disabled: {
|
|
72
|
+
type: Boolean,
|
|
73
|
+
default: !1
|
|
74
|
+
},
|
|
75
|
+
ariaLabel: { default: void 0 }
|
|
76
|
+
},
|
|
77
|
+
emits: [
|
|
78
|
+
"capture",
|
|
79
|
+
"start",
|
|
80
|
+
"stop",
|
|
81
|
+
"statusChange"
|
|
82
|
+
],
|
|
83
|
+
setup(h, { expose: I, emit: L }) {
|
|
84
|
+
let R = h, z = L, { t: B } = A(), { announce: V } = O(), H = k(() => R.size, { component: "GrCameraCapture" }), U = x(null), W = C(null), G = x("idle"), K = c(() => N(R.facing, R.mirror)), q = c(() => G.value === "live"), J = c(() => {
|
|
85
|
+
switch (G.value) {
|
|
86
|
+
case "starting": return B("grMedia.camera.starting", "Requesting camera access…");
|
|
87
|
+
case "denied": return B("grMedia.camera.denied", "Camera access is blocked. Allow it in the browser settings for this site.");
|
|
88
|
+
case "missing": return B("grMedia.camera.missing", "No camera found on this device.");
|
|
89
|
+
case "busy": return B("grMedia.camera.busy", "The camera is busy in another application.");
|
|
90
|
+
case "insecure": return B("grMedia.camera.insecure", "The camera works only over HTTPS.");
|
|
91
|
+
case "error": return B("grMedia.camera.error", "The camera could not be started.");
|
|
92
|
+
default: return B("grMedia.camera.idle", "The camera is off.");
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
function Y(e) {
|
|
96
|
+
G.value !== e && (G.value = e, z("statusChange", e));
|
|
97
|
+
}
|
|
98
|
+
function X() {
|
|
99
|
+
W.value?.getTracks().forEach((e) => e.stop()), W.value = null, U.value && (U.value.srcObject = null);
|
|
100
|
+
}
|
|
101
|
+
async function Z() {
|
|
102
|
+
if (!(R.disabled || G.value === "starting" || G.value === "live")) {
|
|
103
|
+
if (!M(navigator)) {
|
|
104
|
+
Y("insecure");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
Y("starting");
|
|
108
|
+
try {
|
|
109
|
+
let e = await navigator.mediaDevices.getUserMedia({
|
|
110
|
+
video: R.deviceId ? { deviceId: { exact: R.deviceId } } : { facingMode: R.facing },
|
|
111
|
+
audio: !1
|
|
112
|
+
});
|
|
113
|
+
W.value = e, U.value && (U.value.srcObject = e, await U.value.play().catch(() => void 0)), Y("live"), z("start"), V(B("grMedia.camera.live", "Camera is on"));
|
|
114
|
+
} catch (e) {
|
|
115
|
+
X(), Y(j(e)), V(J.value, { politeness: "assertive" });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function Q() {
|
|
120
|
+
W.value && (X(), Y("idle"), z("stop"));
|
|
121
|
+
}
|
|
122
|
+
async function $() {
|
|
123
|
+
let e = U.value;
|
|
124
|
+
if (!e || !q.value) return null;
|
|
125
|
+
let t = P({
|
|
126
|
+
width: e.videoWidth,
|
|
127
|
+
height: e.videoHeight
|
|
128
|
+
}, R.aspectRatio);
|
|
129
|
+
if (t.sw <= 0) return null;
|
|
130
|
+
let n = document.createElement("canvas");
|
|
131
|
+
n.width = Math.max(1, Math.round(R.output?.width ?? t.sw)), n.height = Math.max(1, Math.round(R.output?.height ?? t.sh));
|
|
132
|
+
let r = n.getContext("2d");
|
|
133
|
+
if (!r) return null;
|
|
134
|
+
r.drawImage(e, t.sx, t.sy, t.sw, t.sh, 0, 0, n.width, n.height);
|
|
135
|
+
let i = await new Promise((e) => {
|
|
136
|
+
n.toBlob(e, R.output?.type ?? "image/jpeg", R.output?.quality ?? .92);
|
|
137
|
+
});
|
|
138
|
+
return i && (z("capture", i), V(B("grMedia.camera.captured", "Photo taken"))), i;
|
|
139
|
+
}
|
|
140
|
+
return y(() => {
|
|
141
|
+
R.autoStart && Z();
|
|
142
|
+
}), v(X), I({
|
|
143
|
+
start: Z,
|
|
144
|
+
stop: Q,
|
|
145
|
+
capture: $,
|
|
146
|
+
status: G
|
|
147
|
+
}), (c, v) => (b(), d("div", {
|
|
148
|
+
class: g(T(i)),
|
|
149
|
+
role: "group",
|
|
150
|
+
"aria-label": h.ariaLabel ?? T(B)("grMedia.camera.label", "Camera")
|
|
151
|
+
}, [f("div", {
|
|
152
|
+
class: g(T(a)),
|
|
153
|
+
style: _({ aspectRatio: String(h.aspectRatio) })
|
|
154
|
+
}, [f("video", {
|
|
155
|
+
ref_key: "videoEl",
|
|
156
|
+
ref: U,
|
|
157
|
+
class: g([T(s), K.value ? T(e) : ""]),
|
|
158
|
+
playsinline: "",
|
|
159
|
+
muted: "",
|
|
160
|
+
autoplay: ""
|
|
161
|
+
}, null, 2), q.value ? u("", !0) : (b(), d("div", {
|
|
162
|
+
key: 0,
|
|
163
|
+
class: g(T(t))
|
|
164
|
+
}, [f("p", { class: g([T(n), T(o)[T(H)]]) }, w(J.value), 3), G.value !== "starting" && G.value !== "insecure" && G.value !== "missing" ? (b(), l(T(D), {
|
|
165
|
+
key: 0,
|
|
166
|
+
size: T(H),
|
|
167
|
+
disabled: h.disabled,
|
|
168
|
+
onClick: Z
|
|
169
|
+
}, {
|
|
170
|
+
default: E(() => [p(w(G.value === "idle" ? T(B)("grMedia.camera.start", "Turn on camera") : T(B)("grMedia.camera.retry", "Try again")), 1)]),
|
|
171
|
+
_: 1
|
|
172
|
+
}, 8, ["size", "disabled"])) : u("", !0)], 2))], 6), f("div", { class: g(T(r)) }, [S(c.$slots, "controls", {
|
|
173
|
+
status: G.value,
|
|
174
|
+
start: Z,
|
|
175
|
+
stop: Q,
|
|
176
|
+
capture: $
|
|
177
|
+
}, () => [m(T(D), {
|
|
178
|
+
size: T(H),
|
|
179
|
+
disabled: h.disabled || !q.value,
|
|
180
|
+
onClick: $
|
|
181
|
+
}, {
|
|
182
|
+
default: E(() => [p(w(T(B)("grMedia.camera.capture", "Take photo")), 1)]),
|
|
183
|
+
_: 1
|
|
184
|
+
}, 8, ["size", "disabled"]), q.value ? (b(), l(T(D), {
|
|
185
|
+
key: 0,
|
|
186
|
+
variant: "outline",
|
|
187
|
+
size: T(H),
|
|
188
|
+
disabled: h.disabled,
|
|
189
|
+
onClick: Q
|
|
190
|
+
}, {
|
|
191
|
+
default: E(() => [p(w(T(B)("grMedia.camera.stop", "Turn off")), 1)]),
|
|
192
|
+
_: 1
|
|
193
|
+
}, 8, ["size", "disabled"])) : u("", !0)])], 2)], 10, F));
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
//#endregion
|
|
197
|
+
export { N as a, M as i, P as n, j as r, I as t };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "./chunks/GrCameraCapture-DJKFrVfw.js";
|
|
2
|
+
import { i as a, t as o } from "../../chunks/config-CZ4ZDG0_.js";
|
|
3
|
+
export { i as GrCameraCapture, i as default, n as cameraFrameRect, r as cameraStatusFromError, t as cameraSupported, o as grCameraCaptureConfig, a as grCameraCaptureSafelist, e as shouldMirrorPreview };
|
package/dist/components/GrImageCrop/chunks/{GrImageCrop-DYM2vZHV.js → GrImageCrop-Fo2qrX2n.js}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { c as e, d as t, f as n, i as r, l as i, n as a, o, r as s, s as c, u as l } from "../../../chunks/config-
|
|
1
|
+
import { c as e, d as t, f as n, i as r, l as i, n as a, o, r as s, s as c, u as l } from "../../../chunks/config-Be_Mh801.js";
|
|
2
2
|
import { Fragment as u, computed as d, createCommentVNode as ee, createElementBlock as f, createElementVNode as p, createVNode as te, defineComponent as m, normalizeClass as h, normalizeStyle as g, onBeforeUnmount as _, onMounted as ne, openBlock as v, ref as y, renderSlot as b, shallowRef as x, toDisplayString as re, unref as S, watch as ie } from "vue";
|
|
3
|
-
import ae from "@feugene/granularity/
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
3
|
+
import { useGrComponentSize as ae } from "@feugene/granularity/composables/useGrComponentConfig";
|
|
4
|
+
import { useGranularityTranslations as oe } from "@feugene/granularity/composables/useGranularityTranslations";
|
|
5
|
+
import se from "@feugene/granularity/components/GrSlider";
|
|
6
|
+
import { useDragGesture as ce } from "@feugene/granularity/composables/useDragGesture";
|
|
7
7
|
//#region src/components/GrImageCrop/cropGeometry.ts
|
|
8
8
|
function C(e, t) {
|
|
9
9
|
return e.width <= 0 || e.height <= 0 ? 1 : Math.max(t.width / e.width, t.height / e.height);
|
|
@@ -80,7 +80,7 @@ var le = ["aria-label"], ue = [
|
|
|
80
80
|
"error"
|
|
81
81
|
],
|
|
82
82
|
setup(m, { expose: C, emit: w }) {
|
|
83
|
-
let T = m, j = w, { t: M } =
|
|
83
|
+
let T = m, j = w, { t: M } = oe(), N = ae(() => T.size, { component: "GrImageCrop" }), P = y(null), F = x(null), I = y({
|
|
84
84
|
width: 0,
|
|
85
85
|
height: 0
|
|
86
86
|
}), L = y(0), R = y({
|
|
@@ -123,7 +123,7 @@ var le = ["aria-label"], ue = [
|
|
|
123
123
|
}), Y = y({
|
|
124
124
|
x: 0,
|
|
125
125
|
y: 0
|
|
126
|
-
}), X =
|
|
126
|
+
}), X = ce({
|
|
127
127
|
disabled: () => T.disabled || !V.value,
|
|
128
128
|
onStart: (e) => {
|
|
129
129
|
J.value = {
|
|
@@ -249,7 +249,7 @@ var le = ["aria-label"], ue = [
|
|
|
249
249
|
}, [b(d.$slots, "controls", {
|
|
250
250
|
zoom: H.value,
|
|
251
251
|
setZoom: K
|
|
252
|
-
}, () => [te(S(
|
|
252
|
+
}, () => [te(S(se), {
|
|
253
253
|
"model-value": H.value,
|
|
254
254
|
min: 1,
|
|
255
255
|
max: m.maxZoom,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./chunks/GrImageCrop-
|
|
2
|
-
import { a as s, t as c } from "../../chunks/config-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./chunks/GrImageCrop-Fo2qrX2n.js";
|
|
2
|
+
import { a as s, t as c } from "../../chunks/config-Be_Mh801.js";
|
|
3
3
|
export { o as GrImageCrop, o as default, n as clampOffset, i as coverScale, t as cropRect, e as displaySize, c as grImageCropConfig, s as grImageCropSafelist, r as offsetBounds, a as viewportFor };
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import { t as e } from "./chunks/config-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { t as e } from "./chunks/config-CZ4ZDG0_.js";
|
|
2
|
+
import { t } from "./chunks/config-Be_Mh801.js";
|
|
3
|
+
import { defineGranularProvider as n, resolvePackageBaseUrl as r } from "@feugene/unocss-preset-granular/contract";
|
|
4
|
+
import { granularityProvider as i } from "@feugene/granularity/granular-provider";
|
|
4
5
|
//#region src/granular-provider/shared.ts
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
var a = "@feugene/granularity-media", o = {
|
|
7
|
+
GrCameraCapture: e,
|
|
8
|
+
GrImageCrop: t
|
|
9
|
+
};
|
|
10
|
+
function s(e, t) {
|
|
11
|
+
return n({
|
|
12
|
+
id: a,
|
|
9
13
|
contractVersion: 1,
|
|
10
|
-
packageBaseUrl:
|
|
11
|
-
components: Object.values(
|
|
14
|
+
packageBaseUrl: t,
|
|
15
|
+
components: Object.values(o),
|
|
12
16
|
dependencies: [e]
|
|
13
17
|
});
|
|
14
18
|
}
|
|
15
|
-
var
|
|
19
|
+
var c = s(i, r(import.meta.url, 0));
|
|
16
20
|
//#endregion
|
|
17
|
-
export {
|
|
21
|
+
export { a as GRANULARITY_MEDIA_PROVIDER_ID, s as createGranularityMediaProvider, c as default, c as granularityMediaProvider, o as granularityMediaComponentConfigs };
|
package/dist/i18n/all.js
CHANGED
package/dist/i18n/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as e } from "../chunks/en-
|
|
2
|
-
import { t } from "../chunks/es-
|
|
3
|
-
import { t as n } from "../chunks/ru-
|
|
4
|
-
import { i as r, n as i, r as a, t as o } from "../chunks/ru-
|
|
1
|
+
import { t as e } from "../chunks/en-C-Wc5fTB.js";
|
|
2
|
+
import { t } from "../chunks/es-BUPtE9_0.js";
|
|
3
|
+
import { t as n } from "../chunks/ru-BqNILj54.js";
|
|
4
|
+
import { i as r, n as i, r as a, t as o } from "../chunks/ru-DN_wJDkP.js";
|
|
5
5
|
//#region src/i18n/index.ts
|
|
6
6
|
var s = {
|
|
7
7
|
en: e,
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import { a as e, i as t, n,
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "./components/GrCameraCapture/chunks/GrCameraCapture-DJKFrVfw.js";
|
|
2
|
+
import { i as a, t as o } from "./chunks/config-CZ4ZDG0_.js";
|
|
3
|
+
import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./components/GrImageCrop/chunks/GrImageCrop-Fo2qrX2n.js";
|
|
4
|
+
import { a as m, t as h } from "./chunks/config-Be_Mh801.js";
|
|
5
|
+
export { i as GrCameraCapture, p as GrImageCrop, n as cameraFrameRect, r as cameraStatusFromError, t as cameraSupported, l as clampOffset, d as coverScale, c as cropRect, s as displaySize, o as grCameraCaptureConfig, a as grCameraCaptureSafelist, h as grImageCropConfig, m as grImageCropSafelist, u as offsetBounds, e as shouldMirrorPreview, f as viewportFor };
|
package/dist/resolver.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createGranularResolver as e } from "@feugene/unplugin-granularity";
|
|
2
2
|
//#region src/componentNames.ts
|
|
3
|
-
var t = ["GrImageCrop"], n = "@feugene/granularity-media";
|
|
3
|
+
var t = ["GrCameraCapture", "GrImageCrop"], n = "@feugene/granularity-media";
|
|
4
4
|
function r() {
|
|
5
5
|
return e({
|
|
6
6
|
packageName: n,
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Список генерируется `yarn generate:registry` — руками не писать.
|
|
10
10
|
*/
|
|
11
|
-
export declare const GRANULARITY_MEDIA_COMPONENTS: readonly ["GrImageCrop"];
|
|
11
|
+
export declare const GRANULARITY_MEDIA_COMPONENTS: readonly ["GrCameraCapture", "GrImageCrop"];
|
|
12
12
|
export type GranularityMediaComponentName = typeof GRANULARITY_MEDIA_COMPONENTS[number];
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { GrCameraStatus } from './cameraState';
|
|
2
|
+
import type { GrCameraCaptureSize, GrCameraFacing } from './grCameraCaptureStyles';
|
|
3
|
+
export interface GrCameraCaptureOutput {
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
type?: string;
|
|
7
|
+
quality?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface GrCameraCaptureProps {
|
|
10
|
+
/** Какая камера: `user` — фронтальная, `environment` — тыловая. */
|
|
11
|
+
facing?: GrCameraFacing;
|
|
12
|
+
/** Конкретное устройство, если приложение его уже выбрало. */
|
|
13
|
+
deviceId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Включать камеру сразу.
|
|
16
|
+
*
|
|
17
|
+
* По умолчанию `false`: запрос разрешения без действия пользователя
|
|
18
|
+
* отклоняют не глядя, а второй раз браузер уже не спросит.
|
|
19
|
+
*/
|
|
20
|
+
autoStart?: boolean;
|
|
21
|
+
aspectRatio?: number;
|
|
22
|
+
/** Зеркалить превью. По умолчанию — только фронтальную камеру. */
|
|
23
|
+
mirror?: boolean;
|
|
24
|
+
output?: GrCameraCaptureOutput;
|
|
25
|
+
size?: GrCameraCaptureSize;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface GrCameraCaptureEmits {
|
|
30
|
+
(e: 'capture', blob: Blob): void;
|
|
31
|
+
(e: 'start'): void;
|
|
32
|
+
(e: 'stop'): void;
|
|
33
|
+
(e: 'statusChange', status: GrCameraStatus): void;
|
|
34
|
+
}
|
|
35
|
+
type __VLS_Slots = {
|
|
36
|
+
/** Своя панель управления вместо встроенной. */
|
|
37
|
+
controls?: (props: {
|
|
38
|
+
status: GrCameraStatus;
|
|
39
|
+
start: () => void;
|
|
40
|
+
stop: () => void;
|
|
41
|
+
capture: () => Promise<Blob | null>;
|
|
42
|
+
}) => unknown;
|
|
43
|
+
};
|
|
44
|
+
declare function start(): Promise<void>;
|
|
45
|
+
declare function stop(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Снимок текущего кадра.
|
|
48
|
+
*
|
|
49
|
+
* Зеркало превью сюда **не переносится**: на снимке оказывается то, что было
|
|
50
|
+
* перед камерой. Иначе текст на визитке или в документе уехал бы в зазеркалье —
|
|
51
|
+
* а снимают ими ровно это.
|
|
52
|
+
*/
|
|
53
|
+
declare function capture(): Promise<Blob | null>;
|
|
54
|
+
declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<GrCameraCaptureProps>, {
|
|
55
|
+
facing: string;
|
|
56
|
+
deviceId: undefined;
|
|
57
|
+
autoStart: boolean;
|
|
58
|
+
aspectRatio: number;
|
|
59
|
+
mirror: undefined;
|
|
60
|
+
output: undefined;
|
|
61
|
+
size: undefined;
|
|
62
|
+
disabled: boolean;
|
|
63
|
+
ariaLabel: undefined;
|
|
64
|
+
}>>, {
|
|
65
|
+
start: typeof start;
|
|
66
|
+
stop: typeof stop;
|
|
67
|
+
capture: typeof capture;
|
|
68
|
+
status: import("vue").Ref<GrCameraStatus, GrCameraStatus>;
|
|
69
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
70
|
+
capture: (blob: Blob) => void;
|
|
71
|
+
start: () => void;
|
|
72
|
+
stop: () => void;
|
|
73
|
+
statusChange: (status: GrCameraStatus) => void;
|
|
74
|
+
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<GrCameraCaptureProps>, {
|
|
75
|
+
facing: string;
|
|
76
|
+
deviceId: undefined;
|
|
77
|
+
autoStart: boolean;
|
|
78
|
+
aspectRatio: number;
|
|
79
|
+
mirror: undefined;
|
|
80
|
+
output: undefined;
|
|
81
|
+
size: undefined;
|
|
82
|
+
disabled: boolean;
|
|
83
|
+
ariaLabel: undefined;
|
|
84
|
+
}>>> & Readonly<{
|
|
85
|
+
onCapture?: ((blob: Blob) => any) | undefined;
|
|
86
|
+
onStart?: (() => any) | undefined;
|
|
87
|
+
onStop?: (() => any) | undefined;
|
|
88
|
+
onStatusChange?: ((status: GrCameraStatus) => any) | undefined;
|
|
89
|
+
}>, {
|
|
90
|
+
facing: GrCameraFacing;
|
|
91
|
+
deviceId: string;
|
|
92
|
+
autoStart: boolean;
|
|
93
|
+
aspectRatio: number;
|
|
94
|
+
mirror: boolean;
|
|
95
|
+
output: GrCameraCaptureOutput;
|
|
96
|
+
size: GrCameraCaptureSize;
|
|
97
|
+
disabled: boolean;
|
|
98
|
+
ariaLabel: string;
|
|
99
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
100
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
101
|
+
declare const _default: typeof __VLS_export;
|
|
102
|
+
export default _default;
|
|
103
|
+
type __VLS_TypePropsToOption<T> = {
|
|
104
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
105
|
+
type: import('vue').PropType<Required<T>[K]>;
|
|
106
|
+
} : {
|
|
107
|
+
type: import('vue').PropType<T[K]>;
|
|
108
|
+
required: true;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
type __VLS_WithDefaults<P, D> = {
|
|
112
|
+
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_PrettifyLocal<P[K] & {
|
|
113
|
+
default: D[K];
|
|
114
|
+
}> : P[K];
|
|
115
|
+
};
|
|
116
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
117
|
+
new (): {
|
|
118
|
+
$slots: S;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
122
|
+
[K in keyof T]: T[K];
|
|
123
|
+
} : {
|
|
124
|
+
[K in keyof T as K]: T[K];
|
|
125
|
+
}) & {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Состояния камеры и разбор отказов.
|
|
3
|
+
*
|
|
4
|
+
* Вынесено из компонента, потому что именно здесь живёт вся неочевидность:
|
|
5
|
+
* `getUserMedia` отвечает `DOMException` с полудюжиной имён, и от того, в какое
|
|
6
|
+
* из них попал отказ, зависит текст, который увидит пользователь. Проверить это
|
|
7
|
+
* рендером нельзя — камеры у тестового окружения нет.
|
|
8
|
+
*/
|
|
9
|
+
export type GrCameraStatus = 'idle'
|
|
10
|
+
/** Разрешение запрошено, ответа нет. */
|
|
11
|
+
| 'starting'
|
|
12
|
+
/** Поток идёт. */
|
|
13
|
+
| 'live'
|
|
14
|
+
/** Пользователь отказал или запретила политика. */
|
|
15
|
+
| 'denied'
|
|
16
|
+
/** Камеры в системе нет. */
|
|
17
|
+
| 'missing'
|
|
18
|
+
/** Устройство занято другим приложением. */
|
|
19
|
+
| 'busy'
|
|
20
|
+
/** Страница открыта не по HTTPS: API просто отсутствует. */
|
|
21
|
+
| 'insecure' | 'error';
|
|
22
|
+
/**
|
|
23
|
+
* Имя `DOMException` → состояние.
|
|
24
|
+
*
|
|
25
|
+
* Названия заданы спецификацией, но разные браузеры выбирают из них по-разному
|
|
26
|
+
* в одной и той же ситуации: отказ пользователя — `NotAllowedError` везде, а
|
|
27
|
+
* вот занятое устройство Safari называет `NotReadableError`, Firefox —
|
|
28
|
+
* `AbortError`. Поэтому разбор по имени, а не по сообщению: сообщение
|
|
29
|
+
* локализовано браузером и меняется между версиями.
|
|
30
|
+
*/
|
|
31
|
+
export declare function cameraStatusFromError(error: unknown): GrCameraStatus;
|
|
32
|
+
/**
|
|
33
|
+
* Доступен ли API вовсе.
|
|
34
|
+
*
|
|
35
|
+
* На `http://` (кроме localhost) `navigator.mediaDevices` не существует —
|
|
36
|
+
* не «отказ», а отсутствие. Сообщение «разрешите доступ к камере» в этом случае
|
|
37
|
+
* отправляет пользователя искать настройку, которой нет.
|
|
38
|
+
*/
|
|
39
|
+
export declare function cameraSupported(navigatorLike: Pick<Navigator, 'mediaDevices'> | undefined): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Зеркалить ли превью.
|
|
42
|
+
*
|
|
43
|
+
* Фронтальную камеру зеркалят всегда: человек видит себя как в зеркале, и
|
|
44
|
+
* незеркальное превью читается как чужое лицо. Тыловую — никогда: там в кадре
|
|
45
|
+
* мир, а не ты.
|
|
46
|
+
*
|
|
47
|
+
* **Снимок при этом не зеркалится.** Иначе текст на визитке или в документе
|
|
48
|
+
* уедет в зазеркалье — а снимают ими ровно это.
|
|
49
|
+
*/
|
|
50
|
+
export declare function shouldMirrorPreview(facing: 'user' | 'environment', mirror: boolean | undefined): boolean;
|
|
51
|
+
export interface GrCameraFrameRect {
|
|
52
|
+
sx: number;
|
|
53
|
+
sy: number;
|
|
54
|
+
sw: number;
|
|
55
|
+
sh: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Какая часть кадра камеры попадёт в снимок при заданном соотношении сторон.
|
|
59
|
+
*
|
|
60
|
+
* Камера отдаёт своё соотношение (обычно 4:3 или 16:9), а приложению нужно
|
|
61
|
+
* своё. Лишнее срезается по центру — так же, как это делает `object-fit: cover`
|
|
62
|
+
* в превью: снимок обязан совпасть с тем, что пользователь видел.
|
|
63
|
+
*/
|
|
64
|
+
export declare function cameraFrameRect(frame: {
|
|
65
|
+
width: number;
|
|
66
|
+
height: number;
|
|
67
|
+
}, aspectRatio: number): GrCameraFrameRect;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Кнопки состояния и съёмки — `GrButton` ядра, и ребро объявлено: пресет
|
|
3
|
+
* подмешивает safelist и CSS только тем компонентам, что попали в селекцию, и
|
|
4
|
+
* без графа потребитель, выбравший одну камеру, получил бы кнопки без фона.
|
|
5
|
+
*/
|
|
6
|
+
export declare const grCameraCaptureConfig: import("@feugene/unocss-preset-granular/contract").GranularComponentDescriptor<"GrCameraCapture">;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GrCameraCaptureSize } from './grCameraCaptureStyles';
|
|
2
|
+
/**
|
|
3
|
+
* Настраиваемые через `GrConfigProvider` пропы.
|
|
4
|
+
*
|
|
5
|
+
* Аугментация реестра идёт по месту объявления — через
|
|
6
|
+
* `@feugene/granularity/composables/useGrComponentConfig`, а не через реэкспорт:
|
|
7
|
+
* иначе слияние отваливается молча.
|
|
8
|
+
*/
|
|
9
|
+
export interface GrCameraCaptureConfigurableProps {
|
|
10
|
+
size?: GrCameraCaptureSize;
|
|
11
|
+
}
|
|
12
|
+
declare module '@feugene/granularity/composables/useGrComponentConfig' {
|
|
13
|
+
interface GrComponentDefaultsRegistry {
|
|
14
|
+
GrCameraCapture?: GrCameraCaptureConfigurableProps;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type GrCameraCaptureSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
2
|
+
export type GrCameraFacing = 'user' | 'environment';
|
|
3
|
+
export declare const rootClass = "grid gap-2";
|
|
4
|
+
export declare const frameClass = "relative w-full overflow-hidden rounded-[var(--gr-radius-md)] border border-[var(--gr-brd)] bg-[var(--gr-muted)]";
|
|
5
|
+
/** Превью покрывает рамку целиком: снимок берётся тем же `cover`, что и показ. */
|
|
6
|
+
export declare const videoClass = "h-full w-full object-cover";
|
|
7
|
+
export declare const mirroredClass = "scale-x-[-1]";
|
|
8
|
+
/**
|
|
9
|
+
* Подложка состояния поверх рамки. Появление плавное: отказ и ошибка приходят
|
|
10
|
+
* асинхронно, и мгновенная подмена картинки текстом читается как сбой.
|
|
11
|
+
*/
|
|
12
|
+
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)]";
|
|
13
|
+
export declare const stateTextClass = "text-[var(--gr-muted-fg)]";
|
|
14
|
+
export declare const controlsClass = "flex flex-wrap items-center gap-2";
|
|
15
|
+
export declare const sizeTextClass: {
|
|
16
|
+
readonly xs: "text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]";
|
|
17
|
+
readonly sm: "text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]";
|
|
18
|
+
readonly md: "text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)]";
|
|
19
|
+
readonly lg: "text-[length:var(--gr-control-text-lg)] leading-[var(--gr-control-leading-lg)]";
|
|
20
|
+
};
|
|
21
|
+
export declare const grCameraCaptureSafelist: string[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default } from './GrCameraCapture.vue';
|
|
2
|
+
export { default as GrCameraCapture } from './GrCameraCapture.vue';
|
|
3
|
+
export type { GrCameraFrameRect, GrCameraStatus } from './cameraState';
|
|
4
|
+
export { cameraFrameRect, cameraStatusFromError, cameraSupported, shouldMirrorPreview } from './cameraState';
|
|
5
|
+
export { grCameraCaptureConfig } from './config';
|
|
6
|
+
export type { GrCameraCaptureConfigurableProps } from './defaults';
|
|
7
|
+
export type { GrCameraCaptureEmits, GrCameraCaptureOutput, GrCameraCaptureProps } from './GrCameraCapture.vue';
|
|
8
|
+
export { grCameraCaptureSafelist } from './grCameraCaptureStyles';
|
|
9
|
+
export type { GrCameraCaptureSize, GrCameraFacing } from './grCameraCaptureStyles';
|
|
@@ -84,23 +84,23 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
84
84
|
disabled: boolean;
|
|
85
85
|
ariaLabel: undefined;
|
|
86
86
|
}>>> & Readonly<{
|
|
87
|
+
onError?: ((error: unknown) => any) | undefined;
|
|
87
88
|
"onUpdate:zoom"?: ((value: number) => any) | undefined;
|
|
88
89
|
onChange?: ((rect: GrCropRect) => any) | undefined;
|
|
89
90
|
onLoad?: ((size: {
|
|
90
91
|
width: number;
|
|
91
92
|
height: number;
|
|
92
93
|
}) => any) | undefined;
|
|
93
|
-
onError?: ((error: unknown) => any) | undefined;
|
|
94
94
|
}>, {
|
|
95
|
-
src: string | File | Blob | null;
|
|
96
95
|
aspectRatio: number;
|
|
97
|
-
shape: GrImageCropShape;
|
|
98
|
-
zoom: number;
|
|
99
|
-
maxZoom: number;
|
|
100
96
|
output: GrImageCropOutput;
|
|
101
97
|
size: GrImageCropSize;
|
|
102
98
|
disabled: boolean;
|
|
103
99
|
ariaLabel: string;
|
|
100
|
+
src: string | File | Blob | null;
|
|
101
|
+
shape: GrImageCropShape;
|
|
102
|
+
zoom: number;
|
|
103
|
+
maxZoom: number;
|
|
104
104
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
105
105
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
106
106
|
declare const _default: typeof __VLS_export;
|
|
@@ -8,6 +8,7 @@ export declare const GRANULARITY_MEDIA_PROVIDER_ID = "@feugene/granularity-media
|
|
|
8
8
|
* генератор — раскладывает компонент по четырём спискам.
|
|
9
9
|
*/
|
|
10
10
|
export declare const granularityMediaComponentConfigs: {
|
|
11
|
+
GrCameraCapture: GranularComponentDescriptor<"GrCameraCapture">;
|
|
11
12
|
GrImageCrop: GranularComponentDescriptor<"GrImageCrop">;
|
|
12
13
|
};
|
|
13
14
|
export type GranularityMediaComponentName = keyof typeof granularityMediaComponentConfigs;
|
|
@@ -6,6 +6,22 @@ export declare const grMediaMessages: {
|
|
|
6
6
|
empty: string;
|
|
7
7
|
zoom: string;
|
|
8
8
|
};
|
|
9
|
+
camera: {
|
|
10
|
+
label: string;
|
|
11
|
+
idle: string;
|
|
12
|
+
starting: string;
|
|
13
|
+
denied: string;
|
|
14
|
+
missing: string;
|
|
15
|
+
busy: string;
|
|
16
|
+
insecure: string;
|
|
17
|
+
error: string;
|
|
18
|
+
start: string;
|
|
19
|
+
retry: string;
|
|
20
|
+
capture: string;
|
|
21
|
+
stop: string;
|
|
22
|
+
live: string;
|
|
23
|
+
captured: string;
|
|
24
|
+
};
|
|
9
25
|
};
|
|
10
26
|
readonly es: {
|
|
11
27
|
imageCrop: {
|
|
@@ -14,6 +30,22 @@ export declare const grMediaMessages: {
|
|
|
14
30
|
empty: string;
|
|
15
31
|
zoom: string;
|
|
16
32
|
};
|
|
33
|
+
camera: {
|
|
34
|
+
label: string;
|
|
35
|
+
idle: string;
|
|
36
|
+
starting: string;
|
|
37
|
+
denied: string;
|
|
38
|
+
missing: string;
|
|
39
|
+
busy: string;
|
|
40
|
+
insecure: string;
|
|
41
|
+
error: string;
|
|
42
|
+
start: string;
|
|
43
|
+
retry: string;
|
|
44
|
+
capture: string;
|
|
45
|
+
stop: string;
|
|
46
|
+
live: string;
|
|
47
|
+
captured: string;
|
|
48
|
+
};
|
|
17
49
|
};
|
|
18
50
|
readonly ru: {
|
|
19
51
|
imageCrop: {
|
|
@@ -22,6 +54,22 @@ export declare const grMediaMessages: {
|
|
|
22
54
|
empty: string;
|
|
23
55
|
zoom: string;
|
|
24
56
|
};
|
|
57
|
+
camera: {
|
|
58
|
+
label: string;
|
|
59
|
+
idle: string;
|
|
60
|
+
starting: string;
|
|
61
|
+
denied: string;
|
|
62
|
+
missing: string;
|
|
63
|
+
busy: string;
|
|
64
|
+
insecure: string;
|
|
65
|
+
error: string;
|
|
66
|
+
start: string;
|
|
67
|
+
retry: string;
|
|
68
|
+
capture: string;
|
|
69
|
+
stop: string;
|
|
70
|
+
live: string;
|
|
71
|
+
captured: string;
|
|
72
|
+
};
|
|
25
73
|
};
|
|
26
74
|
};
|
|
27
75
|
export type GrMediaLocale = keyof typeof grMediaMessages;
|
|
@@ -8,6 +8,22 @@ export declare const en: {
|
|
|
8
8
|
empty: string;
|
|
9
9
|
zoom: string;
|
|
10
10
|
};
|
|
11
|
+
camera: {
|
|
12
|
+
label: string;
|
|
13
|
+
idle: string;
|
|
14
|
+
starting: string;
|
|
15
|
+
denied: string;
|
|
16
|
+
missing: string;
|
|
17
|
+
busy: string;
|
|
18
|
+
insecure: string;
|
|
19
|
+
error: string;
|
|
20
|
+
start: string;
|
|
21
|
+
retry: string;
|
|
22
|
+
capture: string;
|
|
23
|
+
stop: string;
|
|
24
|
+
live: string;
|
|
25
|
+
captured: string;
|
|
26
|
+
};
|
|
11
27
|
};
|
|
12
28
|
imageCrop: {
|
|
13
29
|
label: string;
|
|
@@ -15,6 +31,22 @@ export declare const en: {
|
|
|
15
31
|
empty: string;
|
|
16
32
|
zoom: string;
|
|
17
33
|
};
|
|
34
|
+
camera: {
|
|
35
|
+
label: string;
|
|
36
|
+
idle: string;
|
|
37
|
+
starting: string;
|
|
38
|
+
denied: string;
|
|
39
|
+
missing: string;
|
|
40
|
+
busy: string;
|
|
41
|
+
insecure: string;
|
|
42
|
+
error: string;
|
|
43
|
+
start: string;
|
|
44
|
+
retry: string;
|
|
45
|
+
capture: string;
|
|
46
|
+
stop: string;
|
|
47
|
+
live: string;
|
|
48
|
+
captured: string;
|
|
49
|
+
};
|
|
18
50
|
}>;
|
|
19
51
|
};
|
|
20
52
|
};
|
|
@@ -8,6 +8,22 @@ export declare const es: {
|
|
|
8
8
|
empty: string;
|
|
9
9
|
zoom: string;
|
|
10
10
|
};
|
|
11
|
+
camera: {
|
|
12
|
+
label: string;
|
|
13
|
+
idle: string;
|
|
14
|
+
starting: string;
|
|
15
|
+
denied: string;
|
|
16
|
+
missing: string;
|
|
17
|
+
busy: string;
|
|
18
|
+
insecure: string;
|
|
19
|
+
error: string;
|
|
20
|
+
start: string;
|
|
21
|
+
retry: string;
|
|
22
|
+
capture: string;
|
|
23
|
+
stop: string;
|
|
24
|
+
live: string;
|
|
25
|
+
captured: string;
|
|
26
|
+
};
|
|
11
27
|
};
|
|
12
28
|
imageCrop: {
|
|
13
29
|
label: string;
|
|
@@ -15,6 +31,22 @@ export declare const es: {
|
|
|
15
31
|
empty: string;
|
|
16
32
|
zoom: string;
|
|
17
33
|
};
|
|
34
|
+
camera: {
|
|
35
|
+
label: string;
|
|
36
|
+
idle: string;
|
|
37
|
+
starting: string;
|
|
38
|
+
denied: string;
|
|
39
|
+
missing: string;
|
|
40
|
+
busy: string;
|
|
41
|
+
insecure: string;
|
|
42
|
+
error: string;
|
|
43
|
+
start: string;
|
|
44
|
+
retry: string;
|
|
45
|
+
capture: string;
|
|
46
|
+
stop: string;
|
|
47
|
+
live: string;
|
|
48
|
+
captured: string;
|
|
49
|
+
};
|
|
18
50
|
}>;
|
|
19
51
|
};
|
|
20
52
|
};
|
|
@@ -8,6 +8,22 @@ export declare const ru: {
|
|
|
8
8
|
empty: string;
|
|
9
9
|
zoom: string;
|
|
10
10
|
};
|
|
11
|
+
camera: {
|
|
12
|
+
label: string;
|
|
13
|
+
idle: string;
|
|
14
|
+
starting: string;
|
|
15
|
+
denied: string;
|
|
16
|
+
missing: string;
|
|
17
|
+
busy: string;
|
|
18
|
+
insecure: string;
|
|
19
|
+
error: string;
|
|
20
|
+
start: string;
|
|
21
|
+
retry: string;
|
|
22
|
+
capture: string;
|
|
23
|
+
stop: string;
|
|
24
|
+
live: string;
|
|
25
|
+
captured: string;
|
|
26
|
+
};
|
|
11
27
|
};
|
|
12
28
|
imageCrop: {
|
|
13
29
|
label: string;
|
|
@@ -15,6 +31,22 @@ export declare const ru: {
|
|
|
15
31
|
empty: string;
|
|
16
32
|
zoom: string;
|
|
17
33
|
};
|
|
34
|
+
camera: {
|
|
35
|
+
label: string;
|
|
36
|
+
idle: string;
|
|
37
|
+
starting: string;
|
|
38
|
+
denied: string;
|
|
39
|
+
missing: string;
|
|
40
|
+
busy: string;
|
|
41
|
+
insecure: string;
|
|
42
|
+
error: string;
|
|
43
|
+
start: string;
|
|
44
|
+
retry: string;
|
|
45
|
+
capture: string;
|
|
46
|
+
stop: string;
|
|
47
|
+
live: string;
|
|
48
|
+
captured: string;
|
|
49
|
+
};
|
|
18
50
|
}>;
|
|
19
51
|
};
|
|
20
52
|
};
|
package/dist/types/index.d.ts
CHANGED
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.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Evgeniy Fureev",
|
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
"types": "./dist/types/index.d.ts",
|
|
48
48
|
"import": "./dist/index.js"
|
|
49
49
|
},
|
|
50
|
+
"./components/GrCameraCapture": {
|
|
51
|
+
"types": "./dist/types/components/GrCameraCapture/index.d.ts",
|
|
52
|
+
"import": "./dist/components/GrCameraCapture/index.js"
|
|
53
|
+
},
|
|
50
54
|
"./components/GrImageCrop": {
|
|
51
55
|
"types": "./dist/types/components/GrImageCrop/index.d.ts",
|
|
52
56
|
"import": "./dist/components/GrImageCrop/index.js"
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
-
//#region src/i18n/locales/en.json
|
|
3
|
-
var t = /* @__PURE__ */ e({
|
|
4
|
-
default: () => r,
|
|
5
|
-
imageCrop: () => n
|
|
6
|
-
}), n = {
|
|
7
|
-
label: "Image crop",
|
|
8
|
-
frame: "Frame: arrow keys move the image, plus and minus zoom",
|
|
9
|
-
empty: "No image selected",
|
|
10
|
-
zoom: "Zoom"
|
|
11
|
-
}, r = { imageCrop: n };
|
|
12
|
-
//#endregion
|
|
13
|
-
export { t as n, r as t };
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
-
//#region src/i18n/locales/es.json
|
|
3
|
-
var t = /* @__PURE__ */ e({
|
|
4
|
-
default: () => r,
|
|
5
|
-
imageCrop: () => n
|
|
6
|
-
}), n = {
|
|
7
|
-
label: "Recorte de imagen",
|
|
8
|
-
frame: "Marco: las flechas mueven la imagen, más y menos ajustan el zoom",
|
|
9
|
-
empty: "Ninguna imagen seleccionada",
|
|
10
|
-
zoom: "Zoom"
|
|
11
|
-
}, r = { imageCrop: n };
|
|
12
|
-
//#endregion
|
|
13
|
-
export { t as n, r as t };
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
-
//#region src/i18n/locales/ru.json
|
|
3
|
-
var t = /* @__PURE__ */ e({
|
|
4
|
-
default: () => r,
|
|
5
|
-
imageCrop: () => n
|
|
6
|
-
}), n = {
|
|
7
|
-
label: "Кадрирование изображения",
|
|
8
|
-
frame: "Кадр: стрелки двигают изображение, плюс и минус меняют увеличение",
|
|
9
|
-
empty: "Изображение не выбрано",
|
|
10
|
-
zoom: "Увеличение"
|
|
11
|
-
}, r = { imageCrop: n };
|
|
12
|
-
//#endregion
|
|
13
|
-
export { t as n, r as t };
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
//#region src/i18n/messages/const.ts
|
|
2
|
-
var e = "grMedia", t = { en: { [e]: async () => import("./en-BrjpU7lq.js").then((e) => e.n) } }, n = { es: { [e]: async () => import("./es-BdTkuOLs.js").then((e) => e.n) } }, r = { ru: { [e]: async () => import("./ru-ByR24Aqk.js").then((e) => e.n) } };
|
|
3
|
-
//#endregion
|
|
4
|
-
export { e as i, n, t as r, r as t };
|