@feugene/granularity 0.9.2 → 0.9.4
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/grSelectStyles-pUt19VGy.js.map +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-Cn1X-sWS.js → GrImageViewer-CsvXQHM9.js} +203 -50
- package/dist/components/GrImageViewer/chunks/GrImageViewer-CsvXQHM9.js.map +1 -0
- package/dist/components/GrImageViewer/index.js +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-cD6rFO9W.js → GrPagination-BNDEqM8_.js} +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-cD6rFO9W.js.map → GrPagination-BNDEqM8_.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-W9lbAjRZ.js → GrSelect-CnHdbPlf.js} +108 -56
- package/dist/components/GrSelect/chunks/GrSelect-CnHdbPlf.js.map +1 -0
- package/dist/components/GrSelect/index.js +1 -1
- package/dist/index.js +3 -3
- package/dist/types/src/components/GrImageViewer/GrImageViewer.vue.d.ts +30 -0
- package/dist/types/src/components/GrSelect/GrSelect.vue.d.ts +5 -4
- package/dist/types/src/components/GrSelect/grSelectStyles.d.ts +7 -0
- package/dist/types/src/components/GrSelect/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/components/GrImageViewer/chunks/GrImageViewer-Cn1X-sWS.js.map +0 -1
- package/dist/components/GrSelect/chunks/GrSelect-W9lbAjRZ.js.map +0 -1
|
@@ -7,6 +7,13 @@ export type GrSelectOption = {
|
|
|
7
7
|
value: string;
|
|
8
8
|
label: string;
|
|
9
9
|
};
|
|
10
|
+
/** Группа опций: заголовок `label` + вложенные опции `options`. */
|
|
11
|
+
export type GrSelectOptionGroup = {
|
|
12
|
+
label: string;
|
|
13
|
+
options: GrSelectOption[];
|
|
14
|
+
};
|
|
15
|
+
/** Элемент списка опций: либо одиночная опция, либо группа опций. */
|
|
16
|
+
export type GrSelectOptionOrGroup = GrSelectOption | GrSelectOptionGroup;
|
|
10
17
|
export type GrSelectModelValue = string | string[];
|
|
11
18
|
export declare const defaultBaseClass = "w-full rounded-md border border-[var(--brd)] bg-[var(--bg)] text-[var(--fg)] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]";
|
|
12
19
|
export declare const linkBaseClass = "cursor-pointer inline-block w-auto align-baseline appearance-none bg-transparent border border-transparent px-0 py-0 rounded-[6px] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default } from './GrSelect.vue';
|
|
2
2
|
export { default as GrSelect } from './GrSelect.vue';
|
|
3
3
|
export { grSelectConfig } from './config';
|
|
4
|
-
export type { GrSelectModelValue, GrSelectOption, GrSelectOptionsView, GrSelectProps, GrSelectSize, GrSelectUnderline, GrSelectVariant, GrSelectView, } from './GrSelect.vue';
|
|
4
|
+
export type { GrSelectModelValue, GrSelectOption, GrSelectOptionGroup, GrSelectOptionOrGroup, GrSelectOptionsView, GrSelectProps, GrSelectSize, GrSelectUnderline, GrSelectVariant, GrSelectView, } from './GrSelect.vue';
|
|
5
5
|
export { grSelectSafelist } from './safelist';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feugene/granularity",
|
|
3
3
|
"description": "Granularity design system package with Vue 3 components, consumed via the `@feugene/unocss-preset-granular` preset.",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.4",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Evgeniy Fureev",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrImageViewer-Cn1X-sWS.js","names":["$slots"],"sources":["../../../../src/components/GrImageViewer/GrImageViewer.vue","../../../../src/components/GrImageViewer/GrImageViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\n/**\n * Usage:\n *\n * <GrImageViewer\n * v-model=\"open\"\n * :url-list=\"images\"\n * :initial-index=\"0\"\n * show-progress\n * hide-on-click-modal\n * />\n */\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n urlList: string[]\n initialIndex?: number\n zoomRate?: number\n minScale?: number\n maxScale?: number\n hideOnClickModal?: boolean\n closeOnPressEscape?: boolean\n showProgress?: boolean\n showZoomValue?: boolean\n zIndex?: number\n /** i18n: aria-label кнопки закрытия. */\n closeLabel?: string\n /** i18n: aria-label кнопки «предыдущее изображение». */\n prevLabel?: string\n /** i18n: aria-label кнопки «следующее изображение». */\n nextLabel?: string\n /** i18n: aria-label кнопки «увеличить». */\n zoomInLabel?: string\n /** i18n: aria-label кнопки «уменьшить». */\n zoomOutLabel?: string\n /** i18n: aria-label кнопки «сбросить масштаб». */\n resetZoomLabel?: string\n /** i18n: aria-label кнопки «повернуть влево». */\n rotateLeftLabel?: string\n /** i18n: aria-label кнопки «повернуть вправо». */\n rotateRightLabel?: string\n /** i18n: текст в пустом состоянии (нет изображений). */\n emptyText?: string\n }>(),\n {\n initialIndex: 0,\n zoomRate: 1.2,\n minScale: 0.5,\n maxScale: 5,\n hideOnClickModal: false,\n closeOnPressEscape: true,\n showProgress: false,\n showZoomValue: true,\n zIndex: undefined,\n closeLabel: 'Close image viewer',\n prevLabel: 'Previous image',\n nextLabel: 'Next image',\n zoomInLabel: 'Zoom in',\n zoomOutLabel: 'Zoom out',\n resetZoomLabel: 'Reset zoom',\n rotateLeftLabel: 'Rotate left',\n rotateRightLabel: 'Rotate right',\n emptyText: 'No image',\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'close'): void\n (e: 'switch', newIndex: number): void\n (e: 'rotate', deg: number): void\n}>()\n\ntype GrImageViewerToolbarActions = {\n close: () => void\n prev: () => void\n next: () => void\n zoomIn: () => void\n zoomOut: () => void\n reset: () => void\n rotateLeft: () => void\n rotateRight: () => void\n}\n\nconst DEFAULT_Z_INDEX = 2000\nconst TRAILING_ZERO_DECIMAL_RE = /\\.0$/\n\nconst open = computed(() => props.modelValue)\nconst total = computed(() => props.urlList.length)\nconst hasImages = computed(() => total.value > 0)\n\nconst currentIndex = ref(0)\nconst scale = ref(1)\nconst rotation = ref(0)\nconst offsetX = ref(0)\nconst offsetY = ref(0)\n\nconst currentUrl = computed(() => {\n if (!hasImages.value) {\n return undefined\n }\n\n return props.urlList[currentIndex.value]\n})\n\nconst displayIndex = computed(() => {\n if (!hasImages.value) {\n return 0\n }\n\n return currentIndex.value + 1\n})\n\nconst viewerStyle = computed(() => ({\n zIndex: String(Number.isFinite(props.zIndex) ? props.zIndex : DEFAULT_Z_INDEX),\n}))\n\nconst imageStyle = computed(() => ({\n transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value}) rotate(${rotation.value}deg)`,\n}))\n\nconst zoomValueText = computed(() => {\n const zoomPercent = scale.value * 100\n\n if (Number.isInteger(zoomPercent)) {\n return String(zoomPercent)\n }\n\n return zoomPercent.toFixed(1).replace(TRAILING_ZERO_DECIMAL_RE, '')\n})\n\nfunction normalizeIndex(value: number): number {\n if (!hasImages.value) {\n return 0\n }\n\n const normalized = Number.isFinite(value) ? Math.trunc(value) : 0\n\n return ((normalized % total.value) + total.value) % total.value\n}\n\nfunction resetTransform(): void {\n scale.value = 1\n rotation.value = 0\n offsetX.value = 0\n offsetY.value = 0\n}\n\nfunction setIndex(nextIndex: number, options?: { emitSwitch?: boolean }): void {\n const normalizedIndex = normalizeIndex(nextIndex)\n\n if (normalizedIndex === currentIndex.value) {\n return\n }\n\n currentIndex.value = normalizedIndex\n resetTransform()\n\n if (options?.emitSwitch !== false) {\n emit('switch', normalizedIndex)\n }\n}\n\nfunction syncIndexFromInitial(): void {\n if (!hasImages.value) {\n currentIndex.value = 0\n return\n }\n\n currentIndex.value = normalizeIndex(props.initialIndex)\n resetTransform()\n}\n\nfunction preloadAt(index: number): void {\n const url = props.urlList[index]\n\n if (!url) {\n return\n }\n\n const image = new Image()\n image.decoding = 'async'\n image.src = url\n}\n\nfunction closeViewer(): void {\n emit('update:modelValue', false)\n emit('close')\n}\n\nfunction prev(): void {\n setIndex(currentIndex.value - 1)\n}\n\nfunction next(): void {\n setIndex(currentIndex.value + 1)\n}\n\nfunction zoomIn(): void {\n scale.value = Math.min(props.maxScale, Number((scale.value * props.zoomRate).toFixed(4)))\n}\n\nfunction zoomOut(): void {\n scale.value = Math.max(props.minScale, Number((scale.value / props.zoomRate).toFixed(4)))\n}\n\nfunction rotateLeft(): void {\n rotation.value -= 90\n emit('rotate', rotation.value)\n}\n\nfunction rotateRight(): void {\n rotation.value += 90\n emit('rotate', rotation.value)\n}\n\nconst toolbarActions: GrImageViewerToolbarActions = {\n close: closeViewer,\n prev,\n next,\n zoomIn,\n zoomOut,\n reset: resetTransform,\n rotateLeft,\n rotateRight,\n}\n\nfunction onBackdropClick(): void {\n if (!props.hideOnClickModal) {\n return\n }\n\n closeViewer()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'Escape':\n if (!props.closeOnPressEscape) {\n return\n }\n\n event.preventDefault()\n closeViewer()\n return\n\n case 'ArrowLeft':\n event.preventDefault()\n prev()\n return\n\n case 'ArrowRight':\n event.preventDefault()\n next()\n return\n\n case '+':\n case '=':\n case 'Add':\n event.preventDefault()\n zoomIn()\n return\n\n case '-':\n case '_':\n case 'Subtract':\n event.preventDefault()\n zoomOut()\n return\n\n case '0':\n event.preventDefault()\n resetTransform()\n break\n\n default:\n }\n}\n\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (!isOpen) {\n return\n }\n\n syncIndexFromInitial()\n },\n { immediate: true },\n)\n\nwatch(\n () => props.initialIndex,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n)\n\nwatch(\n () => props.urlList,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n { deep: true },\n)\n\nwatch(\n currentIndex,\n (index) => {\n if (!hasImages.value || total.value < 2) {\n return\n }\n\n preloadAt(normalizeIndex(index - 1))\n preloadAt(normalizeIndex(index + 1))\n },\n { immediate: true },\n)\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"open\" as=\"template\">\n <Dialog\n as=\"div\"\n class=\"fixed inset-0\"\n :style=\"viewerStyle\"\n role=\"dialog\"\n aria-modal=\"true\"\n :static=\"true\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0 overflow-hidden\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-150 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-120 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-image-viewer-overlay\n class=\"absolute inset-0 bg-black/60 backdrop-blur-sm\"\n aria-hidden=\"true\"\n @click=\"onBackdropClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-180 ease-out\"\n enter-from=\"opacity-0 scale-98\"\n enter-to=\"opacity-100 scale-100\"\n leave=\"duration-130 ease-in\"\n leave-from=\"opacity-100 scale-100\"\n leave-to=\"opacity-0 scale-98\"\n >\n <DialogPanel\n data-ds-image-viewer-panel\n class=\"relative z-10 h-full w-full outline-none\"\n tabindex=\"-1\"\n >\n <div class=\"pointer-events-none absolute inset-x-0 top-0 z-30 px-3 py-3 sm:px-6\">\n <div class=\"pointer-events-auto flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-2\">\n <div\n v-if=\"showProgress\"\n data-ds-image-viewer-progress\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-600 text-white/95 sm:text-sm\"\n >\n {{ displayIndex }} / {{ total }}\n </div>\n\n <div\n v-if=\"showZoomValue\"\n data-ds-image-viewer-zoom-value\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-700 text-white/95 sm:text-sm\"\n >\n {{ zoomValueText }}%\n </div>\n </div>\n\n <button\n type=\"button\"\n data-ds-image-viewer-close\n :aria-label=\"closeLabel\"\n class=\"h-11 w-11 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"closeViewer\"\n >\n <span aria-hidden=\"true\" class=\"text-lg leading-none\">✕</span>\n </button>\n </div>\n </div>\n\n <div class=\"relative z-10 h-full w-full flex items-center justify-center px-16 py-16 sm:px-24 sm:py-20\">\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-prev\n :aria-label=\"prevLabel\"\n class=\"absolute left-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:left-6\"\n @click=\"prev\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">‹</span>\n </button>\n\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-next\n :aria-label=\"nextLabel\"\n class=\"absolute right-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:right-6\"\n @click=\"next\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">›</span>\n </button>\n\n <div class=\"h-full w-full flex items-center justify-center\">\n <img\n v-if=\"currentUrl\"\n data-ds-image-viewer-image\n :src=\"currentUrl\"\n alt=\"\"\n draggable=\"false\"\n class=\"max-h-full max-w-full select-none object-contain will-change-transform transition-transform duration-150 ease-out\"\n :style=\"imageStyle\"\n >\n\n <div\n v-else\n class=\"rounded-[var(--ds-radius-xl)] border border-white/20 bg-black/25 px-4 py-3 text-sm text-white/80\"\n >\n {{ emptyText }}\n </div>\n </div>\n </div>\n\n <div class=\"pointer-events-none absolute inset-x-0 bottom-0 z-30 px-3 pb-3 sm:px-6 sm:pb-6\">\n <div class=\"pointer-events-auto mx-auto w-full max-w-max\">\n <slot\n name=\"toolbar\"\n :index=\"currentIndex\"\n :display-index=\"displayIndex\"\n :total=\"total\"\n :scale=\"scale\"\n :rotation=\"rotation\"\n :actions=\"toolbarActions\"\n >\n <div class=\"rounded-full border border-white/20 bg-black/35 p-1 backdrop-blur-sm flex items-center gap-1\">\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-out\n :aria-label=\"zoomOutLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomOut\"\n >\n −\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-reset\n :aria-label=\"resetZoomLabel\"\n class=\"h-11 min-w-11 px-3 flex items-center justify-center rounded-full text-xs font-700 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"resetTransform\"\n >\n 100%\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-in\n :aria-label=\"zoomInLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomIn\"\n >\n +\n </button>\n\n <template v-if=\"$slots['toolbar-actions']\">\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <slot\n name=\"toolbar-actions\"\n :index=\"currentIndex\"\n :display-index=\"displayIndex\"\n :total=\"total\"\n :scale=\"scale\"\n :rotation=\"rotation\"\n :actions=\"toolbarActions\"\n />\n\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n </template>\n\n <div v-else class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-left\n :aria-label=\"rotateLeftLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateLeft\"\n >\n ↺\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-right\n :aria-label=\"rotateRightLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateRight\"\n >\n ↻\n </button>\n </div>\n </slot>\n </div>\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\n/**\n * Usage:\n *\n * <GrImageViewer\n * v-model=\"open\"\n * :url-list=\"images\"\n * :initial-index=\"0\"\n * show-progress\n * hide-on-click-modal\n * />\n */\nconst props = withDefaults(\n defineProps<{\n modelValue: boolean\n urlList: string[]\n initialIndex?: number\n zoomRate?: number\n minScale?: number\n maxScale?: number\n hideOnClickModal?: boolean\n closeOnPressEscape?: boolean\n showProgress?: boolean\n showZoomValue?: boolean\n zIndex?: number\n /** i18n: aria-label кнопки закрытия. */\n closeLabel?: string\n /** i18n: aria-label кнопки «предыдущее изображение». */\n prevLabel?: string\n /** i18n: aria-label кнопки «следующее изображение». */\n nextLabel?: string\n /** i18n: aria-label кнопки «увеличить». */\n zoomInLabel?: string\n /** i18n: aria-label кнопки «уменьшить». */\n zoomOutLabel?: string\n /** i18n: aria-label кнопки «сбросить масштаб». */\n resetZoomLabel?: string\n /** i18n: aria-label кнопки «повернуть влево». */\n rotateLeftLabel?: string\n /** i18n: aria-label кнопки «повернуть вправо». */\n rotateRightLabel?: string\n /** i18n: текст в пустом состоянии (нет изображений). */\n emptyText?: string\n }>(),\n {\n initialIndex: 0,\n zoomRate: 1.2,\n minScale: 0.5,\n maxScale: 5,\n hideOnClickModal: false,\n closeOnPressEscape: true,\n showProgress: false,\n showZoomValue: true,\n zIndex: undefined,\n closeLabel: 'Close image viewer',\n prevLabel: 'Previous image',\n nextLabel: 'Next image',\n zoomInLabel: 'Zoom in',\n zoomOutLabel: 'Zoom out',\n resetZoomLabel: 'Reset zoom',\n rotateLeftLabel: 'Rotate left',\n rotateRightLabel: 'Rotate right',\n emptyText: 'No image',\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'close'): void\n (e: 'switch', newIndex: number): void\n (e: 'rotate', deg: number): void\n}>()\n\ntype GrImageViewerToolbarActions = {\n close: () => void\n prev: () => void\n next: () => void\n zoomIn: () => void\n zoomOut: () => void\n reset: () => void\n rotateLeft: () => void\n rotateRight: () => void\n}\n\nconst DEFAULT_Z_INDEX = 2000\nconst TRAILING_ZERO_DECIMAL_RE = /\\.0$/\n\nconst open = computed(() => props.modelValue)\nconst total = computed(() => props.urlList.length)\nconst hasImages = computed(() => total.value > 0)\n\nconst currentIndex = ref(0)\nconst scale = ref(1)\nconst rotation = ref(0)\nconst offsetX = ref(0)\nconst offsetY = ref(0)\n\nconst currentUrl = computed(() => {\n if (!hasImages.value) {\n return undefined\n }\n\n return props.urlList[currentIndex.value]\n})\n\nconst displayIndex = computed(() => {\n if (!hasImages.value) {\n return 0\n }\n\n return currentIndex.value + 1\n})\n\nconst viewerStyle = computed(() => ({\n zIndex: String(Number.isFinite(props.zIndex) ? props.zIndex : DEFAULT_Z_INDEX),\n}))\n\nconst imageStyle = computed(() => ({\n transform: `translate3d(${offsetX.value}px, ${offsetY.value}px, 0) scale(${scale.value}) rotate(${rotation.value}deg)`,\n}))\n\nconst zoomValueText = computed(() => {\n const zoomPercent = scale.value * 100\n\n if (Number.isInteger(zoomPercent)) {\n return String(zoomPercent)\n }\n\n return zoomPercent.toFixed(1).replace(TRAILING_ZERO_DECIMAL_RE, '')\n})\n\nfunction normalizeIndex(value: number): number {\n if (!hasImages.value) {\n return 0\n }\n\n const normalized = Number.isFinite(value) ? Math.trunc(value) : 0\n\n return ((normalized % total.value) + total.value) % total.value\n}\n\nfunction resetTransform(): void {\n scale.value = 1\n rotation.value = 0\n offsetX.value = 0\n offsetY.value = 0\n}\n\nfunction setIndex(nextIndex: number, options?: { emitSwitch?: boolean }): void {\n const normalizedIndex = normalizeIndex(nextIndex)\n\n if (normalizedIndex === currentIndex.value) {\n return\n }\n\n currentIndex.value = normalizedIndex\n resetTransform()\n\n if (options?.emitSwitch !== false) {\n emit('switch', normalizedIndex)\n }\n}\n\nfunction syncIndexFromInitial(): void {\n if (!hasImages.value) {\n currentIndex.value = 0\n return\n }\n\n currentIndex.value = normalizeIndex(props.initialIndex)\n resetTransform()\n}\n\nfunction preloadAt(index: number): void {\n const url = props.urlList[index]\n\n if (!url) {\n return\n }\n\n const image = new Image()\n image.decoding = 'async'\n image.src = url\n}\n\nfunction closeViewer(): void {\n emit('update:modelValue', false)\n emit('close')\n}\n\nfunction prev(): void {\n setIndex(currentIndex.value - 1)\n}\n\nfunction next(): void {\n setIndex(currentIndex.value + 1)\n}\n\nfunction zoomIn(): void {\n scale.value = Math.min(props.maxScale, Number((scale.value * props.zoomRate).toFixed(4)))\n}\n\nfunction zoomOut(): void {\n scale.value = Math.max(props.minScale, Number((scale.value / props.zoomRate).toFixed(4)))\n}\n\nfunction rotateLeft(): void {\n rotation.value -= 90\n emit('rotate', rotation.value)\n}\n\nfunction rotateRight(): void {\n rotation.value += 90\n emit('rotate', rotation.value)\n}\n\nconst toolbarActions: GrImageViewerToolbarActions = {\n close: closeViewer,\n prev,\n next,\n zoomIn,\n zoomOut,\n reset: resetTransform,\n rotateLeft,\n rotateRight,\n}\n\nfunction onBackdropClick(): void {\n if (!props.hideOnClickModal) {\n return\n }\n\n closeViewer()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'Escape':\n if (!props.closeOnPressEscape) {\n return\n }\n\n event.preventDefault()\n closeViewer()\n return\n\n case 'ArrowLeft':\n event.preventDefault()\n prev()\n return\n\n case 'ArrowRight':\n event.preventDefault()\n next()\n return\n\n case '+':\n case '=':\n case 'Add':\n event.preventDefault()\n zoomIn()\n return\n\n case '-':\n case '_':\n case 'Subtract':\n event.preventDefault()\n zoomOut()\n return\n\n case '0':\n event.preventDefault()\n resetTransform()\n break\n\n default:\n }\n}\n\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (!isOpen) {\n return\n }\n\n syncIndexFromInitial()\n },\n { immediate: true },\n)\n\nwatch(\n () => props.initialIndex,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n)\n\nwatch(\n () => props.urlList,\n () => {\n if (!props.modelValue) {\n return\n }\n\n syncIndexFromInitial()\n },\n { deep: true },\n)\n\nwatch(\n currentIndex,\n (index) => {\n if (!hasImages.value || total.value < 2) {\n return\n }\n\n preloadAt(normalizeIndex(index - 1))\n preloadAt(normalizeIndex(index + 1))\n },\n { immediate: true },\n)\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"open\" as=\"template\">\n <Dialog\n as=\"div\"\n class=\"fixed inset-0\"\n :style=\"viewerStyle\"\n role=\"dialog\"\n aria-modal=\"true\"\n :static=\"true\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0 overflow-hidden\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-150 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-120 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-image-viewer-overlay\n class=\"absolute inset-0 bg-black/60 backdrop-blur-sm\"\n aria-hidden=\"true\"\n @click=\"onBackdropClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-180 ease-out\"\n enter-from=\"opacity-0 scale-98\"\n enter-to=\"opacity-100 scale-100\"\n leave=\"duration-130 ease-in\"\n leave-from=\"opacity-100 scale-100\"\n leave-to=\"opacity-0 scale-98\"\n >\n <DialogPanel\n data-ds-image-viewer-panel\n class=\"relative z-10 h-full w-full outline-none\"\n tabindex=\"-1\"\n >\n <div class=\"pointer-events-none absolute inset-x-0 top-0 z-30 px-3 py-3 sm:px-6\">\n <div class=\"pointer-events-auto flex items-center justify-between gap-3\">\n <div class=\"flex items-center gap-2\">\n <div\n v-if=\"showProgress\"\n data-ds-image-viewer-progress\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-600 text-white/95 sm:text-sm\"\n >\n {{ displayIndex }} / {{ total }}\n </div>\n\n <div\n v-if=\"showZoomValue\"\n data-ds-image-viewer-zoom-value\n class=\"rounded-full bg-black/35 px-3 py-1 text-xs font-700 text-white/95 sm:text-sm\"\n >\n {{ zoomValueText }}%\n </div>\n </div>\n\n <button\n type=\"button\"\n data-ds-image-viewer-close\n :aria-label=\"closeLabel\"\n class=\"h-11 w-11 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"closeViewer\"\n >\n <span aria-hidden=\"true\" class=\"text-lg leading-none\">✕</span>\n </button>\n </div>\n </div>\n\n <div class=\"relative z-10 h-full w-full flex items-center justify-center px-16 py-16 sm:px-24 sm:py-20\">\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-prev\n :aria-label=\"prevLabel\"\n class=\"absolute left-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:left-6\"\n @click=\"prev\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">‹</span>\n </button>\n\n <button\n v-if=\"total > 1\"\n type=\"button\"\n data-ds-image-viewer-next\n :aria-label=\"nextLabel\"\n class=\"absolute right-3 top-1/2 z-20 h-12 w-12 -translate-y-1/2 flex items-center justify-center rounded-full border border-white/20 bg-black/35 text-white transition-colors hover:bg-black/55 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 sm:right-6\"\n @click=\"next\"\n >\n <span aria-hidden=\"true\" class=\"text-xl leading-none\">›</span>\n </button>\n\n <div class=\"h-full w-full flex items-center justify-center\">\n <img\n v-if=\"currentUrl\"\n data-ds-image-viewer-image\n :src=\"currentUrl\"\n alt=\"\"\n draggable=\"false\"\n class=\"max-h-full max-w-full select-none object-contain will-change-transform transition-transform duration-150 ease-out\"\n :style=\"imageStyle\"\n >\n\n <div\n v-else\n class=\"rounded-[var(--ds-radius-xl)] border border-white/20 bg-black/25 px-4 py-3 text-sm text-white/80\"\n >\n {{ emptyText }}\n </div>\n </div>\n </div>\n\n <div class=\"pointer-events-none absolute inset-x-0 bottom-0 z-30 px-3 pb-3 sm:px-6 sm:pb-6\">\n <div class=\"pointer-events-auto mx-auto w-full max-w-max\">\n <slot\n name=\"toolbar\"\n :index=\"currentIndex\"\n :display-index=\"displayIndex\"\n :total=\"total\"\n :scale=\"scale\"\n :rotation=\"rotation\"\n :actions=\"toolbarActions\"\n >\n <div class=\"rounded-full border border-white/20 bg-black/35 p-1 backdrop-blur-sm flex items-center gap-1\">\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-out\n :aria-label=\"zoomOutLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomOut\"\n >\n −\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-reset\n :aria-label=\"resetZoomLabel\"\n class=\"h-11 min-w-11 px-3 flex items-center justify-center rounded-full text-xs font-700 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"resetTransform\"\n >\n 100%\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-zoom-in\n :aria-label=\"zoomInLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"zoomIn\"\n >\n +\n </button>\n\n <template v-if=\"$slots['toolbar-actions']\">\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <slot\n name=\"toolbar-actions\"\n :index=\"currentIndex\"\n :display-index=\"displayIndex\"\n :total=\"total\"\n :scale=\"scale\"\n :rotation=\"rotation\"\n :actions=\"toolbarActions\"\n />\n\n <div class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n </template>\n\n <div v-else class=\"mx-1 h-6 w-px bg-white/25\" aria-hidden=\"true\" />\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-left\n :aria-label=\"rotateLeftLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateLeft\"\n >\n ↺\n </button>\n\n <button\n type=\"button\"\n data-ds-image-viewer-rotate-right\n :aria-label=\"rotateRightLabel\"\n class=\"h-11 min-w-11 px-2 flex items-center justify-center rounded-full text-sm font-600 text-white transition-colors hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80\"\n @click=\"rotateRight\"\n >\n ↻\n </button>\n </div>\n </slot>\n </div>\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrSelect-W9lbAjRZ.js","names":[],"sources":["../../../../src/components/GrSelect/GrSelect.vue","../../../../src/components/GrSelect/GrSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного DS-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n options?: GrSelectOption[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: 'Add value…',\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: 'Clear',\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\nconst optionsResolved = computed(() => props.options ?? [])\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(optionsResolved.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return optionsResolved.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => optionsResolved.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return optionsResolved.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\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') return\n\n const root = rootEl.value\n if (!root) return\n\n const rect = root.getBoundingClientRect()\n\n // Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n // и без отдельной обработки панель тоже становилась шириной выбранной опции, из-за чего\n // её размер «прыгал» при выборе разных опций. Решение — отдать вычисление ширины браузеру:\n // `width: max-content` (по самому длинному ребёнку — `w-full`-кнопкам с `whitespace-nowrap`),\n // `min-width` равной ширине триггера (чтобы панель не была уже самого триггера).\n // Это чисто CSS-подход — без JS-замеров и ResizeObserver, максимально перформантный.\n if (props.view === 'link') {\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: 'max-content',\n minWidth: `${rect.width}px`,\n zIndex: '2147483647',\n }\n return\n }\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') return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') closeDropdown()\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen) {\n customValue.value = ''\n return\n }\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n if (props.allowCustomValue) {\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nconst visibleOptions = computed(() => {\n const base = optionsResolved.value\n\n const withCustom = (() => {\n if (!props.allowCustomValue) return base\n if (props.multiple) return base\n if (modelSingle.value === '') return base\n if (hasModelInOptions.value) return base\n return [{ value: modelSingle.value, label: modelSingle.value }, ...base]\n })()\n\n if (!props.allowCustomValue) return withCustom\n\n const q = customValue.value.trim().toLowerCase()\n if (!q) return withCustom\n\n return withCustom.filter((o) => {\n return o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q)\n })\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !optionsResolved.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !optionsResolved.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\nconst showNativeChevron = computed(() => {\n return props.optionsView === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !props.options.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && props.optionsView === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: props.optionsView,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"optionsView === 'native'\"\n data-ds-select\n :class=\"rootClass\"\n >\n <select\n data-ds-select-native\n :value=\"multiple ? modelMultiple : modelSingle\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" />\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\">\n {{ modelSingle }}\n </option>\n <option v-for=\"opt in options\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-ds-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"ds-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-select\n :class=\"rootClass\"\n >\n <button\n data-testid=\"ds-select-trigger\"\n data-ds-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"ds-select-chevron\"\n class=\"shrink-0 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"ds-select-clear\"\n data-ds-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=\"clearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\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-select-panel\"\n data-ds-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"ds-select-custom-input\"\n type=\"text\"\n :placeholder=\"customValuePlaceholder\"\n size=\"sm\"\n @keydown.enter.prevent=\"addCustom\"\n />\n </div>\n\n <div\n data-ds-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"ds-select-add-option\"\n data-ds-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n Add “{{ customValue.trim() }}”\n </button>\n\n <button\n v-for=\"opt in visibleOptions\"\n :key=\"opt.value\"\n data-ds-select-option\n type=\"button\"\n role=\"option\"\n :aria-selected=\"isSelected(opt.value) ? 'true' : 'false'\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"toggleValue(opt.value)\"\n >\n <slot name=\"option\" :option=\"opt\" :selected=\"isSelected(opt.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(opt.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onUnmounted, ref, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного DS-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n options?: GrSelectOption[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: 'Add value…',\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: 'Clear',\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\nconst optionsResolved = computed(() => props.options ?? [])\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(optionsResolved.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return optionsResolved.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => optionsResolved.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return optionsResolved.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\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') return\n\n const root = rootEl.value\n if (!root) return\n\n const rect = root.getBoundingClientRect()\n\n // Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n // и без отдельной обработки панель тоже становилась шириной выбранной опции, из-за чего\n // её размер «прыгал» при выборе разных опций. Решение — отдать вычисление ширины браузеру:\n // `width: max-content` (по самому длинному ребёнку — `w-full`-кнопкам с `whitespace-nowrap`),\n // `min-width` равной ширине триггера (чтобы панель не была уже самого триггера).\n // Это чисто CSS-подход — без JS-замеров и ResizeObserver, максимально перформантный.\n if (props.view === 'link') {\n panelStyle.value = {\n left: `${rect.left}px`,\n top: `${rect.bottom + 8}px`,\n width: 'max-content',\n minWidth: `${rect.width}px`,\n zIndex: '2147483647',\n }\n return\n }\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') return\n window.addEventListener('resize', syncPanelPosition)\n window.addEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction unbindPanelPositionListeners(): void {\n if (typeof window === 'undefined') return\n window.removeEventListener('resize', syncPanelPosition)\n window.removeEventListener('scroll', syncPanelPosition, true)\n}\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') closeDropdown()\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n\n if (!isOpen) {\n customValue.value = ''\n return\n }\n\n bindPanelPositionListeners()\n await nextTick()\n syncPanelPosition()\n\n if (props.allowCustomValue) {\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n unbindPanelPositionListeners()\n})\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nconst visibleOptions = computed(() => {\n const base = optionsResolved.value\n\n const withCustom = (() => {\n if (!props.allowCustomValue) return base\n if (props.multiple) return base\n if (modelSingle.value === '') return base\n if (hasModelInOptions.value) return base\n return [{ value: modelSingle.value, label: modelSingle.value }, ...base]\n })()\n\n if (!props.allowCustomValue) return withCustom\n\n const q = customValue.value.trim().toLowerCase()\n if (!q) return withCustom\n\n return withCustom.filter((o) => {\n return o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q)\n })\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (props.optionsView !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !optionsResolved.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !optionsResolved.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\nconst showNativeChevron = computed(() => {\n return props.optionsView === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !props.options.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (props.optionsView !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && props.optionsView === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: props.optionsView,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"optionsView === 'native'\"\n data-ds-select\n :class=\"rootClass\"\n >\n <select\n data-ds-select-native\n :value=\"multiple ? modelMultiple : modelSingle\"\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" />\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\">\n {{ modelSingle }}\n </option>\n <option v-for=\"opt in options\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-ds-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"ds-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-ds-select\n :class=\"rootClass\"\n >\n <button\n data-testid=\"ds-select-trigger\"\n data-ds-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n role=\"combobox\"\n aria-readonly=\"true\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"ds-select-chevron\"\n class=\"shrink-0 text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down inline-block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"ds-select-clear\"\n data-ds-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=\"clearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\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-select-panel\"\n data-ds-select-panel\n class=\"fixed w-full\"\n :style=\"panelStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"ds-select-custom-input\"\n type=\"text\"\n :placeholder=\"customValuePlaceholder\"\n size=\"sm\"\n @keydown.enter.prevent=\"addCustom\"\n />\n </div>\n\n <div\n data-ds-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"ds-select-add-option\"\n data-ds-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n Add “{{ customValue.trim() }}”\n </button>\n\n <button\n v-for=\"opt in visibleOptions\"\n :key=\"opt.value\"\n data-ds-select-option\n type=\"button\"\n role=\"option\"\n :aria-selected=\"isSelected(opt.value) ? 'true' : 'false'\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"toggleValue(opt.value)\"\n >\n <slot name=\"option\" :option=\"opt\" :selected=\"isSelected(opt.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(opt.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </span>\n </slot>\n </button>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":"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"}
|