@feugene/granularity 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{en-BMjJZttK.js → en-QNZhjmSJ.js} +18 -1
- package/dist/chunks/en-QNZhjmSJ.js.map +1 -0
- package/dist/chunks/{es-EYCf8G_1.js → es-C1c5XsIK.js} +18 -1
- package/dist/chunks/es-C1c5XsIK.js.map +1 -0
- package/dist/chunks/{es-Cu_RlgIr.js → es-CKLPuE0h.js} +3 -3
- package/dist/chunks/{es-Cu_RlgIr.js.map → es-CKLPuE0h.js.map} +1 -1
- package/dist/chunks/granularityI18n-CXa_uN7f.js +46 -0
- package/dist/chunks/granularityI18n-CXa_uN7f.js.map +1 -0
- package/dist/chunks/{ru-DKswMSdd.js → ru-DcF7ciz0.js} +18 -1
- package/dist/chunks/ru-DcF7ciz0.js.map +1 -0
- package/dist/chunks/{shared-DO3Sr5FJ.js → shared-BkIIEolF.js} +7 -0
- package/dist/chunks/shared-BkIIEolF.js.map +1 -0
- package/dist/components/GrNavbar/chunks/{GrNavbar-BDIKAYfN.js → GrNavbar-BoVuSZ-V.js} +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-BDIKAYfN.js.map → GrNavbar-BoVuSZ-V.js.map} +1 -1
- package/dist/components/GrNavbar/index.js +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-BZlH-uEd.js → GrPagination-DW44c2-a.js} +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-BZlH-uEd.js.map → GrPagination-DW44c2-a.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-n0CZT4Ic.js → GrTreeSelect-DkAIksRW.js} +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-n0CZT4Ic.js.map → GrTreeSelect-DkAIksRW.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/i18n/all.js +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/index.js +977 -4
- package/dist/index.js.map +1 -1
- package/dist/types/src/components/GrResponseErrorBanner/GrFormErrorBanner.vue.d.ts +70 -0
- package/dist/types/src/components/GrResponseErrorBanner/GrResponseErrorBanner.vue.d.ts +96 -0
- package/dist/types/src/components/GrResponseErrorBanner/GrUploadErrorBanner.vue.d.ts +73 -0
- package/dist/types/src/components/GrResponseErrorBanner/config.d.ts +1 -0
- package/dist/types/src/components/GrResponseErrorBanner/index.d.ts +10 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/abortErrorParser.d.ts +15 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/createResponseErrorClassifier.d.ts +28 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/fileValidationParser.d.ts +12 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/httpStatusParser.d.ts +15 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/index.d.ts +11 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/jsonApiErrorParser.d.ts +17 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/laravelValidationParser.d.ts +15 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/networkErrorParser.d.ts +14 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/normalizeError.d.ts +19 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/plainMessageParser.d.ts +15 -0
- package/dist/types/src/components/GrResponseErrorBanner/parsers/problemDetailsParser.d.ts +17 -0
- package/dist/types/src/components/GrResponseErrorBanner/responseError.defaults.d.ts +18 -0
- package/dist/types/src/components/GrResponseErrorBanner/responseError.types.d.ts +115 -0
- package/dist/types/src/components/GrResponseErrorBanner/useResponseError.d.ts +62 -0
- package/dist/types/src/components/GrToaster/GrToaster.vue.d.ts +1 -1
- package/dist/types/src/granular-provider/shared.d.ts +1 -0
- package/dist/types/src/i18n/messages/en.d.ts +32 -0
- package/dist/types/src/i18n/messages/es.d.ts +32 -0
- package/dist/types/src/i18n/messages/ru.d.ts +32 -0
- package/dist/types/src/index.d.ts +1 -0
- package/package.json +1 -1
- package/dist/chunks/en-BMjJZttK.js.map +0 -1
- package/dist/chunks/es-EYCf8G_1.js.map +0 -1
- package/dist/chunks/granularityI18n-Dm4DuCS9.js +0 -26
- package/dist/chunks/granularityI18n-Dm4DuCS9.js.map +0 -1
- package/dist/chunks/ru-DKswMSdd.js.map +0 -1
- package/dist/chunks/shared-DO3Sr5FJ.js.map +0 -1
|
@@ -25,6 +25,22 @@ var pagination = {
|
|
|
25
25
|
"next": "Next"
|
|
26
26
|
};
|
|
27
27
|
var navbar = { "openMenu": "Open menu" };
|
|
28
|
+
var responseError = {
|
|
29
|
+
"networkTitle": "No connection to the server",
|
|
30
|
+
"networkMessage": "Check your internet connection and try again.",
|
|
31
|
+
"abortedTitle": "Request cancelled",
|
|
32
|
+
"abortedMessage": "The request was cancelled.",
|
|
33
|
+
"validationTitle": "Check the data",
|
|
34
|
+
"validationMessage": "The server returned a validation error. Fix the fields and try again.",
|
|
35
|
+
"clientTitle": "Request error",
|
|
36
|
+
"clientMessage": "The server rejected the request.",
|
|
37
|
+
"serverTitle": "Server error",
|
|
38
|
+
"serverMessage": "A server error occurred. Please try again.",
|
|
39
|
+
"unknownTitle": "Something went wrong",
|
|
40
|
+
"unknownMessage": "The operation could not be completed. Please try again.",
|
|
41
|
+
"retryLabel": "Retry",
|
|
42
|
+
"dismissLabel": "Dismiss"
|
|
43
|
+
};
|
|
28
44
|
var colors = {
|
|
29
45
|
"neutral": "Neutral",
|
|
30
46
|
"success": "Success",
|
|
@@ -42,7 +58,8 @@ var en_default = {
|
|
|
42
58
|
dialog,
|
|
43
59
|
pagination,
|
|
44
60
|
navbar,
|
|
61
|
+
responseError,
|
|
45
62
|
colors
|
|
46
63
|
};
|
|
47
64
|
//#endregion
|
|
48
|
-
export { colors, common, en_default as default, dialog, loading, navbar, pagination, treeSelect };
|
|
65
|
+
export { colors, common, en_default as default, dialog, loading, navbar, pagination, responseError, treeSelect };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"en-QNZhjmSJ.js","names":[],"sources":["../../src/i18n/locales/en.json"],"sourcesContent":["{\n \"common\": {\n \"confirm\": \"Confirm\",\n \"cancel\": \"Cancel\",\n \"add\": \"Add\",\n \"clear\": \"Clear\",\n \"close\": \"Close\"\n },\n \"loading\": {\n \"defaultText\": \"Loading...\"\n },\n \"treeSelect\": {\n \"filterPlaceholder\": \"Search…\",\n \"empty\": \"No data\"\n },\n \"dialog\": {\n \"confirm\": {\n \"title\": \"Confirm\"\n },\n \"prompt\": {\n \"title\": \"Prompt\",\n \"label\": \"Value\",\n \"required\": \"Enter a value.\"\n }\n },\n \"pagination\": {\n \"pageSize\": \"Page size\",\n \"prev\": \"Prev\",\n \"next\": \"Next\"\n },\n \"navbar\": {\n \"openMenu\": \"Open menu\"\n },\n \"responseError\": {\n \"networkTitle\": \"No connection to the server\",\n \"networkMessage\": \"Check your internet connection and try again.\",\n \"abortedTitle\": \"Request cancelled\",\n \"abortedMessage\": \"The request was cancelled.\",\n \"validationTitle\": \"Check the data\",\n \"validationMessage\": \"The server returned a validation error. Fix the fields and try again.\",\n \"clientTitle\": \"Request error\",\n \"clientMessage\": \"The server rejected the request.\",\n \"serverTitle\": \"Server error\",\n \"serverMessage\": \"A server error occurred. Please try again.\",\n \"unknownTitle\": \"Something went wrong\",\n \"unknownMessage\": \"The operation could not be completed. Please try again.\",\n \"retryLabel\": \"Retry\",\n \"dismissLabel\": \"Dismiss\"\n },\n \"colors\": {\n \"neutral\": \"Neutral\",\n \"success\": \"Success\",\n \"warning\": \"Warning\",\n \"danger\": \"Danger\",\n \"info\": \"Info\",\n \"primary\": \"Primary\",\n \"slate\": \"Slate\",\n \"azure\": \"Azure\"\n }\n}\n"],"mappings":""}
|
|
@@ -25,6 +25,22 @@ var pagination = {
|
|
|
25
25
|
"next": "Siguiente"
|
|
26
26
|
};
|
|
27
27
|
var navbar = { "openMenu": "Abrir menú" };
|
|
28
|
+
var responseError = {
|
|
29
|
+
"networkTitle": "Sin conexión con el servidor",
|
|
30
|
+
"networkMessage": "Compruebe su conexión a internet e inténtelo de nuevo.",
|
|
31
|
+
"abortedTitle": "Solicitud cancelada",
|
|
32
|
+
"abortedMessage": "La solicitud fue cancelada.",
|
|
33
|
+
"validationTitle": "Compruebe los datos",
|
|
34
|
+
"validationMessage": "El servidor devolvió un error de validación. Corrija los campos e inténtelo de nuevo.",
|
|
35
|
+
"clientTitle": "Error de solicitud",
|
|
36
|
+
"clientMessage": "El servidor rechazó la solicitud.",
|
|
37
|
+
"serverTitle": "Error del servidor",
|
|
38
|
+
"serverMessage": "Se produjo un error en el servidor. Inténtelo de nuevo.",
|
|
39
|
+
"unknownTitle": "Algo salió mal",
|
|
40
|
+
"unknownMessage": "No se pudo completar la operación. Inténtelo de nuevo.",
|
|
41
|
+
"retryLabel": "Reintentar",
|
|
42
|
+
"dismissLabel": "Descartar"
|
|
43
|
+
};
|
|
28
44
|
var colors = {
|
|
29
45
|
"neutral": "Neutral",
|
|
30
46
|
"success": "Éxito",
|
|
@@ -42,7 +58,8 @@ var es_default = {
|
|
|
42
58
|
dialog,
|
|
43
59
|
pagination,
|
|
44
60
|
navbar,
|
|
61
|
+
responseError,
|
|
45
62
|
colors
|
|
46
63
|
};
|
|
47
64
|
//#endregion
|
|
48
|
-
export { colors, common, es_default as default, dialog, loading, navbar, pagination, treeSelect };
|
|
65
|
+
export { colors, common, es_default as default, dialog, loading, navbar, pagination, responseError, treeSelect };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"es-C1c5XsIK.js","names":[],"sources":["../../src/i18n/locales/es.json"],"sourcesContent":["{\n \"common\": {\n \"confirm\": \"Confirmar\",\n \"cancel\": \"Cancelar\",\n \"add\": \"Añadir\",\n \"clear\": \"Limpiar\",\n \"close\": \"Cerrar\"\n },\n \"loading\": {\n \"defaultText\": \"Cargando...\"\n },\n \"treeSelect\": {\n \"filterPlaceholder\": \"Buscar…\",\n \"empty\": \"Sin datos\"\n },\n \"dialog\": {\n \"confirm\": {\n \"title\": \"Confirmar\"\n },\n \"prompt\": {\n \"title\": \"Aviso\",\n \"label\": \"Valor\",\n \"required\": \"Introduzca un valor.\"\n }\n },\n \"pagination\": {\n \"pageSize\": \"Tamaño de página\",\n \"prev\": \"Anterior\",\n \"next\": \"Siguiente\"\n },\n \"navbar\": {\n \"openMenu\": \"Abrir menú\"\n },\n \"responseError\": {\n \"networkTitle\": \"Sin conexión con el servidor\",\n \"networkMessage\": \"Compruebe su conexión a internet e inténtelo de nuevo.\",\n \"abortedTitle\": \"Solicitud cancelada\",\n \"abortedMessage\": \"La solicitud fue cancelada.\",\n \"validationTitle\": \"Compruebe los datos\",\n \"validationMessage\": \"El servidor devolvió un error de validación. Corrija los campos e inténtelo de nuevo.\",\n \"clientTitle\": \"Error de solicitud\",\n \"clientMessage\": \"El servidor rechazó la solicitud.\",\n \"serverTitle\": \"Error del servidor\",\n \"serverMessage\": \"Se produjo un error en el servidor. Inténtelo de nuevo.\",\n \"unknownTitle\": \"Algo salió mal\",\n \"unknownMessage\": \"No se pudo completar la operación. Inténtelo de nuevo.\",\n \"retryLabel\": \"Reintentar\",\n \"dismissLabel\": \"Descartar\"\n },\n \"colors\": {\n \"neutral\": \"Neutral\",\n \"success\": \"Éxito\",\n \"warning\": \"Advertencia\",\n \"danger\": \"Peligro\",\n \"info\": \"Info\",\n \"primary\": \"Primario\",\n \"slate\": \"Pizarra\",\n \"azure\": \"Azul celeste\"\n }\n}\n"],"mappings":""}
|
|
@@ -9,12 +9,12 @@ var GRANULARITY_I18N_BLOCK = "gr";
|
|
|
9
9
|
* can import only the languages they actually need — see the
|
|
10
10
|
* "Authoring localization packages" guide in the docs.
|
|
11
11
|
*/
|
|
12
|
-
var en = { en: { ["gr"]: () => import("./en-
|
|
12
|
+
var en = { en: { ["gr"]: () => import("./en-QNZhjmSJ.js") } };
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/i18n/messages/ru.ts
|
|
15
|
-
var ru = { ru: { ["gr"]: () => import("./ru-
|
|
15
|
+
var ru = { ru: { ["gr"]: () => import("./ru-DcF7ciz0.js") } };
|
|
16
16
|
//#endregion
|
|
17
17
|
//#region src/i18n/messages/es.ts
|
|
18
|
-
var es = { es: { ["gr"]: () => import("./es-
|
|
18
|
+
var es = { es: { ["gr"]: () => import("./es-C1c5XsIK.js") } };
|
|
19
19
|
//#endregion
|
|
20
20
|
export { GRANULARITY_I18N_BLOCK as i, ru as n, en as r, es as t };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"es-
|
|
1
|
+
{"version":3,"file":"es-CKLPuE0h.js","names":[],"sources":["../../src/i18n/messages/const.ts","../../src/i18n/messages/en.ts","../../src/i18n/messages/ru.ts","../../src/i18n/messages/es.ts"],"sourcesContent":["export const GRANULARITY_I18N_BLOCK = 'gr'","import type {LocaleLoaderCollection} from '@feugene/fint-i18n/core'\nimport {GRANULARITY_I18N_BLOCK} from \"./const\";\n\n/**\n * English locale loaders for the playground app.\n *\n * Exported separately from other locales so that consumers (and bundlers)\n * can import only the languages they actually need — see the\n * \"Authoring localization packages\" guide in the docs.\n */\nexport const en = {\n en: {\n [GRANULARITY_I18N_BLOCK]: () => import('../locales/en.json'),\n },\n} satisfies LocaleLoaderCollection\n\nexport default en\n","import type {LocaleLoaderCollection} from '@feugene/fint-i18n/core'\nimport {GRANULARITY_I18N_BLOCK} from \"./const\";\n\nexport const ru = {\n ru: {\n [GRANULARITY_I18N_BLOCK]: () => import('../locales/ru.json'),\n },\n} satisfies LocaleLoaderCollection\n\nexport default ru\n","import type {LocaleLoaderCollection} from '@feugene/fint-i18n/core'\nimport {GRANULARITY_I18N_BLOCK} from \"./const\";\n\nexport const es = {\n es: {\n [GRANULARITY_I18N_BLOCK]: () => import('../locales/es.json'),\n },\n} satisfies LocaleLoaderCollection\n\nexport default es\n"],"mappings":";AAAA,IAAa,yBAAyB;;;;;;;;;;ACUtC,IAAa,KAAK,EACd,IAAI,GAAA,aACgC,OAAO,qBAC1C,EACJ;;;ACXD,IAAa,KAAK,EACd,IAAI,GAAA,aACgC,OAAO,qBAC1C,EACJ;;;ACJD,IAAa,KAAK,EACd,IAAI,GAAA,aACgC,OAAO,qBAC1C,EACJ"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { t as GRANULARITY_I18N_KEY } from "./adapter-CRyH4YC8.js";
|
|
2
|
+
import { getCurrentInstance, inject } from "vue";
|
|
3
|
+
//#region src/internal/granularityI18n.ts
|
|
4
|
+
/**
|
|
5
|
+
* Ключ provide/inject из `@feugene/fint-i18n` (`Symbol.for("FintI18n")`).
|
|
6
|
+
*
|
|
7
|
+
* Дублируем константу здесь, чтобы не делать `@feugene/fint-i18n` обязательной
|
|
8
|
+
* runtime-зависимостью пакета: достаточно, чтобы приложение само установило
|
|
9
|
+
* fint-i18n через `installI18n(app, i18n)` — символ глобальный (Symbol.for),
|
|
10
|
+
* поэтому ключи будут совпадать без прямого импорта.
|
|
11
|
+
*/
|
|
12
|
+
var FINT_I18N_KEY = Symbol.for("FintI18n");
|
|
13
|
+
function isGranularityI18nLike(value) {
|
|
14
|
+
return typeof value?.t === "function";
|
|
15
|
+
}
|
|
16
|
+
function resolveGranularityI18n(context) {
|
|
17
|
+
if (isGranularityI18nLike(context)) return context;
|
|
18
|
+
const provides = context?.provides;
|
|
19
|
+
if (provides) {
|
|
20
|
+
const fromGranularity = provides[GRANULARITY_I18N_KEY];
|
|
21
|
+
if (isGranularityI18nLike(fromGranularity)) return fromGranularity;
|
|
22
|
+
const fromFint = provides[FINT_I18N_KEY];
|
|
23
|
+
if (isGranularityI18nLike(fromFint)) return fromFint;
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (!getCurrentInstance()) return null;
|
|
27
|
+
const fromGranularity = inject(GRANULARITY_I18N_KEY, null);
|
|
28
|
+
if (isGranularityI18nLike(fromGranularity)) return fromGranularity;
|
|
29
|
+
const fromFint = inject(FINT_I18N_KEY, null);
|
|
30
|
+
if (isGranularityI18nLike(fromFint)) return fromFint;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function useGranularityTranslations(context) {
|
|
34
|
+
const i18n = resolveGranularityI18n(context);
|
|
35
|
+
const t = (key, fallback, params) => {
|
|
36
|
+
if (!i18n) return fallback;
|
|
37
|
+
const result = i18n.t(key, params);
|
|
38
|
+
return result === key ? fallback : result;
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
i18n,
|
|
42
|
+
t
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { useGranularityTranslations as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"granularityI18n-CXa_uN7f.js","names":[],"sources":["../../src/internal/granularityI18n.ts"],"sourcesContent":["import { getCurrentInstance, inject, type AppContext } from 'vue'\n\nimport { GRANULARITY_I18N_KEY, type GranularityI18nAdapter } from '../i18n/adapter'\n\nexport type GranularityI18nLike = GranularityI18nAdapter\n\n/**\n * Ключ provide/inject из `@feugene/fint-i18n` (`Symbol.for(\"FintI18n\")`).\n *\n * Дублируем константу здесь, чтобы не делать `@feugene/fint-i18n` обязательной\n * runtime-зависимостью пакета: достаточно, чтобы приложение само установило\n * fint-i18n через `installI18n(app, i18n)` — символ глобальный (Symbol.for),\n * поэтому ключи будут совпадать без прямого импорта.\n */\nconst FINT_I18N_KEY: symbol = Symbol.for('FintI18n')\n\nfunction isGranularityI18nLike(value: unknown): value is GranularityI18nLike {\n return typeof (value as GranularityI18nLike | null)?.t === 'function'\n}\n\nexport function resolveGranularityI18n(context?: AppContext | GranularityI18nLike | null): GranularityI18nLike | null {\n if (isGranularityI18nLike(context)) {\n return context\n }\n\n const provides = context?.provides\n\n if (provides) {\n const fromGranularity = provides[GRANULARITY_I18N_KEY as symbol] as GranularityI18nLike | null | undefined\n if (isGranularityI18nLike(fromGranularity)) {\n return fromGranularity\n }\n\n const fromFint = provides[FINT_I18N_KEY] as GranularityI18nLike | null | undefined\n if (isGranularityI18nLike(fromFint)) {\n return fromFint\n }\n\n return null\n }\n\n if (!getCurrentInstance()) {\n return null\n }\n\n const fromGranularity = inject<GranularityI18nLike | null>(GRANULARITY_I18N_KEY, null)\n if (isGranularityI18nLike(fromGranularity)) {\n return fromGranularity\n }\n\n const fromFint = inject<GranularityI18nLike | null>(FINT_I18N_KEY, null)\n if (isGranularityI18nLike(fromFint)) {\n return fromFint\n }\n\n return null\n}\n\nexport function useGranularityTranslations(context?: AppContext | GranularityI18nLike | null) {\n const i18n = resolveGranularityI18n(context)\n\n const t = (key: string, fallback: string, params?: Record<string, any>): string => {\n if (!i18n) {\n return fallback\n }\n\n const result = i18n.t(key, params)\n return result === key ? fallback : result\n }\n\n return {\n i18n,\n t,\n }\n}"],"mappings":";;;;;;;;;;;AAcA,IAAM,gBAAwB,OAAO,IAAI,WAAW;AAEpD,SAAS,sBAAsB,OAA8C;AAC3E,QAAO,OAAQ,OAAsC,MAAM;;AAG7D,SAAgB,uBAAuB,SAA+E;AACpH,KAAI,sBAAsB,QAAQ,CAChC,QAAO;CAGT,MAAM,WAAW,SAAS;AAE1B,KAAI,UAAU;EACZ,MAAM,kBAAkB,SAAS;AACjC,MAAI,sBAAsB,gBAAgB,CACxC,QAAO;EAGT,MAAM,WAAW,SAAS;AAC1B,MAAI,sBAAsB,SAAS,CACjC,QAAO;AAGT,SAAO;;AAGT,KAAI,CAAC,oBAAoB,CACvB,QAAO;CAGT,MAAM,kBAAkB,OAAmC,sBAAsB,KAAK;AACtF,KAAI,sBAAsB,gBAAgB,CACxC,QAAO;CAGT,MAAM,WAAW,OAAmC,eAAe,KAAK;AACxE,KAAI,sBAAsB,SAAS,CACjC,QAAO;AAGT,QAAO;;AAGT,SAAgB,2BAA2B,SAAmD;CAC5F,MAAM,OAAO,uBAAuB,QAAQ;CAE5C,MAAM,KAAK,KAAa,UAAkB,WAAyC;AACjF,MAAI,CAAC,KACH,QAAO;EAGT,MAAM,SAAS,KAAK,EAAE,KAAK,OAAO;AAClC,SAAO,WAAW,MAAM,WAAW;;AAGrC,QAAO;EACL;EACA;EACD"}
|
|
@@ -25,6 +25,22 @@ var pagination = {
|
|
|
25
25
|
"next": "Вперёд"
|
|
26
26
|
};
|
|
27
27
|
var navbar = { "openMenu": "Открыть меню" };
|
|
28
|
+
var responseError = {
|
|
29
|
+
"networkTitle": "Нет связи с сервером",
|
|
30
|
+
"networkMessage": "Проверьте подключение к интернету и попробуйте ещё раз.",
|
|
31
|
+
"abortedTitle": "Запрос отменён",
|
|
32
|
+
"abortedMessage": "Запрос был отменён.",
|
|
33
|
+
"validationTitle": "Проверьте данные",
|
|
34
|
+
"validationMessage": "Сервер вернул ошибку валидации. Исправьте поля и попробуйте снова.",
|
|
35
|
+
"clientTitle": "Ошибка запроса",
|
|
36
|
+
"clientMessage": "Сервер отклонил запрос.",
|
|
37
|
+
"serverTitle": "Ошибка сервера",
|
|
38
|
+
"serverMessage": "Произошла ошибка на сервере. Попробуйте ещё раз.",
|
|
39
|
+
"unknownTitle": "Что-то пошло не так",
|
|
40
|
+
"unknownMessage": "Не удалось выполнить операцию. Попробуйте ещё раз.",
|
|
41
|
+
"retryLabel": "Повторить",
|
|
42
|
+
"dismissLabel": "Скрыть"
|
|
43
|
+
};
|
|
28
44
|
var colors = {
|
|
29
45
|
"neutral": "Нейтральный",
|
|
30
46
|
"success": "Успех",
|
|
@@ -42,7 +58,8 @@ var ru_default = {
|
|
|
42
58
|
dialog,
|
|
43
59
|
pagination,
|
|
44
60
|
navbar,
|
|
61
|
+
responseError,
|
|
45
62
|
colors
|
|
46
63
|
};
|
|
47
64
|
//#endregion
|
|
48
|
-
export { colors, common, ru_default as default, dialog, loading, navbar, pagination, treeSelect };
|
|
65
|
+
export { colors, common, ru_default as default, dialog, loading, navbar, pagination, responseError, treeSelect };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ru-DcF7ciz0.js","names":[],"sources":["../../src/i18n/locales/ru.json"],"sourcesContent":["{\n \"common\": {\n \"confirm\": \"Подтвердить\",\n \"cancel\": \"Отмена\",\n \"add\": \"Добавить\",\n \"clear\": \"Очистить\",\n \"close\": \"Закрыть\"\n },\n \"loading\": {\n \"defaultText\": \"Загрузка...\"\n },\n \"treeSelect\": {\n \"filterPlaceholder\": \"Поиск…\",\n \"empty\": \"Нет данных\"\n },\n \"dialog\": {\n \"confirm\": {\n \"title\": \"Подтверждение\"\n },\n \"prompt\": {\n \"title\": \"Введите значение\",\n \"label\": \"Значение\",\n \"required\": \"Введите значение.\"\n }\n },\n \"pagination\": {\n \"pageSize\": \"Размер страницы\",\n \"prev\": \"Назад\",\n \"next\": \"Вперёд\"\n },\n \"navbar\": {\n \"openMenu\": \"Открыть меню\"\n },\n \"responseError\": {\n \"networkTitle\": \"Нет связи с сервером\",\n \"networkMessage\": \"Проверьте подключение к интернету и попробуйте ещё раз.\",\n \"abortedTitle\": \"Запрос отменён\",\n \"abortedMessage\": \"Запрос был отменён.\",\n \"validationTitle\": \"Проверьте данные\",\n \"validationMessage\": \"Сервер вернул ошибку валидации. Исправьте поля и попробуйте снова.\",\n \"clientTitle\": \"Ошибка запроса\",\n \"clientMessage\": \"Сервер отклонил запрос.\",\n \"serverTitle\": \"Ошибка сервера\",\n \"serverMessage\": \"Произошла ошибка на сервере. Попробуйте ещё раз.\",\n \"unknownTitle\": \"Что-то пошло не так\",\n \"unknownMessage\": \"Не удалось выполнить операцию. Попробуйте ещё раз.\",\n \"retryLabel\": \"Повторить\",\n \"dismissLabel\": \"Скрыть\"\n },\n \"colors\": {\n \"neutral\": \"Нейтральный\",\n \"success\": \"Успех\",\n \"warning\": \"Предупреждение\",\n \"danger\": \"Опасность\",\n \"info\": \"Инфо\",\n \"primary\": \"Primary\",\n \"slate\": \"Сланцевый\",\n \"azure\": \"Лазурный\"\n }\n}\n"],"mappings":""}
|
|
@@ -54,6 +54,12 @@ var grDialogConfig = defineGranularComponent(import.meta.url, {
|
|
|
54
54
|
safelist: grDialogSafelist
|
|
55
55
|
});
|
|
56
56
|
//#endregion
|
|
57
|
+
//#region src/components/GrResponseErrorBanner/config.ts
|
|
58
|
+
var grResponseErrorBannerConfig = defineGranularComponent(import.meta.url, {
|
|
59
|
+
name: "GrResponseErrorBanner",
|
|
60
|
+
dependencies: ["GrAlert", "GrButton"]
|
|
61
|
+
});
|
|
62
|
+
//#endregion
|
|
57
63
|
//#region src/granular-provider/shared.ts
|
|
58
64
|
/** Идентификатор провайдера — совпадает с именем пакета. */
|
|
59
65
|
var GRANULARITY_PROVIDER_ID = "@feugene/granularity";
|
|
@@ -124,6 +130,7 @@ var granularityComponentConfigs = {
|
|
|
124
130
|
GrPromptDialog: grPromptDialogConfig,
|
|
125
131
|
GrRadio: grRadioConfig,
|
|
126
132
|
GrRadioGroup: grRadioGroupConfig,
|
|
133
|
+
GrResponseErrorBanner: grResponseErrorBannerConfig,
|
|
127
134
|
GrSegmented: grSegmentedConfig,
|
|
128
135
|
GrSelect: grSelectConfig,
|
|
129
136
|
GrSidebar: grSidebarConfig,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-BkIIEolF.js","names":[],"sources":["../../src/components/GrDialog/config.ts","../../src/components/GrResponseErrorBanner/config.ts","../../src/granular-provider/shared.ts"],"sourcesContent":["import {defineGranularComponent} from '@feugene/unocss-preset-granular/contract'\n\nimport { grDialogSafelist } from './safelist'\n\nexport const grDialogConfig = defineGranularComponent(import.meta.url, {\n name: 'GrDialog',\n dependencies: ['GrButton', 'GrModal'],\n safelist: grDialogSafelist,\n})\n","import { defineGranularComponent } from '@feugene/unocss-preset-granular/contract'\n\nexport const grResponseErrorBannerConfig = defineGranularComponent(import.meta.url, {\n name: 'GrResponseErrorBanner',\n dependencies: ['GrAlert', 'GrButton'],\n})\n","// Общий код browser- и node-entry granular-provider'а.\n//\n// Оба entry (`./index.ts`, `./node.ts`) отличаются только списком\n// компонентов, у которых есть node-only вариант конфига (например,\n// `GrButton`, чей `config.node.ts` использует `tokenDefinitionsFromCssSync`\n// и тянет `node:fs`). Browser-вариант таких компонентов берёт облегчённый\n// `./config.ts`, node-вариант — `./config.node.ts`. Всё остальное — `id`,\n// `theme.*`, `packageBaseUrl` и полный список browser-safe компонентов —\n// живёт здесь.\n//\n// ВАЖНО: все `new URL('../styles/...', import.meta.url)` намеренно находятся\n// здесь — `shared.ts` лежит в той же директории, что и `index.ts` / `node.ts`,\n// поэтому относительные пути идентичны. Это ОК для браузера: бандлеры\n// транслируют такие конструкции в статические asset-URL и `node:*` в бандл\n// не утаскивают.\n//\n// Ключи записи `<Name>: ds<Name>Config` в `granularityComponentConfigs`\n// критичны для regex‑парсинга в `apps/showcase/scripts/generate-component-api.mjs`,\n// не ломайте формат.\nimport {\n defineGranularProvider,\n type GranularComponentDescriptor,\n type GranularProvider,\n} from '@feugene/unocss-preset-granular/contract'\nimport { grAlertConfig } from '../components/GrAlert/config'\nimport { grAvatarConfig } from '../components/GrAvatar/config'\nimport { grBadgeConfig } from '../components/GrBadge/config'\nimport { grBadgeWrapConfig } from '../components/GrBadgeWrap/config'\nimport { grBottomNavConfig } from '../components/GrBottomNav/config'\nimport { grButtonConfig } from '../components/GrButton/config'\nimport { grButtonGroupConfig } from '../components/GrButtonGroup/config'\nimport { grCardConfig } from '../components/GrCard/config'\nimport { grCheckboxConfig } from '../components/GrCheckbox/config'\nimport { grCollapseConfig } from '../components/GrCollapse/config'\nimport { grConfirmDialogConfig } from '../components/GrConfirmDialog/config'\nimport { grDataTableConfig } from '../components/GrDataTable/config'\nimport { grDialogConfig } from '../components/GrDialog/config'\nimport { grDrawerConfig } from '../components/GrDrawer/config'\nimport { grDropdownConfig } from '../components/GrDropdown/config'\nimport { grDropdownMenuConfig } from '../components/GrDropdownMenu/config'\nimport { grEmptyStateConfig } from '../components/GrEmptyState/config'\nimport { grFileUploadConfig } from '../components/GrFileUpload/config'\nimport { grFormFieldConfig } from '../components/GrFormField/config'\nimport { grFormFileConfig } from '../components/GrFormFile/config'\nimport { grFormSectionConfig } from '../components/GrFormSection/config'\nimport { grIconConfig } from '../components/GrIcon/config'\nimport { grImageViewerConfig } from '../components/GrImageViewer/config'\nimport { grInputConfig } from '../components/GrInput/config'\nimport { grInputTagConfig } from '../components/GrInputTag/config'\nimport { grLinkConfig } from '../components/GrLink/config'\nimport { grListConfig } from '../components/GrList/config'\nimport { grLoadingConfig } from '../components/GrLoading/config'\nimport { grModalConfig } from '../components/GrModal/config'\nimport { grNavbarConfig } from '../components/GrNavbar/config'\nimport { grNumberInputConfig } from '../components/GrNumberInput/config'\nimport { grPaginationConfig } from '../components/GrPagination/config'\nimport { grProgressBarConfig } from '../components/GrProgressBar/config'\nimport { grPromptDialogConfig } from '../components/GrPromptDialog/config'\nimport { grRadioConfig } from '../components/GrRadio/config'\nimport { grRadioGroupConfig } from '../components/GrRadioGroup/config'\nimport { grResponseErrorBannerConfig } from '../components/GrResponseErrorBanner/config'\nimport { grSegmentedConfig } from '../components/GrSegmented/config'\nimport { grSelectConfig } from '../components/GrSelect/config'\nimport { grSidebarConfig } from '../components/GrSidebar/config'\nimport { grSkeletonConfig } from '../components/GrSkeleton/config'\nimport { grSwitchConfig } from '../components/GrSwitch/config'\nimport { grTableConfig } from '../components/GrTable/config'\nimport { grTabsConfig } from '../components/GrTabs/config'\nimport { grTextareaConfig } from '../components/GrTextarea/config'\nimport { grToasterConfig } from '../components/GrToaster/config'\nimport { grTooltipConfig } from '../components/GrTooltip/config'\nimport { grTreeConfig } from '../components/GrTree/config'\nimport { grTreeSelectConfig } from '../components/GrTreeSelect/config'\n\n/** Идентификатор провайдера — совпадает с именем пакета. */\nexport const GRANULARITY_PROVIDER_ID = '@feugene/granularity'\n\n// runtime-concat: литерал `new URL('..', import.meta.url)` rolldown заменяет\n// на `data:`-URL, поэтому собираем корень пакета из `import.meta.url` вручную\n// (отрезая два последних сегмента: имя файла и каталог `granular-provider/`).\nconst packageBaseUrl = `${import.meta.url.slice(\n 0,\n import.meta.url.lastIndexOf('/', import.meta.url.lastIndexOf('/') - 1) + 1,\n)}`\n\n/** Встроенные темы пакета. Единственный источник правды о списке тем. */\nexport const granularityThemeNames = ['light', 'dark'] as const\nexport type GranularityThemeName = (typeof granularityThemeNames)[number]\n\n/** Темы, активные по умолчанию, если окружение не переопределяет выбор. */\nexport const granularityDefaultThemes: readonly GranularityThemeName[] = ['light']\n\nconst theme = {\n baseCssUrl: new URL('../styles/base.css', import.meta.url).href,\n tokensCssUrl: new URL('../styles/tokens.css', import.meta.url).href,\n themes: {\n light: new URL('../styles/themes/light.css', import.meta.url).href,\n dark: new URL('../styles/themes/dark.css', import.meta.url).href,\n },\n defaultThemes: granularityDefaultThemes,\n} as const\n\n/**\n * Полный реестр публичных компонентов пакета `@feugene/granularity`.\n *\n * Ключи — имена компонентов (совпадают с публичным subpath‑экспортом\n * `@feugene/granularity/components/<Name>`). Значения — browser‑safe\n * `ds<Name>Config` из `./config.ts` рядом с SFC.\n *\n * Для компонентов, у которых есть node-only вариант конфига\n * (например, `GrButton` с `tokenDefinitionsFromCssSync`), browser‑конфиг\n * остаётся здесь, а node‑вариант подставляется снаружи через\n * `createGranularityProvider(overrides)` — см. `./index.ts` (browser)\n * и `./node.ts` (node).\n *\n * Формат записи `<Name>: ds<Name>Config` критичен для regex‑парсинга\n * в `apps/showcase/scripts/generate-component-api.mjs`, не ломайте его.\n */\nexport const granularityComponentConfigs = {\n GrAlert: grAlertConfig,\n GrAvatar: grAvatarConfig,\n GrBadge: grBadgeConfig,\n GrBadgeWrap: grBadgeWrapConfig,\n GrBottomNav: grBottomNavConfig,\n GrButton: grButtonConfig,\n GrButtonGroup: grButtonGroupConfig,\n GrCard: grCardConfig,\n GrCheckbox: grCheckboxConfig,\n GrCollapse: grCollapseConfig,\n GrConfirmDialog: grConfirmDialogConfig,\n GrDataTable: grDataTableConfig,\n GrDialog: grDialogConfig,\n GrDrawer: grDrawerConfig,\n GrDropdown: grDropdownConfig,\n GrDropdownMenu: grDropdownMenuConfig,\n GrEmptyState: grEmptyStateConfig,\n GrFileUpload: grFileUploadConfig,\n GrFormField: grFormFieldConfig,\n GrFormFile: grFormFileConfig,\n GrFormSection: grFormSectionConfig,\n GrIcon: grIconConfig,\n GrImageViewer: grImageViewerConfig,\n GrInput: grInputConfig,\n GrInputTag: grInputTagConfig,\n GrLink: grLinkConfig,\n GrList: grListConfig,\n GrLoading: grLoadingConfig,\n GrModal: grModalConfig,\n GrNavbar: grNavbarConfig,\n GrNumberInput: grNumberInputConfig,\n GrPagination: grPaginationConfig,\n GrProgressBar: grProgressBarConfig,\n GrPromptDialog: grPromptDialogConfig,\n GrRadio: grRadioConfig,\n GrRadioGroup: grRadioGroupConfig,\n GrResponseErrorBanner: grResponseErrorBannerConfig,\n GrSegmented: grSegmentedConfig,\n GrSelect: grSelectConfig,\n GrSidebar: grSidebarConfig,\n GrSkeleton: grSkeletonConfig,\n GrSwitch: grSwitchConfig,\n GrTable: grTableConfig,\n GrTabs: grTabsConfig,\n GrTextarea: grTextareaConfig,\n GrToaster: grToasterConfig,\n GrTooltip: grTooltipConfig,\n GrTree: grTreeConfig,\n GrTreeSelect: grTreeSelectConfig,\n}\n\nexport type GranularityComponentName = keyof typeof granularityComponentConfigs\n\n/** Базовый (browser-safe) набор компонентов в порядке реестра. */\nconst baseComponents: readonly GranularComponentDescriptor[] = Object.values(\n granularityComponentConfigs,\n)\n\n/**\n * Собирает granular-provider пакета `@feugene/granularity`.\n *\n * Принимает массив `GranularComponentDescriptor`'ов — это компоненты,\n * у которых есть node-only вариант конфига (например, `GrButton` с\n * `tokenDefinitionsFromCssSync`). Дескрипторы с именем, которое уже\n * присутствует в базовом наборе, переопределяют его (побеждает\n * переданный снаружи), остальные — добавляются в конец списка.\n */\nexport function createGranularityProvider(\n overrides: readonly GranularComponentDescriptor[] = [],\n): GranularProvider {\n const overrideByName = new Map(\n overrides.map(component => [component.name, component]),\n )\n const components: GranularComponentDescriptor[] = [\n ...baseComponents.map(component => overrideByName.get(component.name) ?? component),\n ...overrides.filter(component => !baseComponents.some(base => base.name === component.name)),\n ]\n\n return defineGranularProvider({\n id: GRANULARITY_PROVIDER_ID,\n contractVersion: 1,\n packageBaseUrl,\n components,\n theme,\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAa,iBAAiB,wBAAwB,OAAO,KAAK,KAAK;CACrE,MAAM;CACN,cAAc,CAAC,YAAY,UAAU;CACrC,UAAU;CACX,CAAC;;;ACNF,IAAa,8BAA8B,wBAAwB,OAAO,KAAK,KAAK;CAClF,MAAM;CACN,cAAc,CAAC,WAAW,WAAW;CACtC,CAAC;;;;ACsEF,IAAa,0BAA0B;AAKvC,IAAM,iBAAiB,GAAG,OAAO,KAAK,IAAI,MACxC,GACA,OAAO,KAAK,IAAI,YAAY,KAAK,OAAO,KAAK,IAAI,YAAY,IAAI,GAAG,EAAE,GAAG,EAC1E;;AAGD,IAAa,wBAAwB,CAAC,SAAS,OAAO;;AAItD,IAAa,2BAA4D,CAAC,QAAQ;AAElF,IAAM,QAAQ;CACZ,YAAY,IAAA,IAAA,i2BAAA,KAAA,OAAA,KAAA,IAA8C,CAAC;CAC3D,cAAc,IAAA,IAAA,iuKAAA,KAAA,OAAA,KAAA,IAAgD,CAAC;CAC/D,QAAQ;EACN,OAAO,IAAA,IAAA,qrGAAA,KAAA,OAAA,KAAA,IAAsD,CAAC;EAC9D,MAAM,IAAA,IAAA,qxGAAA,KAAA,OAAA,KAAA,IAAqD,CAAC;EAC7D;CACD,eAAe;CAChB;;;;;;;;;;;;;;;;;AAkBD,IAAa,8BAA8B;CACzC,SAAS;CACT,UAAU;CACV,SAAS;CACT,aAAa;CACb,aAAa;CACb,UAAU;CACV,eAAe;CACf,QAAQ;CACR,YAAY;CACZ,YAAY;CACZ,iBAAiB;CACjB,aAAa;CACb,UAAU;CACV,UAAU;CACV,YAAY;CACZ,gBAAgB;CAChB,cAAc;CACd,cAAc;CACd,aAAa;CACb,YAAY;CACZ,eAAe;CACf,QAAQ;CACR,eAAe;CACf,SAAS;CACT,YAAY;CACZ,QAAQ;CACR,QAAQ;CACR,WAAW;CACX,SAAS;CACT,UAAU;CACV,eAAe;CACf,cAAc;CACd,eAAe;CACf,gBAAgB;CAChB,SAAS;CACT,cAAc;CACd,uBAAuB;CACvB,aAAa;CACb,UAAU;CACV,WAAW;CACX,YAAY;CACZ,UAAU;CACV,SAAS;CACT,QAAQ;CACR,YAAY;CACZ,WAAW;CACX,WAAW;CACX,QAAQ;CACR,cAAc;CACf;;AAKD,IAAM,iBAAyD,OAAO,OACpE,4BACD;;;;;;;;;;AAWD,SAAgB,0BACd,YAAoD,EAAE,EACpC;CAClB,MAAM,iBAAiB,IAAI,IACzB,UAAU,KAAI,cAAa,CAAC,UAAU,MAAM,UAAU,CAAC,CACxD;AAMD,QAAO,uBAAuB;EAC5B,IAAI;EACJ,iBAAiB;EACjB;EACA,YATgD,CAChD,GAAG,eAAe,KAAI,cAAa,eAAe,IAAI,UAAU,KAAK,IAAI,UAAU,EACnF,GAAG,UAAU,QAAO,cAAa,CAAC,eAAe,MAAK,SAAQ,KAAK,SAAS,UAAU,KAAK,CAAC,CAC7F;EAOC;EACD,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-BmGdp8-4.js";
|
|
2
2
|
import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-BV7RBU_0.js";
|
|
3
|
-
import { t as useGranularityTranslations } from "../../../chunks/granularityI18n-
|
|
3
|
+
import { t as useGranularityTranslations } from "../../../chunks/granularityI18n-CXa_uN7f.js";
|
|
4
4
|
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, markRaw, normalizeClass, openBlock, renderSlot, toDisplayString, unref, withCtx } from "vue";
|
|
5
5
|
//#region ~icons/lucide/menu
|
|
6
6
|
var _hoisted_1$1 = {
|
package/dist/components/GrNavbar/chunks/{GrNavbar-BDIKAYfN.js.map → GrNavbar-BoVuSZ-V.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrNavbar-
|
|
1
|
+
{"version":3,"file":"GrNavbar-BoVuSZ-V.js","names":[],"sources":["../../../../src/components/GrNavbar/GrNavbar.vue","../../../../src/components/GrNavbar/GrNavbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport IconMenu from '~icons/lucide/menu'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\n\n/**\n * GrNavbar — верхняя панель приложения (header) с заголовком,\n * опциональной кнопкой меню (обычно — для мобильных) и правым слотом\n * для действий/аватара.\n *\n * - Корень — `<header>` (landmark), без собственного `role`.\n * - `aria-label` кнопки меню локализуется через `useGranularityTranslations`.\n */\nexport interface GrNavbarProps {\n title: string\n showMenuButton?: boolean\n /** Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). */\n menuButtonClass?: string\n}\n\nwithDefaults(defineProps<GrNavbarProps>(), {\n showMenuButton: false,\n menuButtonClass: '',\n})\n\nconst emit = defineEmits<{\n (e: 'menu'): void\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst menuAriaLabel = computed(() => t('gr.navbar.openMenu', 'Open menu'))\n</script>\n\n<template>\n <header\n data-ds-navbar\n class=\"h-[56px] border-b border-[var(--brd)] bg-[var(--bg)] flex items-center justify-between px-4 sm:px-6\"\n >\n <div class=\"flex items-center gap-3\">\n <GrButton\n v-if=\"showMenuButton\"\n data-ds-navbar-menu\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"menuAriaLabel\"\n :class=\"menuButtonClass\"\n @click=\"emit('menu')\"\n >\n <GrIcon :size=\"16\">\n <IconMenu aria-hidden=\"true\" />\n </GrIcon>\n </GrButton>\n <div data-ds-navbar-title class=\"text-[14px] font-700\">\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <div class=\"flex items-center gap-3\">\n <slot />\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport IconMenu from '~icons/lucide/menu'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\n\n/**\n * GrNavbar — верхняя панель приложения (header) с заголовком,\n * опциональной кнопкой меню (обычно — для мобильных) и правым слотом\n * для действий/аватара.\n *\n * - Корень — `<header>` (landmark), без собственного `role`.\n * - `aria-label` кнопки меню локализуется через `useGranularityTranslations`.\n */\nexport interface GrNavbarProps {\n title: string\n showMenuButton?: boolean\n /** Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). */\n menuButtonClass?: string\n}\n\nwithDefaults(defineProps<GrNavbarProps>(), {\n showMenuButton: false,\n menuButtonClass: '',\n})\n\nconst emit = defineEmits<{\n (e: 'menu'): void\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst menuAriaLabel = computed(() => t('gr.navbar.openMenu', 'Open menu'))\n</script>\n\n<template>\n <header\n data-ds-navbar\n class=\"h-[56px] border-b border-[var(--brd)] bg-[var(--bg)] flex items-center justify-between px-4 sm:px-6\"\n >\n <div class=\"flex items-center gap-3\">\n <GrButton\n v-if=\"showMenuButton\"\n data-ds-navbar-menu\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"menuAriaLabel\"\n :class=\"menuButtonClass\"\n @click=\"emit('menu')\"\n >\n <GrIcon :size=\"16\">\n <IconMenu aria-hidden=\"true\" />\n </GrIcon>\n </GrButton>\n <div data-ds-navbar-title class=\"text-[14px] font-700\">\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <div class=\"flex items-center gap-3\">\n <slot />\n </div>\n </header>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BA,MAAM,OAAO;EAIb,MAAM,EAAE,MAAM,4BAA2B;EAEzC,MAAM,gBAAgB,eAAe,EAAE,sBAAsB,YAAY,CAAA;;uBAIvE,mBA6BS,UA7BT,YA6BS,CAzBP,mBAoBM,OApBN,YAoBM,CAlBI,QAAA,kBAAA,WAAA,EADR,YAaW,kBAAA;;IAXT,uBAAA;IACA,SAAQ;IACR,MAAK;IACL,QAAA;IACC,cAAY,cAAA;IACZ,OAAK,eAAE,QAAA,gBAAe;IACtB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;2BAIH,CAFT,YAES,gBAAA,EAFA,MAAM,IAAE,EAAA;4BACgB,CAA/B,YAA+B,MAAA,aAAA,EAAA,EAArB,eAAY,QAAM,CAAA,CAAA,CAAA;;;;mEAGhC,mBAIM,OAJN,YAIM,CAHJ,WAEO,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBADF,QAAA,MAAK,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAKd,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrNavbar_default } from "./chunks/GrNavbar-
|
|
1
|
+
import { t as GrNavbar_default } from "./chunks/GrNavbar-BoVuSZ-V.js";
|
|
2
2
|
import { t as grNavbarConfig } from "../../chunks/config-D1DI3roo.js";
|
|
3
3
|
export { GrNavbar_default as GrNavbar, GrNavbar_default as default, grNavbarConfig };
|
package/dist/components/GrPagination/chunks/{GrPagination-BZlH-uEd.js → GrPagination-DW44c2-a.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-BmGdp8-4.js";
|
|
2
|
-
import { t as useGranularityTranslations } from "../../../chunks/granularityI18n-
|
|
2
|
+
import { t as useGranularityTranslations } from "../../../chunks/granularityI18n-CXa_uN7f.js";
|
|
3
3
|
import { t as GrSelect_default } from "../../GrSelect/chunks/GrSelect-C3NAfTqL.js";
|
|
4
4
|
import { Fragment, computed, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, renderList, toDisplayString, unref, withCtx } from "vue";
|
|
5
5
|
//#region src/components/GrPagination/GrPagination.vue?vue&type=script&setup=true&lang.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrPagination-
|
|
1
|
+
{"version":3,"file":"GrPagination-DW44c2-a.js","names":[],"sources":["../../../../src/components/GrPagination/GrPagination.vue","../../../../src/components/GrPagination/GrPagination.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Окно отображаемых номеров — 5.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n})\n\nconst emit = defineEmits<{\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}>()\n\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)))\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\nconst pages = computed(() => {\n const max = pageCount.value\n const current = props.page\n const windowSize = 5\n const half = Math.floor(windowSize / 2)\n\n let start = Math.max(1, current - half)\n const end = Math.min(max, start + windowSize - 1)\n start = Math.max(1, end - windowSize + 1)\n\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n\n return list\n})\n\nfunction goTo(page: number): void {\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, props.page - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, props.page + 1))\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end gap-3\"\n data-ds-pagination\n role=\"navigation\"\n :aria-label=\"t('gr.pagination.pageSize', 'Pagination')\"\n >\n <div class=\"min-w-[100px]\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <div class=\"flex items-center gap-1\">\n <button\n v-for=\"pageNumber in pages\"\n :key=\"pageNumber\"\n type=\"button\"\n data-ds-pagination-page\n :aria-current=\"pageNumber === page ? 'page' : undefined\"\n :aria-label=\"`Page ${pageNumber}`\"\n class=\"h-8 min-w-8 px-2 rounded-md text-sm font-600 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :class=\"pageNumber === page ? 'bg-[var(--primary)] text-[var(--primary-fg)]' : 'text-[var(--muted-fg)] hover:bg-[var(--muted)] hover:text-[var(--fg)]'\"\n @click=\"goTo(pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Окно отображаемых номеров — 5.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n})\n\nconst emit = defineEmits<{\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}>()\n\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)))\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\nconst pages = computed(() => {\n const max = pageCount.value\n const current = props.page\n const windowSize = 5\n const half = Math.floor(windowSize / 2)\n\n let start = Math.max(1, current - half)\n const end = Math.min(max, start + windowSize - 1)\n start = Math.max(1, end - windowSize + 1)\n\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n\n return list\n})\n\nfunction goTo(page: number): void {\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, props.page - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, props.page + 1))\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end gap-3\"\n data-ds-pagination\n role=\"navigation\"\n :aria-label=\"t('gr.pagination.pageSize', 'Pagination')\"\n >\n <div class=\"min-w-[100px]\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <div class=\"flex items-center gap-1\">\n <button\n v-for=\"pageNumber in pages\"\n :key=\"pageNumber\"\n type=\"button\"\n data-ds-pagination-page\n :aria-current=\"pageNumber === page ? 'page' : undefined\"\n :aria-label=\"`Page ${pageNumber}`\"\n class=\"h-8 min-w-8 px-2 rounded-md text-sm font-600 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :class=\"pageNumber === page ? 'bg-[var(--primary)] text-[var(--primary-fg)]' : 'text-[var(--muted-fg)] hover:bg-[var(--muted)] hover:text-[var(--fg)]'\"\n @click=\"goTo(pageNumber)\"\n >\n {{ pageNumber }}\n </button>\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBA,MAAM,EAAE,MAAM,4BAA2B;EAEzC,MAAM,QAAQ;EAId,MAAM,OAAO;EAKb,MAAM,YAAY,eAAe,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,QAAQ,MAAM,SAAS,CAAC,CAAA;EAErF,MAAM,gBAAgB,SAAS;GAC7B,WAAW,OAAO,MAAM,SAAS;GACjC,MAAM,UAAkB,KAAK,mBAAmB,OAAO,MAAM,CAAC;GAC/D,CAAA;EAED,MAAM,kBAAkB,eACtB,MAAM,UAAU,KAAI,cAAa;GAC/B,OAAO,OAAO,SAAS;GACvB,OAAO,OAAO,SAAS;GACxB,EAAE,CACL;EAEA,MAAM,QAAQ,eAAe;GAC3B,MAAM,MAAM,UAAU;GACtB,MAAM,UAAU,MAAM;GACtB,MAAM,aAAa;GACnB,MAAM,OAAO,KAAK,MAAM,aAAa,EAAC;GAEtC,IAAI,QAAQ,KAAK,IAAI,GAAG,UAAU,KAAI;GACtC,MAAM,MAAM,KAAK,IAAI,KAAK,QAAQ,aAAa,EAAC;AAChD,WAAQ,KAAK,IAAI,GAAG,MAAM,aAAa,EAAC;GAExC,MAAM,OAAiB,EAAC;AACxB,QAAK,IAAI,OAAO,OAAO,QAAQ,KAAK,QAAQ,EAC1C,MAAK,KAAK,KAAI;AAEhB,UAAO;IACR;EAED,SAAS,KAAK,MAAoB;AAChC,QAAK,eAAe,KAAI;;EAG1B,SAAS,OAAa;AACpB,QAAK,KAAK,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;;EAGlC,SAAS,OAAa;AACpB,QAAK,KAAK,IAAI,UAAU,OAAO,MAAM,OAAO,EAAE,CAAA;;;uBAK9C,mBAqCM,OAAA;IApCJ,OAAM;IACN,sBAAA;IACA,MAAK;IACJ,cAAY,MAAA,EAAC,CAAA,0BAAA,aAAA;;IAEd,mBAMM,OANN,YAMM,CALJ,YAIE,kBAAA;iBAHS,cAAA;gFAAa,QAAA;KACrB,SAAS,gBAAA;KACT,cAAY,MAAA,EAAC,CAAA,0BAAA,YAAA;;;;;;IAIlB,YAEW,kBAAA;KAFD,SAAQ;KAAQ,MAAK;KAAM,UAAU,QAAA,QAAI;KAAQ,SAAO;;4BAC3B,CAAA,gBAAA,gBAAlC,MAAA,EAAC,CAAA,sBAAA,OAAA,CAAA,EAAA,EAAA,CAAA,CAAA;;;IAGN,mBAcM,OAdN,YAcM,EAAA,UAAA,KAAA,EAbJ,mBAYS,UAAA,MAAA,WAXc,MAAA,QAAd,eAAU;yBADnB,mBAYS,UAAA;MAVN,KAAK;MACN,MAAK;MACL,2BAAA;MACC,gBAAc,eAAe,QAAA,OAAI,SAAY,KAAA;MAC7C,cAAU,QAAU;MACrB,OAAK,eAAA,CAAC,wJACE,eAAe,QAAA,OAAI,iDAAA,wEAAA,CAAA;MAC1B,UAAK,WAAE,KAAK,WAAU;wBAEpB,WAAU,EAAA,IAAA,WAAA;;IAIjB,YAEW,kBAAA;KAFD,SAAQ;KAAQ,MAAK;KAAM,UAAU,QAAA,QAAQ,UAAA;KAAY,SAAO;;4BACnC,CAAA,gBAAA,gBAAlC,MAAA,EAAC,CAAA,sBAAA,OAAA,CAAA,EAAA,EAAA,CAAA,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrPagination_default } from "./chunks/GrPagination-
|
|
1
|
+
import { t as GrPagination_default } from "./chunks/GrPagination-DW44c2-a.js";
|
|
2
2
|
import { t as grPaginationConfig } from "../../chunks/config-DudL02ag.js";
|
|
3
3
|
export { GrPagination_default as GrPagination, GrPagination_default as default, grPaginationConfig };
|
package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-n0CZT4Ic.js → GrTreeSelect-DkAIksRW.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { vClickOutside } from "../../../directives/clickOutside.js";
|
|
2
2
|
import { t as GrInput_default } from "../../GrInput/chunks/GrInput-CfO2RsM-.js";
|
|
3
|
-
import { t as useGranularityTranslations } from "../../../chunks/granularityI18n-
|
|
3
|
+
import { t as useGranularityTranslations } from "../../../chunks/granularityI18n-CXa_uN7f.js";
|
|
4
4
|
import { t as GrTree_default } from "../../GrTree/chunks/GrTree-Dlev9oBd.js";
|
|
5
5
|
import { i as grTreeSelectPanelClass, r as grTreeSelectClass } from "../../../chunks/config-C0b_3LJV.js";
|
|
6
6
|
import { Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onUnmounted, openBlock, ref, renderSlot, toDisplayString, unref, vShow, watch, withCtx, withDirectives, withModifiers } from "vue";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrTreeSelect-n0CZT4Ic.js","names":["$slots"],"sources":["../../../../src/components/GrTreeSelect/GrTreeSelect.vue","../../../../src/components/GrTreeSelect/GrTreeSelect.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any> = any\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport { vClickOutside } from '../../directives'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree, {\n type GrTreeInstance,\n type GrTreeKey,\n type GrTreeNode,\n} from '../GrTree'\nimport type { GrTreeSelectModelValue, GrTreeSelectProps } from './grTreeSelectTypes'\nimport { grTreeSelectClass, grTreeSelectPanelClass } from './grTreeSelectStyles'\n\nconst props = withDefaults(\n defineProps<GrTreeSelectProps<T>>(),\n {\n props: () => ({\n children: 'children',\n label: 'label',\n }),\n nodeKey: 'id' as any,\n defaultExpandedKeys: () => [],\n disabled: false,\n placeholder: undefined,\n size: 'md',\n invalid: false,\n state: 'default',\n multiple: false,\n clearable: false,\n valueDisplay: 'label',\n filterable: false,\n filterPlaceholder: undefined,\n filterInputmode: undefined,\n filterNodeMethod: undefined,\n closeOnSelect: undefined,\n dropdownMaxHeight: 320,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrTreeSelectModelValue): void\n (e: 'change', value: GrTreeSelectModelValue): void\n (e: 'visibleChange', visible: boolean): void\n (e: 'clear'): void\n (e: 'nodeClick', data: T, node: GrTreeNode<T>): void\n}>()\n\ndefineSlots<{\n /** Рендер значения внутри триггера (вместо дефолтного текста). */\n value?: (props: { value: GrTreeSelectModelValue; labels: string[]; displayValue: string; pathLabels?: string[] }) => any\n /** Рендер строки дерева. */\n node?: (props: { node: GrTreeNode<T>; data: T; selected: boolean }) => any\n /** Содержимое пустого состояния (когда нет данных). */\n empty?: () => any\n}>()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst triggerEl = ref<HTMLInputElement | null>(null)\nconst filterInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst treeRef = ref<GrTreeInstance | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nlet hadPointerDownOnTrigger = false\n\nconst open = ref(false)\nconst filterValue = ref('')\n\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined')\n return\n\n const root = rootEl.value\n if (!root)\n return\n\n const rect = root.getBoundingClientRect()\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nconst closeOnSelectResolved = computed(() => {\n return props.closeOnSelect ?? !props.multiple\n})\n\nfunction getChildrenKey() {\n return props.props.children ?? 'children'\n}\n\nfunction getLabelKey() {\n return props.props.label ?? 'label'\n}\n\nfunction nodeLabel(data: T): string {\n const key = getLabelKey() as keyof T\n const v = data[key]\n return v == null ? '' : String(v)\n}\n\nfunction nodeChildren(data: T): T[] {\n const key = getChildrenKey() as keyof T\n const v = data[key]\n return Array.isArray(v) ? (v as any) : []\n}\n\nfunction nodeKeyOf(data: T, index: number, parentKey: GrTreeKey | undefined): GrTreeKey {\n const keyName = props.nodeKey as keyof T\n const v = data[keyName]\n if (v != null)\n return v as GrTreeKey\n\n return parentKey == null ? index : `${String(parentKey)}:${index}`\n}\n\nconst labelByKey = computed(() => {\n const map = new Map<GrTreeKey, string>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n map.set(key, nodeLabel(item))\n walk(nodeChildren(item), key)\n }\n }\n\n walk(props.data ?? [], undefined)\n return map\n})\n\nconst pathLabelsByKey = computed(() => {\n const map = new Map<GrTreeKey, string[]>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined, parentPath: string[]) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n const label = nodeLabel(item)\n const path = [...parentPath, label]\n map.set(key, path)\n walk(nodeChildren(item), key, path)\n }\n }\n\n walk(props.data ?? [], undefined, [])\n return map\n})\n\nconst selectedKeys = computed<GrTreeKey[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n const v = props.modelValue\n if (v == null || Array.isArray(v))\n return []\n return [v]\n})\n\nconst selectedKeySet = computed(() => new Set(selectedKeys.value))\n\nconst selectedLabels = computed(() => {\n const map = labelByKey.value\n return selectedKeys.value.map((k) => map.get(k) ?? String(k))\n})\n\nconst selectedPathLabels = computed<string[] | undefined>(() => {\n if (props.multiple)\n return undefined\n\n const key = selectedKeys.value[0]\n if (key == null)\n return undefined\n\n const path = pathLabelsByKey.value.get(key)\n if (path?.length)\n return path\n\n const labels = selectedLabels.value\n return labels.length ? [labels[0]] : undefined\n})\n\nconst displayValue = computed(() => {\n const labels = selectedLabels.value\n if (labels.length === 0)\n return ''\n\n if (!props.multiple) {\n if (props.valueDisplay === 'path') {\n const path = selectedPathLabels.value\n if (path?.length)\n return path.join(' / ')\n }\n return labels[0]\n }\n\n if (labels.length === 1)\n return labels[0]\n return `${labels[0]} +${labels.length - 1}`\n})\n\nconst hasSelection = computed(() => selectedKeys.value.length > 0)\n\nconst resolvedFilterPlaceholder = computed(() => {\n return props.filterPlaceholder ?? t('gr.treeSelect.filterPlaceholder', 'Search…')\n})\n\nconst className = computed(() => {\n return grTreeSelectClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n })\n})\n\nconst panelClasses = computed(() => {\n return grTreeSelectPanelClass\n})\n\nfunction setOpen(next: boolean) {\n if (props.disabled)\n return\n\n if (open.value === next)\n return\n\n open.value = next\n emit('visibleChange', next)\n}\n\nfunction onClickOutside(): void {\n setOpen(false)\n}\n\nfunction openDropdown(): void {\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction toggleDropdown(): void {\n setOpen(!open.value)\n}\n\nfunction onTriggerPointerDown(): void {\n hadPointerDownOnTrigger = true\n if (typeof window !== 'undefined') {\n window.setTimeout(() => {\n hadPointerDownOnTrigger = false\n }, 0)\n }\n else {\n // SSR / no window: reset sync\n hadPointerDownOnTrigger = false\n }\n}\n\nfunction onTriggerFocus(): void {\n if (hadPointerDownOnTrigger)\n return\n openDropdown()\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n closeDropdown()\n nextTick(() => triggerEl.value?.focus())\n }\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined')\n return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen)\n document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen)\n return\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n // sync tree highlight\n if (!props.multiple) {\n const v = props.modelValue\n if (v != null && !Array.isArray(v))\n treeRef.value?.setCurrentKey(v)\n }\n\n // focus filter input\n if (props.filterable) {\n await nextTick()\n filterInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined')\n return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (props.multiple)\n return\n if (v == null || Array.isArray(v)) {\n treeRef.value?.setCurrentKey(undefined)\n return\n }\n treeRef.value?.setCurrentKey(v)\n },\n)\n\nwatch(\n filterValue,\n (v) => {\n treeRef.value?.filter(v)\n },\n)\n\nwatch(\n open,\n (isOpen) => {\n if (isOpen)\n return\n if (filterValue.value.trim().length > 0)\n filterValue.value = ''\n },\n)\n\nfunction emitModel(next: GrTreeSelectModelValue) {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction clear(): void {\n if (props.disabled)\n return\n\n const next: GrTreeSelectModelValue = props.multiple ? [] : null\n emitModel(next)\n emit('clear')\n if (!props.multiple)\n treeRef.value?.setCurrentKey(undefined)\n}\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (props.disabled)\n return\n\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openDropdown()\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n closeDropdown()\n }\n}\n\nfunction onNodeClick(data: T, node: GrTreeNode<T>): void {\n emit('nodeClick', data, node)\n\n if (props.disabled)\n return\n\n if (props.multiple) {\n const next = new Set(selectedKeySet.value)\n if (next.has(node.key))\n next.delete(node.key)\n else\n next.add(node.key)\n emitModel([...next])\n\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n return\n }\n\n emitModel(node.key)\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: onClickOutside, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-tree-select\n class=\"relative\"\n >\n <div class=\"relative\">\n <input\n ref=\"triggerEl\"\n data-testid=\"ds-tree-select-trigger\"\n data-ds-tree-select-trigger\n type=\"text\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n readonly\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] text-[var(--fg)] placeholder:text-[var(--muted-fg)] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"[className, $slots.value ? 'text-transparent placeholder:text-transparent' : '']\"\n @pointerdown=\"onTriggerPointerDown\"\n @click=\"toggleDropdown\"\n @focus=\"onTriggerFocus\"\n @keydown=\"onTriggerKeydown\"\n >\n\n <button\n v-if=\"clearable && hasSelection\"\n data-testid=\"ds-tree-select-clear\"\n data-ds-tree-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"t('gr.common.clear', 'Clear')\"\n @click.stop=\"clear\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" />\n </button>\n\n <span\n v-else\n data-testid=\"ds-tree-select-chevron\"\n data-ds-tree-select-chevron\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span\n class=\"i-lucide-chevron-down inline-block h-4 w-4 transition-transform duration-150\"\n :class=\"open ? 'rotate-180' : ''\"\n />\n </span>\n\n <div\n v-if=\"$slots.value\"\n class=\"absolute inset-y-0 left-3 right-9 flex items-center pointer-events-none\"\n >\n <slot\n name=\"value\"\n :value=\"modelValue\"\n :labels=\"selectedLabels\"\n :display-value=\"displayValue\"\n :path-labels=\"selectedPathLabels\"\n />\n </div>\n </div>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-tree-select-panel\"\n data-ds-tree-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"filterable\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"filterInputRef\"\n v-model=\"filterValue\"\n data-testid=\"ds-tree-select-filter\"\n data-ds-tree-select-filter\n type=\"search\"\n :inputmode=\"filterInputmode\"\n :placeholder=\"resolvedFilterPlaceholder\"\n size=\"sm\"\n />\n </div>\n\n <div\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n >\n <div v-if=\"(data?.length ?? 0) === 0\" class=\"px-3 py-2 text-[13px] text-[var(--muted-fg)]\">\n <slot name=\"empty\">\n {{ t('gr.treeSelect.empty', 'No data') }}\n </slot>\n </div>\n\n <GrTree\n v-else\n ref=\"treeRef\"\n :data=\"data\"\n :props=\"props.props\"\n :node-key=\"nodeKey as any\"\n :default-expanded-keys=\"defaultExpandedKeys\"\n :filter-node-method=\"filterNodeMethod\"\n :highlight-current=\"!multiple\"\n @node-click=\"onNodeClick\"\n >\n <template #default=\"{ node, data }\">\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"data\"\n :selected=\"selectedKeySet.has(node.key)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"selectedKeySet.has(node.key) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ node.label }}</span>\n </div>\n </slot>\n </template>\n </GrTree>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\" generic=\"T extends Record<string, any> = any\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport { vClickOutside } from '../../directives'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree, {\n type GrTreeInstance,\n type GrTreeKey,\n type GrTreeNode,\n} from '../GrTree'\nimport type { GrTreeSelectModelValue, GrTreeSelectProps } from './grTreeSelectTypes'\nimport { grTreeSelectClass, grTreeSelectPanelClass } from './grTreeSelectStyles'\n\nconst props = withDefaults(\n defineProps<GrTreeSelectProps<T>>(),\n {\n props: () => ({\n children: 'children',\n label: 'label',\n }),\n nodeKey: 'id' as any,\n defaultExpandedKeys: () => [],\n disabled: false,\n placeholder: undefined,\n size: 'md',\n invalid: false,\n state: 'default',\n multiple: false,\n clearable: false,\n valueDisplay: 'label',\n filterable: false,\n filterPlaceholder: undefined,\n filterInputmode: undefined,\n filterNodeMethod: undefined,\n closeOnSelect: undefined,\n dropdownMaxHeight: 320,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrTreeSelectModelValue): void\n (e: 'change', value: GrTreeSelectModelValue): void\n (e: 'visibleChange', visible: boolean): void\n (e: 'clear'): void\n (e: 'nodeClick', data: T, node: GrTreeNode<T>): void\n}>()\n\ndefineSlots<{\n /** Рендер значения внутри триггера (вместо дефолтного текста). */\n value?: (props: { value: GrTreeSelectModelValue; labels: string[]; displayValue: string; pathLabels?: string[] }) => any\n /** Рендер строки дерева. */\n node?: (props: { node: GrTreeNode<T>; data: T; selected: boolean }) => any\n /** Содержимое пустого состояния (когда нет данных). */\n empty?: () => any\n}>()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst triggerEl = ref<HTMLInputElement | null>(null)\nconst filterInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst treeRef = ref<GrTreeInstance | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nlet hadPointerDownOnTrigger = false\n\nconst open = ref(false)\nconst filterValue = ref('')\n\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined')\n return\n\n const root = rootEl.value\n if (!root)\n return\n\n const rect = root.getBoundingClientRect()\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nconst closeOnSelectResolved = computed(() => {\n return props.closeOnSelect ?? !props.multiple\n})\n\nfunction getChildrenKey() {\n return props.props.children ?? 'children'\n}\n\nfunction getLabelKey() {\n return props.props.label ?? 'label'\n}\n\nfunction nodeLabel(data: T): string {\n const key = getLabelKey() as keyof T\n const v = data[key]\n return v == null ? '' : String(v)\n}\n\nfunction nodeChildren(data: T): T[] {\n const key = getChildrenKey() as keyof T\n const v = data[key]\n return Array.isArray(v) ? (v as any) : []\n}\n\nfunction nodeKeyOf(data: T, index: number, parentKey: GrTreeKey | undefined): GrTreeKey {\n const keyName = props.nodeKey as keyof T\n const v = data[keyName]\n if (v != null)\n return v as GrTreeKey\n\n return parentKey == null ? index : `${String(parentKey)}:${index}`\n}\n\nconst labelByKey = computed(() => {\n const map = new Map<GrTreeKey, string>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n map.set(key, nodeLabel(item))\n walk(nodeChildren(item), key)\n }\n }\n\n walk(props.data ?? [], undefined)\n return map\n})\n\nconst pathLabelsByKey = computed(() => {\n const map = new Map<GrTreeKey, string[]>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined, parentPath: string[]) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n const label = nodeLabel(item)\n const path = [...parentPath, label]\n map.set(key, path)\n walk(nodeChildren(item), key, path)\n }\n }\n\n walk(props.data ?? [], undefined, [])\n return map\n})\n\nconst selectedKeys = computed<GrTreeKey[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n const v = props.modelValue\n if (v == null || Array.isArray(v))\n return []\n return [v]\n})\n\nconst selectedKeySet = computed(() => new Set(selectedKeys.value))\n\nconst selectedLabels = computed(() => {\n const map = labelByKey.value\n return selectedKeys.value.map((k) => map.get(k) ?? String(k))\n})\n\nconst selectedPathLabels = computed<string[] | undefined>(() => {\n if (props.multiple)\n return undefined\n\n const key = selectedKeys.value[0]\n if (key == null)\n return undefined\n\n const path = pathLabelsByKey.value.get(key)\n if (path?.length)\n return path\n\n const labels = selectedLabels.value\n return labels.length ? [labels[0]] : undefined\n})\n\nconst displayValue = computed(() => {\n const labels = selectedLabels.value\n if (labels.length === 0)\n return ''\n\n if (!props.multiple) {\n if (props.valueDisplay === 'path') {\n const path = selectedPathLabels.value\n if (path?.length)\n return path.join(' / ')\n }\n return labels[0]\n }\n\n if (labels.length === 1)\n return labels[0]\n return `${labels[0]} +${labels.length - 1}`\n})\n\nconst hasSelection = computed(() => selectedKeys.value.length > 0)\n\nconst resolvedFilterPlaceholder = computed(() => {\n return props.filterPlaceholder ?? t('gr.treeSelect.filterPlaceholder', 'Search…')\n})\n\nconst className = computed(() => {\n return grTreeSelectClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n })\n})\n\nconst panelClasses = computed(() => {\n return grTreeSelectPanelClass\n})\n\nfunction setOpen(next: boolean) {\n if (props.disabled)\n return\n\n if (open.value === next)\n return\n\n open.value = next\n emit('visibleChange', next)\n}\n\nfunction onClickOutside(): void {\n setOpen(false)\n}\n\nfunction openDropdown(): void {\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction toggleDropdown(): void {\n setOpen(!open.value)\n}\n\nfunction onTriggerPointerDown(): void {\n hadPointerDownOnTrigger = true\n if (typeof window !== 'undefined') {\n window.setTimeout(() => {\n hadPointerDownOnTrigger = false\n }, 0)\n }\n else {\n // SSR / no window: reset sync\n hadPointerDownOnTrigger = false\n }\n}\n\nfunction onTriggerFocus(): void {\n if (hadPointerDownOnTrigger)\n return\n openDropdown()\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n closeDropdown()\n nextTick(() => triggerEl.value?.focus())\n }\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined')\n return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen)\n document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen)\n return\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n // sync tree highlight\n if (!props.multiple) {\n const v = props.modelValue\n if (v != null && !Array.isArray(v))\n treeRef.value?.setCurrentKey(v)\n }\n\n // focus filter input\n if (props.filterable) {\n await nextTick()\n filterInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined')\n return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (props.multiple)\n return\n if (v == null || Array.isArray(v)) {\n treeRef.value?.setCurrentKey(undefined)\n return\n }\n treeRef.value?.setCurrentKey(v)\n },\n)\n\nwatch(\n filterValue,\n (v) => {\n treeRef.value?.filter(v)\n },\n)\n\nwatch(\n open,\n (isOpen) => {\n if (isOpen)\n return\n if (filterValue.value.trim().length > 0)\n filterValue.value = ''\n },\n)\n\nfunction emitModel(next: GrTreeSelectModelValue) {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction clear(): void {\n if (props.disabled)\n return\n\n const next: GrTreeSelectModelValue = props.multiple ? [] : null\n emitModel(next)\n emit('clear')\n if (!props.multiple)\n treeRef.value?.setCurrentKey(undefined)\n}\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (props.disabled)\n return\n\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openDropdown()\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n closeDropdown()\n }\n}\n\nfunction onNodeClick(data: T, node: GrTreeNode<T>): void {\n emit('nodeClick', data, node)\n\n if (props.disabled)\n return\n\n if (props.multiple) {\n const next = new Set(selectedKeySet.value)\n if (next.has(node.key))\n next.delete(node.key)\n else\n next.add(node.key)\n emitModel([...next])\n\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n return\n }\n\n emitModel(node.key)\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: onClickOutside, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-tree-select\n class=\"relative\"\n >\n <div class=\"relative\">\n <input\n ref=\"triggerEl\"\n data-testid=\"ds-tree-select-trigger\"\n data-ds-tree-select-trigger\n type=\"text\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n readonly\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] text-[var(--fg)] placeholder:text-[var(--muted-fg)] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"[className, $slots.value ? 'text-transparent placeholder:text-transparent' : '']\"\n @pointerdown=\"onTriggerPointerDown\"\n @click=\"toggleDropdown\"\n @focus=\"onTriggerFocus\"\n @keydown=\"onTriggerKeydown\"\n >\n\n <button\n v-if=\"clearable && hasSelection\"\n data-testid=\"ds-tree-select-clear\"\n data-ds-tree-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"t('gr.common.clear', 'Clear')\"\n @click.stop=\"clear\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" />\n </button>\n\n <span\n v-else\n data-testid=\"ds-tree-select-chevron\"\n data-ds-tree-select-chevron\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span\n class=\"i-lucide-chevron-down inline-block h-4 w-4 transition-transform duration-150\"\n :class=\"open ? 'rotate-180' : ''\"\n />\n </span>\n\n <div\n v-if=\"$slots.value\"\n class=\"absolute inset-y-0 left-3 right-9 flex items-center pointer-events-none\"\n >\n <slot\n name=\"value\"\n :value=\"modelValue\"\n :labels=\"selectedLabels\"\n :display-value=\"displayValue\"\n :path-labels=\"selectedPathLabels\"\n />\n </div>\n </div>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-tree-select-panel\"\n data-ds-tree-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"filterable\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"filterInputRef\"\n v-model=\"filterValue\"\n data-testid=\"ds-tree-select-filter\"\n data-ds-tree-select-filter\n type=\"search\"\n :inputmode=\"filterInputmode\"\n :placeholder=\"resolvedFilterPlaceholder\"\n size=\"sm\"\n />\n </div>\n\n <div\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n >\n <div v-if=\"(data?.length ?? 0) === 0\" class=\"px-3 py-2 text-[13px] text-[var(--muted-fg)]\">\n <slot name=\"empty\">\n {{ t('gr.treeSelect.empty', 'No data') }}\n </slot>\n </div>\n\n <GrTree\n v-else\n ref=\"treeRef\"\n :data=\"data\"\n :props=\"props.props\"\n :node-key=\"nodeKey as any\"\n :default-expanded-keys=\"defaultExpandedKeys\"\n :filter-node-method=\"filterNodeMethod\"\n :highlight-current=\"!multiple\"\n @node-click=\"onNodeClick\"\n >\n <template #default=\"{ node, data }\">\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"data\"\n :selected=\"selectedKeySet.has(node.key)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"selectedKeySet.has(node.key) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ node.label }}</span>\n </div>\n </slot>\n </template>\n </GrTree>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GrTreeSelect-DkAIksRW.js","names":["$slots"],"sources":["../../../../src/components/GrTreeSelect/GrTreeSelect.vue","../../../../src/components/GrTreeSelect/GrTreeSelect.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any> = any\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport { vClickOutside } from '../../directives'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree, {\n type GrTreeInstance,\n type GrTreeKey,\n type GrTreeNode,\n} from '../GrTree'\nimport type { GrTreeSelectModelValue, GrTreeSelectProps } from './grTreeSelectTypes'\nimport { grTreeSelectClass, grTreeSelectPanelClass } from './grTreeSelectStyles'\n\nconst props = withDefaults(\n defineProps<GrTreeSelectProps<T>>(),\n {\n props: () => ({\n children: 'children',\n label: 'label',\n }),\n nodeKey: 'id' as any,\n defaultExpandedKeys: () => [],\n disabled: false,\n placeholder: undefined,\n size: 'md',\n invalid: false,\n state: 'default',\n multiple: false,\n clearable: false,\n valueDisplay: 'label',\n filterable: false,\n filterPlaceholder: undefined,\n filterInputmode: undefined,\n filterNodeMethod: undefined,\n closeOnSelect: undefined,\n dropdownMaxHeight: 320,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrTreeSelectModelValue): void\n (e: 'change', value: GrTreeSelectModelValue): void\n (e: 'visibleChange', visible: boolean): void\n (e: 'clear'): void\n (e: 'nodeClick', data: T, node: GrTreeNode<T>): void\n}>()\n\ndefineSlots<{\n /** Рендер значения внутри триггера (вместо дефолтного текста). */\n value?: (props: { value: GrTreeSelectModelValue; labels: string[]; displayValue: string; pathLabels?: string[] }) => any\n /** Рендер строки дерева. */\n node?: (props: { node: GrTreeNode<T>; data: T; selected: boolean }) => any\n /** Содержимое пустого состояния (когда нет данных). */\n empty?: () => any\n}>()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst triggerEl = ref<HTMLInputElement | null>(null)\nconst filterInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst treeRef = ref<GrTreeInstance | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nlet hadPointerDownOnTrigger = false\n\nconst open = ref(false)\nconst filterValue = ref('')\n\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined')\n return\n\n const root = rootEl.value\n if (!root)\n return\n\n const rect = root.getBoundingClientRect()\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nconst closeOnSelectResolved = computed(() => {\n return props.closeOnSelect ?? !props.multiple\n})\n\nfunction getChildrenKey() {\n return props.props.children ?? 'children'\n}\n\nfunction getLabelKey() {\n return props.props.label ?? 'label'\n}\n\nfunction nodeLabel(data: T): string {\n const key = getLabelKey() as keyof T\n const v = data[key]\n return v == null ? '' : String(v)\n}\n\nfunction nodeChildren(data: T): T[] {\n const key = getChildrenKey() as keyof T\n const v = data[key]\n return Array.isArray(v) ? (v as any) : []\n}\n\nfunction nodeKeyOf(data: T, index: number, parentKey: GrTreeKey | undefined): GrTreeKey {\n const keyName = props.nodeKey as keyof T\n const v = data[keyName]\n if (v != null)\n return v as GrTreeKey\n\n return parentKey == null ? index : `${String(parentKey)}:${index}`\n}\n\nconst labelByKey = computed(() => {\n const map = new Map<GrTreeKey, string>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n map.set(key, nodeLabel(item))\n walk(nodeChildren(item), key)\n }\n }\n\n walk(props.data ?? [], undefined)\n return map\n})\n\nconst pathLabelsByKey = computed(() => {\n const map = new Map<GrTreeKey, string[]>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined, parentPath: string[]) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n const label = nodeLabel(item)\n const path = [...parentPath, label]\n map.set(key, path)\n walk(nodeChildren(item), key, path)\n }\n }\n\n walk(props.data ?? [], undefined, [])\n return map\n})\n\nconst selectedKeys = computed<GrTreeKey[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n const v = props.modelValue\n if (v == null || Array.isArray(v))\n return []\n return [v]\n})\n\nconst selectedKeySet = computed(() => new Set(selectedKeys.value))\n\nconst selectedLabels = computed(() => {\n const map = labelByKey.value\n return selectedKeys.value.map((k) => map.get(k) ?? String(k))\n})\n\nconst selectedPathLabels = computed<string[] | undefined>(() => {\n if (props.multiple)\n return undefined\n\n const key = selectedKeys.value[0]\n if (key == null)\n return undefined\n\n const path = pathLabelsByKey.value.get(key)\n if (path?.length)\n return path\n\n const labels = selectedLabels.value\n return labels.length ? [labels[0]] : undefined\n})\n\nconst displayValue = computed(() => {\n const labels = selectedLabels.value\n if (labels.length === 0)\n return ''\n\n if (!props.multiple) {\n if (props.valueDisplay === 'path') {\n const path = selectedPathLabels.value\n if (path?.length)\n return path.join(' / ')\n }\n return labels[0]\n }\n\n if (labels.length === 1)\n return labels[0]\n return `${labels[0]} +${labels.length - 1}`\n})\n\nconst hasSelection = computed(() => selectedKeys.value.length > 0)\n\nconst resolvedFilterPlaceholder = computed(() => {\n return props.filterPlaceholder ?? t('gr.treeSelect.filterPlaceholder', 'Search…')\n})\n\nconst className = computed(() => {\n return grTreeSelectClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n })\n})\n\nconst panelClasses = computed(() => {\n return grTreeSelectPanelClass\n})\n\nfunction setOpen(next: boolean) {\n if (props.disabled)\n return\n\n if (open.value === next)\n return\n\n open.value = next\n emit('visibleChange', next)\n}\n\nfunction onClickOutside(): void {\n setOpen(false)\n}\n\nfunction openDropdown(): void {\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction toggleDropdown(): void {\n setOpen(!open.value)\n}\n\nfunction onTriggerPointerDown(): void {\n hadPointerDownOnTrigger = true\n if (typeof window !== 'undefined') {\n window.setTimeout(() => {\n hadPointerDownOnTrigger = false\n }, 0)\n }\n else {\n // SSR / no window: reset sync\n hadPointerDownOnTrigger = false\n }\n}\n\nfunction onTriggerFocus(): void {\n if (hadPointerDownOnTrigger)\n return\n openDropdown()\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n closeDropdown()\n nextTick(() => triggerEl.value?.focus())\n }\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined')\n return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen)\n document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen)\n return\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n // sync tree highlight\n if (!props.multiple) {\n const v = props.modelValue\n if (v != null && !Array.isArray(v))\n treeRef.value?.setCurrentKey(v)\n }\n\n // focus filter input\n if (props.filterable) {\n await nextTick()\n filterInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined')\n return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (props.multiple)\n return\n if (v == null || Array.isArray(v)) {\n treeRef.value?.setCurrentKey(undefined)\n return\n }\n treeRef.value?.setCurrentKey(v)\n },\n)\n\nwatch(\n filterValue,\n (v) => {\n treeRef.value?.filter(v)\n },\n)\n\nwatch(\n open,\n (isOpen) => {\n if (isOpen)\n return\n if (filterValue.value.trim().length > 0)\n filterValue.value = ''\n },\n)\n\nfunction emitModel(next: GrTreeSelectModelValue) {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction clear(): void {\n if (props.disabled)\n return\n\n const next: GrTreeSelectModelValue = props.multiple ? [] : null\n emitModel(next)\n emit('clear')\n if (!props.multiple)\n treeRef.value?.setCurrentKey(undefined)\n}\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (props.disabled)\n return\n\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openDropdown()\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n closeDropdown()\n }\n}\n\nfunction onNodeClick(data: T, node: GrTreeNode<T>): void {\n emit('nodeClick', data, node)\n\n if (props.disabled)\n return\n\n if (props.multiple) {\n const next = new Set(selectedKeySet.value)\n if (next.has(node.key))\n next.delete(node.key)\n else\n next.add(node.key)\n emitModel([...next])\n\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n return\n }\n\n emitModel(node.key)\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: onClickOutside, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-tree-select\n class=\"relative\"\n >\n <div class=\"relative\">\n <input\n ref=\"triggerEl\"\n data-testid=\"ds-tree-select-trigger\"\n data-ds-tree-select-trigger\n type=\"text\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n readonly\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] text-[var(--fg)] placeholder:text-[var(--muted-fg)] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"[className, $slots.value ? 'text-transparent placeholder:text-transparent' : '']\"\n @pointerdown=\"onTriggerPointerDown\"\n @click=\"toggleDropdown\"\n @focus=\"onTriggerFocus\"\n @keydown=\"onTriggerKeydown\"\n >\n\n <button\n v-if=\"clearable && hasSelection\"\n data-testid=\"ds-tree-select-clear\"\n data-ds-tree-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"t('gr.common.clear', 'Clear')\"\n @click.stop=\"clear\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" />\n </button>\n\n <span\n v-else\n data-testid=\"ds-tree-select-chevron\"\n data-ds-tree-select-chevron\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span\n class=\"i-lucide-chevron-down inline-block h-4 w-4 transition-transform duration-150\"\n :class=\"open ? 'rotate-180' : ''\"\n />\n </span>\n\n <div\n v-if=\"$slots.value\"\n class=\"absolute inset-y-0 left-3 right-9 flex items-center pointer-events-none\"\n >\n <slot\n name=\"value\"\n :value=\"modelValue\"\n :labels=\"selectedLabels\"\n :display-value=\"displayValue\"\n :path-labels=\"selectedPathLabels\"\n />\n </div>\n </div>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-tree-select-panel\"\n data-ds-tree-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"filterable\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"filterInputRef\"\n v-model=\"filterValue\"\n data-testid=\"ds-tree-select-filter\"\n data-ds-tree-select-filter\n type=\"search\"\n :inputmode=\"filterInputmode\"\n :placeholder=\"resolvedFilterPlaceholder\"\n size=\"sm\"\n />\n </div>\n\n <div\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n >\n <div v-if=\"(data?.length ?? 0) === 0\" class=\"px-3 py-2 text-[13px] text-[var(--muted-fg)]\">\n <slot name=\"empty\">\n {{ t('gr.treeSelect.empty', 'No data') }}\n </slot>\n </div>\n\n <GrTree\n v-else\n ref=\"treeRef\"\n :data=\"data\"\n :props=\"props.props\"\n :node-key=\"nodeKey as any\"\n :default-expanded-keys=\"defaultExpandedKeys\"\n :filter-node-method=\"filterNodeMethod\"\n :highlight-current=\"!multiple\"\n @node-click=\"onNodeClick\"\n >\n <template #default=\"{ node, data }\">\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"data\"\n :selected=\"selectedKeySet.has(node.key)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"selectedKeySet.has(node.key) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ node.label }}</span>\n </div>\n </slot>\n </template>\n </GrTree>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\" generic=\"T extends Record<string, any> = any\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport { vClickOutside } from '../../directives'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree, {\n type GrTreeInstance,\n type GrTreeKey,\n type GrTreeNode,\n} from '../GrTree'\nimport type { GrTreeSelectModelValue, GrTreeSelectProps } from './grTreeSelectTypes'\nimport { grTreeSelectClass, grTreeSelectPanelClass } from './grTreeSelectStyles'\n\nconst props = withDefaults(\n defineProps<GrTreeSelectProps<T>>(),\n {\n props: () => ({\n children: 'children',\n label: 'label',\n }),\n nodeKey: 'id' as any,\n defaultExpandedKeys: () => [],\n disabled: false,\n placeholder: undefined,\n size: 'md',\n invalid: false,\n state: 'default',\n multiple: false,\n clearable: false,\n valueDisplay: 'label',\n filterable: false,\n filterPlaceholder: undefined,\n filterInputmode: undefined,\n filterNodeMethod: undefined,\n closeOnSelect: undefined,\n dropdownMaxHeight: 320,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrTreeSelectModelValue): void\n (e: 'change', value: GrTreeSelectModelValue): void\n (e: 'visibleChange', visible: boolean): void\n (e: 'clear'): void\n (e: 'nodeClick', data: T, node: GrTreeNode<T>): void\n}>()\n\ndefineSlots<{\n /** Рендер значения внутри триггера (вместо дефолтного текста). */\n value?: (props: { value: GrTreeSelectModelValue; labels: string[]; displayValue: string; pathLabels?: string[] }) => any\n /** Рендер строки дерева. */\n node?: (props: { node: GrTreeNode<T>; data: T; selected: boolean }) => any\n /** Содержимое пустого состояния (когда нет данных). */\n empty?: () => any\n}>()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst triggerEl = ref<HTMLInputElement | null>(null)\nconst filterInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst treeRef = ref<GrTreeInstance | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst clickOutsideExclude = [() => panelEl.value]\n\nlet hadPointerDownOnTrigger = false\n\nconst open = ref(false)\nconst filterValue = ref('')\n\nconst panelStyle = ref<Record<string, string>>({\n left: '0px',\n top: '0px',\n width: '0px',\n zIndex: '2147483647',\n})\n\nfunction syncPanelPosition(): void {\n if (typeof window === 'undefined')\n return\n\n const root = rootEl.value\n if (!root)\n return\n\n const rect = root.getBoundingClientRect()\n\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: `${rect.width}px`,\n zIndex: '2147483647',\n }\n}\n\nfunction bindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined')\n return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nconst closeOnSelectResolved = computed(() => {\n return props.closeOnSelect ?? !props.multiple\n})\n\nfunction getChildrenKey() {\n return props.props.children ?? 'children'\n}\n\nfunction getLabelKey() {\n return props.props.label ?? 'label'\n}\n\nfunction nodeLabel(data: T): string {\n const key = getLabelKey() as keyof T\n const v = data[key]\n return v == null ? '' : String(v)\n}\n\nfunction nodeChildren(data: T): T[] {\n const key = getChildrenKey() as keyof T\n const v = data[key]\n return Array.isArray(v) ? (v as any) : []\n}\n\nfunction nodeKeyOf(data: T, index: number, parentKey: GrTreeKey | undefined): GrTreeKey {\n const keyName = props.nodeKey as keyof T\n const v = data[keyName]\n if (v != null)\n return v as GrTreeKey\n\n return parentKey == null ? index : `${String(parentKey)}:${index}`\n}\n\nconst labelByKey = computed(() => {\n const map = new Map<GrTreeKey, string>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n map.set(key, nodeLabel(item))\n walk(nodeChildren(item), key)\n }\n }\n\n walk(props.data ?? [], undefined)\n return map\n})\n\nconst pathLabelsByKey = computed(() => {\n const map = new Map<GrTreeKey, string[]>()\n\n const walk = (items: T[], parentKey: GrTreeKey | undefined, parentPath: string[]) => {\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const key = nodeKeyOf(item, i, parentKey)\n const label = nodeLabel(item)\n const path = [...parentPath, label]\n map.set(key, path)\n walk(nodeChildren(item), key, path)\n }\n }\n\n walk(props.data ?? [], undefined, [])\n return map\n})\n\nconst selectedKeys = computed<GrTreeKey[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n const v = props.modelValue\n if (v == null || Array.isArray(v))\n return []\n return [v]\n})\n\nconst selectedKeySet = computed(() => new Set(selectedKeys.value))\n\nconst selectedLabels = computed(() => {\n const map = labelByKey.value\n return selectedKeys.value.map((k) => map.get(k) ?? String(k))\n})\n\nconst selectedPathLabels = computed<string[] | undefined>(() => {\n if (props.multiple)\n return undefined\n\n const key = selectedKeys.value[0]\n if (key == null)\n return undefined\n\n const path = pathLabelsByKey.value.get(key)\n if (path?.length)\n return path\n\n const labels = selectedLabels.value\n return labels.length ? [labels[0]] : undefined\n})\n\nconst displayValue = computed(() => {\n const labels = selectedLabels.value\n if (labels.length === 0)\n return ''\n\n if (!props.multiple) {\n if (props.valueDisplay === 'path') {\n const path = selectedPathLabels.value\n if (path?.length)\n return path.join(' / ')\n }\n return labels[0]\n }\n\n if (labels.length === 1)\n return labels[0]\n return `${labels[0]} +${labels.length - 1}`\n})\n\nconst hasSelection = computed(() => selectedKeys.value.length > 0)\n\nconst resolvedFilterPlaceholder = computed(() => {\n return props.filterPlaceholder ?? t('gr.treeSelect.filterPlaceholder', 'Search…')\n})\n\nconst className = computed(() => {\n return grTreeSelectClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n })\n})\n\nconst panelClasses = computed(() => {\n return grTreeSelectPanelClass\n})\n\nfunction setOpen(next: boolean) {\n if (props.disabled)\n return\n\n if (open.value === next)\n return\n\n open.value = next\n emit('visibleChange', next)\n}\n\nfunction onClickOutside(): void {\n setOpen(false)\n}\n\nfunction openDropdown(): void {\n setOpen(true)\n}\n\nfunction closeDropdown(): void {\n setOpen(false)\n}\n\nfunction toggleDropdown(): void {\n setOpen(!open.value)\n}\n\nfunction onTriggerPointerDown(): void {\n hadPointerDownOnTrigger = true\n if (typeof window !== 'undefined') {\n window.setTimeout(() => {\n hadPointerDownOnTrigger = false\n }, 0)\n }\n else {\n // SSR / no window: reset sync\n hadPointerDownOnTrigger = false\n }\n}\n\nfunction onTriggerFocus(): void {\n if (hadPointerDownOnTrigger)\n return\n openDropdown()\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n closeDropdown()\n nextTick(() => triggerEl.value?.focus())\n }\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined')\n return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen)\n document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen)\n return\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n // sync tree highlight\n if (!props.multiple) {\n const v = props.modelValue\n if (v != null && !Array.isArray(v))\n treeRef.value?.setCurrentKey(v)\n }\n\n // focus filter input\n if (props.filterable) {\n await nextTick()\n filterInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined')\n return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (props.multiple)\n return\n if (v == null || Array.isArray(v)) {\n treeRef.value?.setCurrentKey(undefined)\n return\n }\n treeRef.value?.setCurrentKey(v)\n },\n)\n\nwatch(\n filterValue,\n (v) => {\n treeRef.value?.filter(v)\n },\n)\n\nwatch(\n open,\n (isOpen) => {\n if (isOpen)\n return\n if (filterValue.value.trim().length > 0)\n filterValue.value = ''\n },\n)\n\nfunction emitModel(next: GrTreeSelectModelValue) {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction clear(): void {\n if (props.disabled)\n return\n\n const next: GrTreeSelectModelValue = props.multiple ? [] : null\n emitModel(next)\n emit('clear')\n if (!props.multiple)\n treeRef.value?.setCurrentKey(undefined)\n}\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (props.disabled)\n return\n\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openDropdown()\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n closeDropdown()\n }\n}\n\nfunction onNodeClick(data: T, node: GrTreeNode<T>): void {\n emit('nodeClick', data, node)\n\n if (props.disabled)\n return\n\n if (props.multiple) {\n const next = new Set(selectedKeySet.value)\n if (next.has(node.key))\n next.delete(node.key)\n else\n next.add(node.key)\n emitModel([...next])\n\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n return\n }\n\n emitModel(node.key)\n if (closeOnSelectResolved.value) {\n closeDropdown()\n }\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n v-click-outside=\"{ handler: onClickOutside, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-tree-select\n class=\"relative\"\n >\n <div class=\"relative\">\n <input\n ref=\"triggerEl\"\n data-testid=\"ds-tree-select-trigger\"\n data-ds-tree-select-trigger\n type=\"text\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n readonly\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] text-[var(--fg)] placeholder:text-[var(--muted-fg)] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"[className, $slots.value ? 'text-transparent placeholder:text-transparent' : '']\"\n @pointerdown=\"onTriggerPointerDown\"\n @click=\"toggleDropdown\"\n @focus=\"onTriggerFocus\"\n @keydown=\"onTriggerKeydown\"\n >\n\n <button\n v-if=\"clearable && hasSelection\"\n data-testid=\"ds-tree-select-clear\"\n data-ds-tree-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"t('gr.common.clear', 'Clear')\"\n @click.stop=\"clear\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" />\n </button>\n\n <span\n v-else\n data-testid=\"ds-tree-select-chevron\"\n data-ds-tree-select-chevron\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span\n class=\"i-lucide-chevron-down inline-block h-4 w-4 transition-transform duration-150\"\n :class=\"open ? 'rotate-180' : ''\"\n />\n </span>\n\n <div\n v-if=\"$slots.value\"\n class=\"absolute inset-y-0 left-3 right-9 flex items-center pointer-events-none\"\n >\n <slot\n name=\"value\"\n :value=\"modelValue\"\n :labels=\"selectedLabels\"\n :display-value=\"displayValue\"\n :path-labels=\"selectedPathLabels\"\n />\n </div>\n </div>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"ds-tree-select-panel\"\n data-ds-tree-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"filterable\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"filterInputRef\"\n v-model=\"filterValue\"\n data-testid=\"ds-tree-select-filter\"\n data-ds-tree-select-filter\n type=\"search\"\n :inputmode=\"filterInputmode\"\n :placeholder=\"resolvedFilterPlaceholder\"\n size=\"sm\"\n />\n </div>\n\n <div\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n >\n <div v-if=\"(data?.length ?? 0) === 0\" class=\"px-3 py-2 text-[13px] text-[var(--muted-fg)]\">\n <slot name=\"empty\">\n {{ t('gr.treeSelect.empty', 'No data') }}\n </slot>\n </div>\n\n <GrTree\n v-else\n ref=\"treeRef\"\n :data=\"data\"\n :props=\"props.props\"\n :node-key=\"nodeKey as any\"\n :default-expanded-keys=\"defaultExpandedKeys\"\n :filter-node-method=\"filterNodeMethod\"\n :highlight-current=\"!multiple\"\n @node-click=\"onNodeClick\"\n >\n <template #default=\"{ node, data }\">\n <slot\n name=\"node\"\n :node=\"node\"\n :data=\"data\"\n :selected=\"selectedKeySet.has(node.key)\"\n >\n <div class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"selectedKeySet.has(node.key) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ node.label }}</span>\n </div>\n </slot>\n </template>\n </GrTree>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcA,MAAM,QAAQ;EA0Bd,MAAM,EAAE,MAAM,4BAA2B;EAEzC,MAAM,OAAO;EAiBb,MAAM,SAAS,IAAwB,KAAI;EAC3C,MAAM,YAAY,IAA6B,KAAI;EACnD,MAAM,iBAAiB,IAAyC,KAAI;EACpE,MAAM,UAAU,IAA2B,KAAI;EAC/C,MAAM,UAAU,IAAwB,KAAI;EAE5C,MAAM,sBAAsB,OAAO,QAAQ,MAAK;EAEhD,IAAI,0BAA0B;EAE9B,MAAM,OAAO,IAAI,MAAK;EACtB,MAAM,cAAc,IAAI,GAAE;EAE1B,MAAM,aAAa,IAA4B;GAC7C,MAAM;GACN,KAAK;GACL,OAAO;GACP,QAAQ;GACT,CAAA;EAED,SAAS,oBAA0B;AACjC,OAAI,OAAO,WAAW,YACpB;GAEF,MAAM,OAAO,OAAO;AACpB,OAAI,CAAC,KACH;GAEF,MAAM,OAAO,KAAK,uBAAsB;AAExC,cAAW,QAAQ;IACjB,MAAM,GAAG,KAAK,KAAK;IACnB,KAAK,GAAG,KAAK,SAAS,EAAE;IACxB,OAAO,GAAG,KAAK,MAAM;IACrB,QAAQ;IACV;;EAGF,SAAS,6BAAmC;AAC1C,OAAI,OAAO,WAAW,YACpB;AACF,UAAO,iBAAiB,UAAU,kBAAiB;AACnD,UAAO,iBAAiB,UAAU,mBAAmB,KAAI;;EAG3D,SAAS,+BAAqC;AAC5C,OAAI,OAAO,WAAW,YACpB;AACF,UAAO,oBAAoB,UAAU,kBAAiB;AACtD,UAAO,oBAAoB,UAAU,mBAAmB,KAAI;;EAG9D,MAAM,wBAAwB,eAAe;AAC3C,UAAO,MAAM,iBAAiB,CAAC,MAAM;IACtC;EAED,SAAS,iBAAiB;AACxB,UAAO,MAAM,MAAM,YAAY;;EAGjC,SAAS,cAAc;AACrB,UAAO,MAAM,MAAM,SAAS;;EAG9B,SAAS,UAAU,MAAiB;GAElC,MAAM,IAAI,KADE,aAAa;AAEzB,UAAO,KAAK,OAAO,KAAK,OAAO,EAAC;;EAGlC,SAAS,aAAa,MAAc;GAElC,MAAM,IAAI,KADE,gBAAgB;AAE5B,UAAO,MAAM,QAAQ,EAAE,GAAI,IAAY,EAAC;;EAG1C,SAAS,UAAU,MAAS,OAAe,WAA6C;GAEtF,MAAM,IAAI,KADM,MAAM;AAEtB,OAAI,KAAK,KACP,QAAO;AAET,UAAO,aAAa,OAAO,QAAQ,GAAG,OAAO,UAAU,CAAC,GAAG;;EAG7D,MAAM,aAAa,eAAe;GAChC,MAAM,sBAAM,IAAI,KAAuB;GAEvC,MAAM,QAAQ,OAAY,cAAqC;AAC7D,SAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;KACrC,MAAM,OAAO,MAAM;KACnB,MAAM,MAAM,UAAU,MAAM,GAAG,UAAS;AACxC,SAAI,IAAI,KAAK,UAAU,KAAK,CAAA;AAC5B,UAAK,aAAa,KAAK,EAAE,IAAG;;;AAIhC,QAAK,MAAM,QAAQ,EAAE,EAAE,KAAA,EAAS;AAChC,UAAO;IACR;EAED,MAAM,kBAAkB,eAAe;GACrC,MAAM,sBAAM,IAAI,KAAyB;GAEzC,MAAM,QAAQ,OAAY,WAAkC,eAAyB;AACnF,SAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;KACrC,MAAM,OAAO,MAAM;KACnB,MAAM,MAAM,UAAU,MAAM,GAAG,UAAS;KACxC,MAAM,QAAQ,UAAU,KAAI;KAC5B,MAAM,OAAO,CAAC,GAAG,YAAY,MAAK;AAClC,SAAI,IAAI,KAAK,KAAI;AACjB,UAAK,aAAa,KAAK,EAAE,KAAK,KAAI;;;AAItC,QAAK,MAAM,QAAQ,EAAE,EAAE,KAAA,GAAW,EAAE,CAAA;AACpC,UAAO;IACR;EAED,MAAM,eAAe,eAA4B;AAC/C,OAAI,MAAM,SACR,QAAO,MAAM,QAAQ,MAAM,WAAW,GAAG,MAAM,aAAa,EAAC;GAG/D,MAAM,IAAI,MAAM;AAChB,OAAI,KAAK,QAAQ,MAAM,QAAQ,EAAE,CAC/B,QAAO,EAAC;AACV,UAAO,CAAC,EAAC;IACV;EAED,MAAM,iBAAiB,eAAe,IAAI,IAAI,aAAa,MAAM,CAAA;EAEjE,MAAM,iBAAiB,eAAe;GACpC,MAAM,MAAM,WAAW;AACvB,UAAO,aAAa,MAAM,KAAK,MAAM,IAAI,IAAI,EAAE,IAAI,OAAO,EAAE,CAAA;IAC7D;EAED,MAAM,qBAAqB,eAAqC;AAC9D,OAAI,MAAM,SACR,QAAO,KAAA;GAET,MAAM,MAAM,aAAa,MAAM;AAC/B,OAAI,OAAO,KACT,QAAO,KAAA;GAET,MAAM,OAAO,gBAAgB,MAAM,IAAI,IAAG;AAC1C,OAAI,MAAM,OACR,QAAO;GAET,MAAM,SAAS,eAAe;AAC9B,UAAO,OAAO,SAAS,CAAC,OAAO,GAAG,GAAG,KAAA;IACtC;EAED,MAAM,eAAe,eAAe;GAClC,MAAM,SAAS,eAAe;AAC9B,OAAI,OAAO,WAAW,EACpB,QAAO;AAET,OAAI,CAAC,MAAM,UAAU;AACnB,QAAI,MAAM,iBAAiB,QAAQ;KACjC,MAAM,OAAO,mBAAmB;AAChC,SAAI,MAAM,OACR,QAAO,KAAK,KAAK,MAAK;;AAE1B,WAAO,OAAO;;AAGhB,OAAI,OAAO,WAAW,EACpB,QAAO,OAAO;AAChB,UAAO,GAAG,OAAO,GAAG,IAAI,OAAO,SAAS;IACzC;EAED,MAAM,eAAe,eAAe,aAAa,MAAM,SAAS,EAAC;EAEjE,MAAM,4BAA4B,eAAe;AAC/C,UAAO,MAAM,qBAAqB,EAAE,mCAAmC,UAAS;IACjF;EAED,MAAM,YAAY,eAAe;AAC/B,UAAO,kBAAkB;IACvB,MAAM,MAAM;IACZ,OAAO,MAAM;IACb,SAAS,MAAM;IAChB,CAAA;IACF;EAED,MAAM,eAAe,eAAe;AAClC,UAAO;IACR;EAED,SAAS,QAAQ,MAAe;AAC9B,OAAI,MAAM,SACR;AAEF,OAAI,KAAK,UAAU,KACjB;AAEF,QAAK,QAAQ;AACb,QAAK,iBAAiB,KAAI;;EAG5B,SAAS,iBAAuB;AAC9B,WAAQ,MAAK;;EAGf,SAAS,eAAqB;AAC5B,WAAQ,KAAI;;EAGd,SAAS,gBAAsB;AAC7B,WAAQ,MAAK;;EAGf,SAAS,iBAAuB;AAC9B,WAAQ,CAAC,KAAK,MAAK;;EAGrB,SAAS,uBAA6B;AACpC,6BAA0B;AAC1B,OAAI,OAAO,WAAW,YACpB,QAAO,iBAAiB;AACtB,8BAA0B;MACzB,EAAC;OAIJ,2BAA0B;;EAI9B,SAAS,iBAAuB;AAC9B,OAAI,wBACF;AACF,iBAAa;;EAGf,SAAS,cAAc,GAAwB;AAC7C,OAAI,EAAE,QAAQ,UAAU;AACtB,mBAAc;AACd,mBAAe,UAAU,OAAO,OAAO,CAAA;;;AAI3C,QACE,MACA,OAAO,WAAW;AAChB,OAAI,OAAO,aAAa,YACtB;AAEF,YAAS,oBAAoB,WAAW,cAAa;AACrD,iCAA6B;AAE7B,OAAI,OACF,UAAS,iBAAiB,WAAW,cAAa;AAEpD,OAAI,CAAC,OACH;AAEF,+BAA2B;AAC3B,SAAM,UAAS;AACf,sBAAkB;AAGlB,OAAI,CAAC,MAAM,UAAU;IACnB,MAAM,IAAI,MAAM;AAChB,QAAI,KAAK,QAAQ,CAAC,MAAM,QAAQ,EAAE,CAChC,SAAQ,OAAO,cAAc,EAAC;;AAIlC,OAAI,MAAM,YAAY;AACpB,UAAM,UAAS;AACf,mBAAe,OAAO,OAAM;;KAGhC,EAAE,WAAW,MAAM,CACrB;AAEA,oBAAkB;AAChB,OAAI,OAAO,aAAa,YACtB;AACF,YAAS,oBAAoB,WAAW,cAAa;AACrD,iCAA6B;IAC9B;AAED,cACQ,MAAM,aACX,MAAM;AACL,OAAI,MAAM,SACR;AACF,OAAI,KAAK,QAAQ,MAAM,QAAQ,EAAE,EAAE;AACjC,YAAQ,OAAO,cAAc,KAAA,EAAS;AACtC;;AAEF,WAAQ,OAAO,cAAc,EAAC;IAElC;AAEA,QACE,cACC,MAAM;AACL,WAAQ,OAAO,OAAO,EAAC;IAE3B;AAEA,QACE,OACC,WAAW;AACV,OAAI,OACF;AACF,OAAI,YAAY,MAAM,MAAM,CAAC,SAAS,EACpC,aAAY,QAAQ;IAE1B;EAEA,SAAS,UAAU,MAA8B;AAC/C,QAAK,qBAAqB,KAAI;AAC9B,QAAK,UAAU,KAAI;;EAGrB,SAAS,QAAc;AACrB,OAAI,MAAM,SACR;AAGF,aADqC,MAAM,WAAW,EAAE,GAAG,KAC7C;AACd,QAAK,QAAO;AACZ,OAAI,CAAC,MAAM,SACT,SAAQ,OAAO,cAAc,KAAA,EAAS;;EAG1C,SAAS,iBAAiB,GAAwB;AAChD,OAAI,MAAM,SACR;AAEF,OAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AAC/D,MAAE,gBAAe;AACjB,kBAAa;;AAEf,OAAI,EAAE,QAAQ,UAAU;AACtB,MAAE,gBAAe;AACjB,mBAAc;;;EAIlB,SAAS,YAAY,MAAS,MAA2B;AACvD,QAAK,aAAa,MAAM,KAAI;AAE5B,OAAI,MAAM,SACR;AAEF,OAAI,MAAM,UAAU;IAClB,MAAM,OAAO,IAAI,IAAI,eAAe,MAAK;AACzC,QAAI,KAAK,IAAI,KAAK,IAAI,CACpB,MAAK,OAAO,KAAK,IAAG;QAEpB,MAAK,IAAI,KAAK,IAAG;AACnB,cAAU,CAAC,GAAG,KAAK,CAAA;AAEnB,QAAI,sBAAsB,MACxB,gBAAc;AAEhB;;AAGF,aAAU,KAAK,IAAG;AAClB,OAAI,sBAAsB,MACxB,gBAAc;;;uCAMhB,mBA8IM,OAAA;aA7IA;IAAJ,KAAI;IAEJ,uBAAA;IACA,OAAM;OAEN,mBA2DM,OA3DN,YA2DM;IA1DJ,mBAmBC,SAAA;cAlBK;KAAJ,KAAI;KACJ,eAAY;KACZ,+BAAA;KACA,MAAK;KACJ,OAAO,aAAA;KACP,aAAa,QAAA;KACb,UAAU,QAAA;KACX,UAAA;KACA,MAAK;KACL,iBAAc;KACb,iBAAe,KAAA,QAAI,SAAA;KACnB,gBAAc,QAAA,UAAO,SAAY,KAAA;KAClC,OAAK,eAAA,CAAC,uPAAqP,CAClP,UAAA,OAAWA,KAAAA,OAAO,QAAK,kDAAA,GAAA,CAAA,CAAA;KAC/B,eAAa;KACb,SAAO;KACP,SAAO;KACP,WAAS;;IAIJ,QAAA,aAAa,aAAA,SAAA,WAAA,EADrB,mBAWS,UAAA;;KATP,eAAY;KACZ,6BAAA;KACA,MAAK;KACL,OAAM;KACL,UAAU,QAAA;KACV,cAAY,MAAA,EAAC,CAAA,mBAAA,QAAA;KACb,SAAK,cAAO,OAAK,CAAA,OAAA,CAAA;sCAElB,mBAAgD,QAAA,EAA1C,OAAM,mCAAiC,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,GAAA,WAAA,KAAA,WAAA,EAG/C,mBAUO,QAVP,YAUO,CAJL,mBAGE,QAAA,EAFA,OAAK,eAAA,CAAC,gFACE,KAAA,QAAI,eAAA,GAAA,CAAA,EAAA,EAAA,MAAA,EAAA,CAAA,CAAA;IAKRA,KAAAA,OAAO,SAAA,WAAA,EADf,mBAWM,OAXN,YAWM,CAPJ,WAME,KAAA,QAAA,SAAA;KAJC,OAAO,QAAA;KACP,QAAQ,eAAA;KACR,cAAe,aAAA;KACf,YAAa,mBAAA;;qBAKpB,YA0EW,UAAA,EA1ED,IAAG,QAAM,EAAA,CACjB,YAwEa,YAAA;IAvEX,sBAAmB;IACnB,oBAAiB;IACjB,kBAAe;IACf,sBAAmB;IACnB,oBAAiB;IACjB,kBAAe;;2BAiET,CAAA,eA/DN,mBA+DM,OAAA;cA7DA;KAAJ,KAAI;KACJ,eAAY;KACZ,6BAAA;KACA,OAAM;KACL,OAAK,eAAE,WAAA,MAAU;QAElB,mBAsDI,OAAA,EAtDE,OAAK,eAAE,aAAA,MAAY,EAAA,EAAA,CACd,QAAA,cAAA,WAAA,EAAX,mBAWM,OAXN,YAWM,CAVJ,YASE,iBAAA;cARI;KAAJ,KAAI;iBACK,YAAA;8EAAW,QAAA;KACpB,eAAY;KACZ,8BAAA;KACA,MAAK;KACJ,WAAW,QAAA;KACX,aAAa,0BAAA;KACd,MAAK;;;;;0CAIT,mBAuCM,OAAA;KAtCJ,OAAM;KACL,OAAK,eAAA,EAAA,WAAA,GAAkB,QAAA,kBAAiB,KAAA,CAAA;SAE7B,QAAA,MAAM,UAAM,OAAA,KAAA,WAAA,EAAxB,mBAIM,OAJN,YAIM,CAHJ,WAEO,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBADF,MAAA,EAAC,CAAA,uBAAA,UAAA,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,KAAA,WAAA,EAIR,YA4BS,MAAA,eAAA,EAAA;;cA1BH;KAAJ,KAAI;KACH,MAAM,QAAA;KACN,OAAO,MAAM;KACb,YAAU,QAAA;KACV,yBAAuB,QAAA;KACvB,sBAAoB,QAAA;KACpB,qBAAiB,CAAG,QAAA;KACR;;KAEF,SAAO,SAeT,EAfa,MAAM,WAAI,CAC9B,WAcO,KAAA,QAAA,QAAA;MAZE;MACA;MACN,UAAU,eAAA,MAAe,IAAI,KAAK,IAAG;cAUjC,CARL,mBAOM,OAPN,YAOM,CANJ,mBAIE,QAAA;MAHA,OAAK,eAAA,CAAC,iCACE,eAAA,MAAe,IAAI,KAAK,IAAG,GAAA,yCAAA,GAAA,CAAA;MACnC,eAAY;kBAEd,mBAA8C,QAA9C,YAA8C,gBAApB,KAAK,MAAK,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;;;;;;iCAvDtC,KAAA,MAAI,CAAA,CAAA,CAAA,CAAA;;;aA3EU;IAAc,SAAW,KAAA;IAAI,SAAW;IAAmB,CAAA,CAAA,CAAA"}
|