@feugene/granularity 0.5.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{adapter-K6zZTzBi.js.map → adapter-CRyH4YC8.js.map} +1 -1
- package/dist/chunks/{config-Cgk5ti-b.js.map → config-3SftO0uG.js.map} +1 -1
- package/dist/chunks/{config-BN-B2K4r.js.map → config-B44QVIge.js.map} +1 -1
- package/dist/chunks/{config-C-GVBa19.js.map → config-B8rC_K97.js.map} +1 -1
- package/dist/chunks/{config-DEv4m-ZP.js.map → config-BXffa3ta.js.map} +1 -1
- package/dist/chunks/{config-CUgvsjZx.js.map → config-BhvNm4Pp.js.map} +1 -1
- package/dist/chunks/{config-BNOf_qjD.js.map → config-C0b_3LJV.js.map} +1 -1
- package/dist/chunks/config-C0rHS1-l.js.map +1 -0
- package/dist/chunks/{config-fCcRwjIB.js.map → config-C4qAA0Sb.js.map} +1 -1
- package/dist/chunks/{config-XwI6t8cn.js.map → config-C5TN9J2Z.js.map} +1 -1
- package/dist/chunks/config-CVnluhKO.js.map +1 -0
- package/dist/chunks/{config-DUtUkSzc.js.map → config-CjEplP6D.js.map} +1 -1
- package/dist/chunks/{config-D19pyP4D.js.map → config-CtomKf-k.js.map} +1 -1
- package/dist/chunks/{config-BvuQUBtZ.js.map → config-CxCAfXZ1.js.map} +1 -1
- package/dist/chunks/{config-BDiLiqSk.js.map → config-D1DI3roo.js.map} +1 -1
- package/dist/chunks/{config-CgjOJ3aF.js.map → config-D27BL0H9.js.map} +1 -1
- package/dist/chunks/config-D32znLtV2.js.map +1 -0
- package/dist/chunks/{config-YSOeh6cy.js.map → config-D6Af6WZw.js.map} +1 -1
- package/dist/chunks/{config-BulF0Bf-.js.map → config-D98m8iyz.js.map} +1 -1
- package/dist/chunks/config-DDLoUczC.js.map +1 -0
- package/dist/chunks/{config-CFcv0hjb.js.map → config-DQOSlWvp.js.map} +1 -1
- package/dist/chunks/{config-ndXqsz5A.js.map → config-DbqFk-IW.js.map} +1 -1
- package/dist/chunks/{config-CdHY0Z7L.js.map → config-DnsISjD8.js.map} +1 -1
- package/dist/chunks/{config-BFZN9-vt.js.map → config-DudL02ag.js.map} +1 -1
- package/dist/chunks/{config-DgRJlF00.js.map → config-Dusr9jcE.js.map} +1 -1
- package/dist/chunks/{config-CWvHiaqr2.js.map → config-DxqMXzNV2.js.map} +1 -1
- package/dist/chunks/config-Mp9DJ9Mq2.js.map +1 -0
- package/dist/chunks/{config-R0jaXatX.js → config-XoANT5mU.js} +1 -1
- package/dist/chunks/{config-R0jaXatX.js.map → config-XoANT5mU.js.map} +1 -1
- package/dist/chunks/config-ZJVOVgqR.js +22 -0
- package/dist/chunks/config-ZJVOVgqR.js.map +1 -0
- package/dist/chunks/{config-CM7nbATU2.js.map → config-rxFL-6yF2.js.map} +1 -1
- package/dist/chunks/{config-bpQYM5O-.js.map → config-wo-ncgTH.js.map} +1 -1
- package/dist/chunks/{en-BeJa78lP.js.map → en-BMjJZttK.js.map} +1 -1
- package/dist/chunks/{es-BPqm7rLm.js → es-Cu_RlgIr.js} +3 -3
- package/dist/chunks/{es-BPqm7rLm.js.map → es-Cu_RlgIr.js.map} +1 -1
- package/dist/chunks/{es-axYOwwWD.js.map → es-EYCf8G_1.js.map} +1 -1
- package/dist/chunks/{grSelectStyles-daEeDlBn.js.map → grSelectStyles-DTAYNzxn.js.map} +1 -1
- package/dist/chunks/grStyle-5EMYYctb.js +19 -0
- package/dist/chunks/grStyle-5EMYYctb.js.map +1 -0
- package/dist/chunks/{granularityI18n-GCqUz_lo.js → granularityI18n-Dm4DuCS9.js} +1 -1
- package/dist/chunks/{granularityI18n-GCqUz_lo.js.map → granularityI18n-Dm4DuCS9.js.map} +1 -1
- package/dist/chunks/{ru-Dqh-2C00.js.map → ru-DKswMSdd.js.map} +1 -1
- package/dist/chunks/{shared-CyCPkimZ.js → shared-DO3Sr5FJ.js} +30 -30
- package/dist/chunks/{shared-CyCPkimZ.js.map → shared-DO3Sr5FJ.js.map} +1 -1
- package/dist/components/GrFileUpload/chunks/{GrFileUpload-u01j9S0t.js → GrFileUpload-DYRERhI2.js} +258 -37
- package/dist/components/GrFileUpload/chunks/GrFileUpload-DYRERhI2.js.map +1 -0
- package/dist/components/GrFileUpload/index.js +3 -3
- package/dist/components/GrFormField/chunks/{GrFormField-C6PfAs4u.js.map → GrFormField-C1oWTT_0.js.map} +1 -1
- package/dist/components/GrFormField/index.js +2 -2
- package/dist/components/GrFormFile/chunks/{GrFormFile-DdM8Bc2B.js.map → GrFormFile-deDPicM2.js.map} +1 -1
- package/dist/components/GrFormFile/index.js +2 -2
- package/dist/components/GrFormSection/chunks/{GrFormSection-5djEaQZQ.js.map → GrFormSection-CWBk2EUm.js.map} +1 -1
- package/dist/components/GrFormSection/index.js +2 -2
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-CjSnohW7.js.map → GrImageViewer-Cpj5zyZQ.js.map} +1 -1
- package/dist/components/GrImageViewer/index.js +2 -2
- package/dist/components/GrInput/chunks/{GrInput-ghxaTf6d.js.map → GrInput-CfO2RsM-.js.map} +1 -1
- package/dist/components/GrInput/index.js +2 -2
- package/dist/components/GrInputTag/chunks/{GrInputTag-B0x4U0Yk.js → GrInputTag-G_VYJPF9.js} +1 -1
- package/dist/components/GrInputTag/chunks/{GrInputTag-B0x4U0Yk.js.map → GrInputTag-G_VYJPF9.js.map} +1 -1
- package/dist/components/GrInputTag/index.js +2 -2
- package/dist/components/GrLink/chunks/{GrLink-DvvNkP0M.js → GrLink-BLPigqij.js} +1 -1
- package/dist/components/GrLink/chunks/{GrLink-DvvNkP0M.js.map → GrLink-BLPigqij.js.map} +1 -1
- package/dist/components/GrLink/index.js +2 -2
- package/dist/components/GrList/chunks/{GrList--Ib4zLNw.js.map → GrList-BPjQf-72.js.map} +1 -1
- package/dist/components/GrList/index.js +2 -2
- package/dist/components/GrLoading/index.js +1 -1
- package/dist/components/GrModal/index.js +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-nIyuiVj1.js → GrNavbar-BDIKAYfN.js} +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-nIyuiVj1.js.map → GrNavbar-BDIKAYfN.js.map} +1 -1
- package/dist/components/GrNavbar/index.js +2 -2
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-B0mPLjdv.js → GrNumberInput-ECtFCIKV.js} +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-B0mPLjdv.js.map → GrNumberInput-ECtFCIKV.js.map} +1 -1
- package/dist/components/GrNumberInput/index.js +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-C_rxGwfY.js → GrPagination-BZlH-uEd.js} +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-C_rxGwfY.js.map → GrPagination-BZlH-uEd.js.map} +1 -1
- package/dist/components/GrPagination/index.js +2 -2
- package/dist/components/GrProgressBar/chunks/{GrProgressBar-MvMnzsJl.js → GrProgressBar-hQQK8cYh.js} +1 -1
- package/dist/components/GrProgressBar/chunks/{GrProgressBar-MvMnzsJl.js.map → GrProgressBar-hQQK8cYh.js.map} +1 -1
- package/dist/components/GrProgressBar/index.js +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-DKhbN6ji.js → GrPromptDialog-CUMW6_58.js} +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-DKhbN6ji.js.map → GrPromptDialog-CUMW6_58.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +2 -2
- package/dist/components/GrRadio/chunks/{GrRadio-BxBs3WHq.js → GrRadio-B_t_ZNVl.js} +1 -1
- package/dist/components/GrRadio/chunks/{GrRadio-BxBs3WHq.js.map → GrRadio-B_t_ZNVl.js.map} +1 -1
- package/dist/components/GrRadio/index.js +2 -2
- package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-CEAfJQ6A.js → GrRadioGroup-CVlgpUBr.js} +1 -1
- package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-CEAfJQ6A.js.map → GrRadioGroup-CVlgpUBr.js.map} +1 -1
- package/dist/components/GrRadioGroup/index.js +2 -2
- package/dist/components/GrSegmented/chunks/{GrSegmented-Bwn2qOY4.js → GrSegmented-BPl1koHc.js} +1 -1
- package/dist/components/GrSegmented/chunks/{GrSegmented-Bwn2qOY4.js.map → GrSegmented-BPl1koHc.js.map} +1 -1
- package/dist/components/GrSegmented/index.js +2 -2
- package/dist/components/GrSelect/chunks/{GrSelect-BMMT5MrT.js → GrSelect-C3NAfTqL.js} +2 -2
- package/dist/components/GrSelect/chunks/{GrSelect-BMMT5MrT.js.map → GrSelect-C3NAfTqL.js.map} +1 -1
- package/dist/components/GrSelect/index.js +2 -2
- package/dist/components/GrSidebar/chunks/{GrSidebar-CSZ3RpFB.js.map → GrSidebar-DVucgcfV.js.map} +1 -1
- package/dist/components/GrSidebar/index.js +2 -2
- package/dist/components/GrSkeleton/chunks/{GrSkeleton-DKXkqWDa.js.map → GrSkeleton-CDTvCSoH.js.map} +1 -1
- package/dist/components/GrSkeleton/index.js +2 -2
- package/dist/components/GrSwitch/chunks/{GrSwitch-CdBxwV3k.js → GrSwitch-Bnamgqb2.js} +1 -1
- package/dist/components/GrSwitch/chunks/{GrSwitch-CdBxwV3k.js.map → GrSwitch-Bnamgqb2.js.map} +1 -1
- package/dist/components/GrSwitch/index.js +2 -2
- package/dist/components/GrTable/index.js +1 -1
- package/dist/components/GrTabs/chunks/{GrTabs-BpF8AAUz.js.map → GrTabs-CiGmcmvm.js.map} +1 -1
- package/dist/components/GrTabs/index.js +2 -2
- package/dist/components/GrTextarea/chunks/{GrTextarea-DFS9ZkrA.js → GrTextarea-VBSc6A7D.js} +1 -1
- package/dist/components/GrTextarea/chunks/{GrTextarea-DFS9ZkrA.js.map → GrTextarea-VBSc6A7D.js.map} +1 -1
- package/dist/components/GrTextarea/index.js +2 -2
- package/dist/components/GrToaster/chunks/{GrToaster-3Mf0Moxt.js → GrToaster-I1e-zCpf.js} +1 -1
- package/dist/components/GrToaster/chunks/{GrToaster-3Mf0Moxt.js.map → GrToaster-I1e-zCpf.js.map} +1 -1
- package/dist/components/GrToaster/index.js +2 -2
- package/dist/components/GrTooltip/chunks/{GrTooltip-BvmsukLW.js.map → GrTooltip-CrV1gI_c.js.map} +1 -1
- package/dist/components/GrTooltip/index.js +2 -2
- package/dist/components/GrTree/chunks/{GrTree-C2kN-O4Q.js.map → GrTree-Dlev9oBd.js.map} +1 -1
- package/dist/components/GrTree/index.js +2 -2
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-N-oPAOFj.js → GrTreeSelect-n0CZT4Ic.js} +4 -4
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-N-oPAOFj.js.map → GrTreeSelect-n0CZT4Ic.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +2 -2
- package/dist/directives/dropzone.js +17 -4
- package/dist/directives/dropzone.js.map +1 -1
- package/dist/directives/loading.js +15 -0
- package/dist/directives/loading.js.map +1 -1
- package/dist/granular-provider-node.js +2 -2
- package/dist/granular-provider.js +1 -1
- package/dist/i18n/all.js +1 -1
- package/dist/i18n/index.js +2 -2
- package/dist/index.js +59 -58
- package/dist/index.js.map +1 -1
- package/dist/types/src/components/GrFileUpload/GrFileUpload.vue.d.ts +58 -5
- package/dist/types/src/components/GrFileUpload/index.d.ts +4 -0
- package/dist/types/src/components/GrFileUpload/uploadState.d.ts +38 -0
- package/dist/types/src/components/GrFileUpload/uploadViaXhr.d.ts +33 -0
- package/dist/types/src/components/GrImageViewer/GrImageViewer.vue.d.ts +1 -1
- package/dist/types/src/components/GrInput/GrInput.vue.d.ts +1 -1
- package/dist/types/src/components/GrNumberInput/GrNumberInput.vue.d.ts +1 -1
- package/dist/types/src/components/GrTextarea/GrTextarea.vue.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunks/config-BGmiXpR82.js.map +0 -1
- package/dist/chunks/config-CA9XwYnZ2.js.map +0 -1
- package/dist/chunks/config-CsMIhZfM.js.map +0 -1
- package/dist/chunks/config-CwLoozAz2.js.map +0 -1
- package/dist/chunks/config-DCQO6H6h.js.map +0 -1
- package/dist/chunks/config-Da90XGsi.js +0 -38
- package/dist/chunks/config-Da90XGsi.js.map +0 -1
- package/dist/components/GrFileUpload/chunks/GrFileUpload-u01j9S0t.js.map +0 -1
- /package/dist/chunks/{adapter-K6zZTzBi.js → adapter-CRyH4YC8.js} +0 -0
- /package/dist/chunks/{config-Cgk5ti-b.js → config-3SftO0uG.js} +0 -0
- /package/dist/chunks/{config-BN-B2K4r.js → config-B44QVIge.js} +0 -0
- /package/dist/chunks/{config-C-GVBa19.js → config-B8rC_K97.js} +0 -0
- /package/dist/chunks/{config-DEv4m-ZP.js → config-BXffa3ta.js} +0 -0
- /package/dist/chunks/{config-CUgvsjZx.js → config-BhvNm4Pp.js} +0 -0
- /package/dist/chunks/{config-BNOf_qjD.js → config-C0b_3LJV.js} +0 -0
- /package/dist/chunks/{config-CA9XwYnZ2.js → config-C0rHS1-l.js} +0 -0
- /package/dist/chunks/{config-fCcRwjIB.js → config-C4qAA0Sb.js} +0 -0
- /package/dist/chunks/{config-XwI6t8cn.js → config-C5TN9J2Z.js} +0 -0
- /package/dist/chunks/{config-BGmiXpR82.js → config-CVnluhKO.js} +0 -0
- /package/dist/chunks/{config-DUtUkSzc.js → config-CjEplP6D.js} +0 -0
- /package/dist/chunks/{config-D19pyP4D.js → config-CtomKf-k.js} +0 -0
- /package/dist/chunks/{config-BvuQUBtZ.js → config-CxCAfXZ1.js} +0 -0
- /package/dist/chunks/{config-BDiLiqSk.js → config-D1DI3roo.js} +0 -0
- /package/dist/chunks/{config-CgjOJ3aF.js → config-D27BL0H9.js} +0 -0
- /package/dist/chunks/{config-DCQO6H6h.js → config-D32znLtV2.js} +0 -0
- /package/dist/chunks/{config-YSOeh6cy.js → config-D6Af6WZw.js} +0 -0
- /package/dist/chunks/{config-BulF0Bf-.js → config-D98m8iyz.js} +0 -0
- /package/dist/chunks/{config-CwLoozAz2.js → config-DDLoUczC.js} +0 -0
- /package/dist/chunks/{config-CFcv0hjb.js → config-DQOSlWvp.js} +0 -0
- /package/dist/chunks/{config-ndXqsz5A.js → config-DbqFk-IW.js} +0 -0
- /package/dist/chunks/{config-CdHY0Z7L.js → config-DnsISjD8.js} +0 -0
- /package/dist/chunks/{config-BFZN9-vt.js → config-DudL02ag.js} +0 -0
- /package/dist/chunks/{config-DgRJlF00.js → config-Dusr9jcE.js} +0 -0
- /package/dist/chunks/{config-CWvHiaqr2.js → config-DxqMXzNV2.js} +0 -0
- /package/dist/chunks/{config-CsMIhZfM.js → config-Mp9DJ9Mq2.js} +0 -0
- /package/dist/chunks/{config-CM7nbATU2.js → config-rxFL-6yF2.js} +0 -0
- /package/dist/chunks/{config-bpQYM5O-.js → config-wo-ncgTH.js} +0 -0
- /package/dist/chunks/{en-BeJa78lP.js → en-BMjJZttK.js} +0 -0
- /package/dist/chunks/{es-axYOwwWD.js → es-EYCf8G_1.js} +0 -0
- /package/dist/chunks/{grSelectStyles-daEeDlBn.js → grSelectStyles-DTAYNzxn.js} +0 -0
- /package/dist/chunks/{ru-Dqh-2C00.js → ru-DKswMSdd.js} +0 -0
- /package/dist/components/GrFormField/chunks/{GrFormField-C6PfAs4u.js → GrFormField-C1oWTT_0.js} +0 -0
- /package/dist/components/GrFormFile/chunks/{GrFormFile-DdM8Bc2B.js → GrFormFile-deDPicM2.js} +0 -0
- /package/dist/components/GrFormSection/chunks/{GrFormSection-5djEaQZQ.js → GrFormSection-CWBk2EUm.js} +0 -0
- /package/dist/components/GrImageViewer/chunks/{GrImageViewer-CjSnohW7.js → GrImageViewer-Cpj5zyZQ.js} +0 -0
- /package/dist/components/GrInput/chunks/{GrInput-ghxaTf6d.js → GrInput-CfO2RsM-.js} +0 -0
- /package/dist/components/GrList/chunks/{GrList--Ib4zLNw.js → GrList-BPjQf-72.js} +0 -0
- /package/dist/components/GrSidebar/chunks/{GrSidebar-CSZ3RpFB.js → GrSidebar-DVucgcfV.js} +0 -0
- /package/dist/components/GrSkeleton/chunks/{GrSkeleton-DKXkqWDa.js → GrSkeleton-CDTvCSoH.js} +0 -0
- /package/dist/components/GrTabs/chunks/{GrTabs-BpF8AAUz.js → GrTabs-CiGmcmvm.js} +0 -0
- /package/dist/components/GrTooltip/chunks/{GrTooltip-BvmsukLW.js → GrTooltip-CrV1gI_c.js} +0 -0
- /package/dist/components/GrTree/chunks/{GrTree-C2kN-O4Q.js → GrTree-Dlev9oBd.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrSegmented-Bwn2qOY4.js","names":[],"sources":["../../../../src/components/GrSegmented/GrSegmented.vue","../../../../src/components/GrSegmented/GrSegmented.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBA,MAAM,mBAAmB;GACvB,UAAU;GACV,SAAS;GACT,OAAO;GACP,QAAQ;GACR,eAAe;GAChB;EA2BD,MAAM,QAAQ;EAad,MAAM,OAAO;EAMb,MAAM,eAAe,gBADJ,oBAAmB,EACW,OAAO,KAAK,QAAQ;EAEnE,MAAM,UAAU,IAAwB,KAAI;EAC5C,MAAM,WAAW,oBAAI,IAAI,KAA0B,CAAA;EACnD,MAAM,oBAAoB,IAA8B,KAAI;EAC5D,MAAM,iBAAiB,IAAI,MAAK;EAEhC,IAAI,iBAAwC;EAC5C,IAAI,eAAmC;EACvC,IAAI,eAAmC;EACvC,IAAI,YAAY;EAEhB,MAAM,gBAAgB,eAAe,MAAM,QAAQ,WAAU,WAAU,OAAO,UAAU,MAAM,WAAW,CAAA;EACzG,MAAM,iBAAiB,eAAe,MAAM,QAAQ,cAAc,UAAU,KAAI;EAChF,MAAM,iBAAiB,eAAe,MAAM,QAAQ,QAAO,WAAU,CAAC,sBAAsB,OAAO,CAAC,CAAA;EACpG,MAAM,oBAAoB,eAAe,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,kBAAkB,CAAC,CAAA;EACzF,MAAM,gBAAgB,eAAe,qBAAqB;GACxD,SAAS,MAAM;GACf,OAAO,MAAM;GACb,UAAU,MAAM;GACjB,CAAC,CAAA;EACF,MAAM,YAAY,gBAAwC;GACxD,GAAG,qBAAqB;IACtB,SAAS,MAAM;IACf,MAAM,MAAM;IACb,CAAC;GACF,qBAAqB,MAAM,QAAQ,SAAS,IACxC,MAAM,QAAQ,UAAU,MAAM,QAAQ,kBAAkB,wBAAwB,CAAC,KAAK,IAAG,GACzF;GACL,EAAC;EACF,MAAM,qBAAqB,eAAe,0BAA0B,MAAM,QAAQ,CAAA;EAClF,MAAM,eAAe,eAAe,MAAM,QAAQ,aAAY;EAC9D,MAAM,eAAe,eAAe,cAAc,UAAU,MAAM,kBAAkB,UAAU,KAAI;EAElG,MAAM,iBAAiB,eAAuC;GAC5D,MAAM,WAAW,kBAAkB;AACnC,OAAI,CAAC,SACH,QAAO;IACL,SAAS;IACT,OAAO;IACP,QAAQ;IACR,oBAAoB,GAAG,kBAAkB,MAAM;IAC/C,WAAW;IACb;AAGF,UAAO;IACL,SAAS,eAAe,QAAQ,MAAM;IACtC,OAAO,GAAG,SAAS,MAAM;IACzB,QAAQ,GAAG,SAAS,OAAO;IAC3B,oBAAoB,GAAG,kBAAkB,MAAM;IAC/C,WAAW,eAAe,SAAS,EAAE,MAAM,SAAS,EAAE;IACxD;IACD;EAED,SAAS,aAAa,OAAiC;AACrD,UAAO,OAAO,MAAK;;EAGrB,SAAS,sBAAsB,QAAoC;AACjE,UAAO,MAAM,YAAY,QAAQ,OAAO,SAAQ;;EAGlD,SAAS,iBAAiB,QAAoC;AAC5D,UAAO,OAAO,UAAU,MAAM;;EAGhC,SAAS,iBAAiB,QAAoC;AAC5D,UAAO,QAAQ,OAAO,KAAK,IAAI,CAAC,OAAO;;EAGzC,SAAS,kBAAkB,QAA2B,OAAwB;AAC5E,OAAI,sBAAsB,OAAO,CAC/B,QAAO;AAGT,OAAI,iBAAiB,OAAO,CAC1B,QAAO;AAGT,OAAI,cAAc,UAAU,GAC1B,QAAO;AAGT,UAAO,eAAe,MAAM,IAAI,UAAU,MAAM,QAAQ,QAAQ;;EAGlE,SAAS,WAAW,OAAyB,SAAyD;GACpG,MAAM,MAAM,aAAa,MAAK;AAE9B,OAAI,EAAE,mBAAmB,cAAc;AACrC,aAAS,MAAM,OAAO,IAAG;AACzB;;AAGF,YAAS,MAAM,IAAI,KAAK,QAAO;;EAGjC,SAAS,eAAe,MAAgC,OAA0C;AAChG,OAAI,SAAS,MACX,QAAO;AAGT,OAAI,CAAC,QAAQ,CAAC,MACZ,QAAO;AAGT,UAAO,KAAK,UAAU,MAAM,SACvB,KAAK,WAAW,MAAM,UACtB,KAAK,MAAM,MAAM,KACjB,KAAK,MAAM,MAAM;;EAGxB,SAAS,mBAAyB;GAChC,MAAM,OAAO,QAAQ;GACrB,MAAM,SAAS,eAAe;AAC9B,OAAI,CAAC,QAAQ,CAAC,QAAQ;AACpB,QAAI,kBAAkB,UAAU,KAC9B,mBAAkB,QAAQ;AAE5B,QAAI,eAAe,MACjB,gBAAe,QAAQ;AAEzB;;GAGF,MAAM,eAAe,SAAS,MAAM,IAAI,aAAa,OAAO,MAAM,CAAA;AAClE,OAAI,CAAC,aACH;GAGF,MAAM,WAAW,KAAK,uBAAsB;GAC5C,MAAM,WAAW,aAAa,uBAAsB;GAEpD,MAAM,eAAe;IACnB,OAAO,KAAK,IAAI,KAAK,MAAM,SAAS,MAAM,EAAE,EAAE;IAC9C,QAAQ,KAAK,IAAI,KAAK,MAAM,SAAS,OAAO,EAAE,EAAE;IAChD,GAAG,KAAK,MAAM,SAAS,OAAO,SAAS,KAAK;IAC5C,GAAG,KAAK,IAAI,KAAK,MAAM,SAAS,MAAM,SAAS,IAAI,GAAG,GAAG,EAAE;IAC5D;AAED,OAAI,CAAC,eAAe,kBAAkB,OAAO,aAAa,CACxD,mBAAkB,QAAQ;GAG5B,MAAM,YAAY,aAAa,QAAQ,KAAK,aAAa,SAAS;AAClE,OAAI,eAAe,UAAU,UAC3B,gBAAe,QAAQ;;EAI3B,SAAS,kBAAwB;AAC/B,OAAI,OAAO,mBAAmB,YAC5B;GAGF,MAAM,OAAO,QAAQ;GACrB,MAAM,SAAS,eAAe;GAC9B,MAAM,eAAe,SACjB,SAAS,MAAM,IAAI,aAAa,OAAO,MAAM,CAAC,IAAI,OAClD;AAEJ,OAAI,CAAC,eACH,kBAAiB,IAAI,qBAAqB,iBAAiB,CAAA;AAG7D,OAAI,CAAC,MAAM;AACT,mBAAe,YAAW;AAC1B,mBAAe;AACf,mBAAe;AACf;;AAGF,OAAI,iBAAiB,QAAQ,iBAAiB,aAC5C;AAGF,kBAAe,YAAW;AAC1B,kBAAe,QAAQ,KAAI;AAE3B,OAAI,aACF,gBAAe,QAAQ,aAAY;AAGrC,kBAAe;AACf,kBAAe;;EAGjB,SAAS,kBAAwB;AAC/B,OAAI,UACF;AAGF,eAAY;AACP,kBAAe;AAClB,gBAAY;AACZ,sBAAiB;AACjB,qBAAgB;KACjB;;EAGH,SAAS,UAAU,QAAiC;AAClD,OAAI,sBAAsB,OAAO,IAAI,OAAO,UAAU,MAAM,WAC1D;AAGF,QAAK,qBAAqB,OAAO,MAAK;AACtC,QAAK,UAAU,OAAO,OAAO,OAAM;;EAGrC,SAAS,oBAAoB,YAAoB,WAA2B;AAC1E,OAAI,MAAM,QAAQ,WAAW,EAC3B,QAAO;GAGT,IAAI,SAAS;AACb,QAAK,IAAI,OAAO,GAAG,OAAO,MAAM,QAAQ,QAAQ,QAAQ,GAAG;AACzD,cAAU,SAAS,YAAY,MAAM,QAAQ,UAAU,MAAM,QAAQ;IACrE,MAAM,SAAS,MAAM,QAAQ;AAC7B,QAAI,UAAU,CAAC,sBAAsB,OAAO,CAC1C,QAAO;;AAIX,UAAO;;EAGT,SAAS,WAAW,OAAqB;GACvC,MAAM,SAAS,MAAM,QAAQ;AAC7B,OAAI,CAAC,OACH;AAGF,YAAS,MAAM,IAAI,aAAa,OAAO,MAAM,CAAC,EAAE,OAAM;;EAGxD,SAAS,UAAU,OAAsB,OAAqB;AAC5D,OAAI,MAAM,SACR;AAGF,OAAI,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,aAAa;AAC3D,UAAM,gBAAe;IACrB,MAAM,YAAY,oBAAoB,OAAO,EAAC;AAC9C,QAAI,cAAc,GAChB;IAGF,MAAM,SAAS,MAAM,QAAQ;AAC7B,QAAI,QAAQ;AACV,eAAU,OAAM;AAChB,gBAAW,UAAS;;AAEtB;;AAGF,OAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,WAAW;AACxD,UAAM,gBAAe;IACrB,MAAM,YAAY,oBAAoB,OAAO,GAAE;AAC/C,QAAI,cAAc,GAChB;IAGF,MAAM,SAAS,MAAM,QAAQ;AAC7B,QAAI,QAAQ;AACV,eAAU,OAAM;AAChB,gBAAW,UAAS;;AAEtB;;AAGF,OAAI,MAAM,QAAQ,QAAQ;AACxB,UAAM,gBAAe;IACrB,MAAM,cAAc,eAAe,MAAM;AACzC,QAAI,CAAC,YACH;AAGF,cAAU,YAAW;AACrB,eAAW,MAAM,QAAQ,WAAU,WAAU,OAAO,UAAU,YAAY,MAAM,CAAA;AAChF;;AAGF,OAAI,MAAM,QAAQ,OAAO;AACvB,UAAM,gBAAe;IACrB,MAAM,aAAa,eAAe,MAAM,GAAG,GAAE;AAC7C,QAAI,CAAC,WACH;AAGF,cAAU,WAAU;AACpB,eAAW,MAAM,QAAQ,WAAU,WAAU,OAAO,UAAU,WAAW,MAAM,CAAA;AAC/E;;AAGF,OAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,SAAS;AAC9C,UAAM,gBAAe;IACrB,MAAM,SAAS,MAAM,QAAQ;AAC7B,QAAI,OACF,WAAU,OAAM;;;AAKtB,cAAY,MAAM,kBAAkB,iBAAiB,CAAA;AACrD,cAAY,MAAM,eAAe,iBAAiB,EAAE,EAAE,MAAM,MAAM,CAAA;AAClE,cAAY,MAAM,eAAe,iBAAiB,CAAA;AAClD,cAAY,MAAM,YAAY,iBAAiB,CAAA;AAC/C,cAAY,MAAM,aAAa,iBAAiB,CAAA;AAEhD,kBAAgB,iBAAiB,CAAA;AACjC,wBAAsB;AACpB,mBAAgB,YAAW;AAC3B,kBAAe;AACf,kBAAe;IAChB;;uBAIC,mBAkEM,OAAA;aAjEA;IAAJ,KAAI;IACJ,qBAAA;IACC,gBAAc,QAAA;IACf,MAAK;IACJ,cAAY,QAAA;IACZ,iBAAe,QAAA,WAAQ,SAAY,KAAA;IACnC,OAAK,eAAE,cAAA,MAAa;IACpB,OAAK,eAAE,UAAA,MAAS;OAGT,aAAA,SAAA,WAAA,EADR,mBAME,QAAA;;IAJA,+BAAA;IACA,eAAY;IACX,OAAK,eAAE,mBAAA,MAAkB;IACzB,OAAK,eAAE,eAAA,MAAc;kEAGxB,mBA+CS,UAAA,MAAA,WA9CmB,QAAA,UAAlB,QAAQ,UAAK;wBADvB,mBA+CS,UAAA;KA7CN,KAAK,OAAO;;KACZ,MAAK,YAAW,WAAW,OAAO,OAAO,QAAO;KACjD,0BAAA;KACC,cAAY,OAAO,OAAO,MAAK;KAChC,MAAK;KACL,MAAK;KACJ,gBAAc,iBAAiB,OAAM,GAAA,SAAA;KACrC,iBAAe,sBAAsB,OAAM,GAAA,SAAa,KAAA;KACxD,UAAU,sBAAsB,OAAM;KACtC,UAAU,kBAAkB,QAAQ,MAAK,GAAA,IAAA;KACzC,OAAK,eAAE,MAAA,qBAAoB,CAAA;eAAW,QAAA;gBAA2B,iBAAiB,OAAM;gBAAqB,sBAAsB,OAAM;gBAAqB,iBAAiB,OAAM;;KAMrL,UAAK,WAAE,UAAU,OAAM;KACvB,YAAO,WAAE,UAAU,QAAQ,MAAK;QAEjC,mBASC,SAAA;KARC,MAAK;KACL,UAAS;KACT,eAAY;KACX,MAAM,aAAA;KACN,OAAO,OAAO;KACd,SAAS,iBAAiB,OAAM;KAChC,UAAU,sBAAsB,OAAM;KACtC,OAAO;6BAGV,WAcO,KAAA,QAAA,WAAA;KAbI;KACR,UAAU,iBAAiB,OAAM;KACjC,UAAU,sBAAsB,OAAM;aAWlC,CAPG,OAAO,QAAA,WAAA,EAFf,YAKE,wBAJK,OAAO,KAAI,EAAA;;KAEhB,eAAY;KACX,OAAK,eAAE,MAAA,yBAAwB,CAAA;4DAEtB,OAAO,SAAA,WAAA,EAAnB,mBAEO,QAAA;;KAFoB,OAAK,eAAE,MAAA,0BAAyB,CAAA;uBACtD,OAAO,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,EAAA,IAAA,WAAA"}
|
|
1
|
+
{"version":3,"file":"GrSegmented-BPl1koHc.js","names":[],"sources":["../../../../src/components/GrSegmented/GrSegmented.vue","../../../../src/components/GrSegmented/GrSegmented.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного DS-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `ds-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-ds-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-ds-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-ds-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as grSegmentedSafelist, t as grSegmentedConfig } from "../../chunks/config-
|
|
2
|
-
import { t as GrSegmented_default } from "./chunks/GrSegmented-
|
|
1
|
+
import { n as grSegmentedSafelist, t as grSegmentedConfig } from "../../chunks/config-BhvNm4Pp.js";
|
|
2
|
+
import { t as GrSegmented_default } from "./chunks/GrSegmented-BPl1koHc.js";
|
|
3
3
|
export { GrSegmented_default as GrSegmented, GrSegmented_default as default, grSegmentedConfig, grSegmentedSafelist };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { vClickOutside } from "../../../directives/clickOutside.js";
|
|
2
|
-
import { t as GrInput_default } from "../../GrInput/chunks/GrInput-
|
|
3
|
-
import { a as grSelectPanelClasses, i as grSelectNativeClass, n as grSelectLinkNativeLabelClass, o as grSelectTriggerClass, r as grSelectLinkNativeOverlayClass, s as linkBaseClass, t as defaultBaseClass } from "../../../chunks/grSelectStyles-
|
|
2
|
+
import { t as GrInput_default } from "../../GrInput/chunks/GrInput-CfO2RsM-.js";
|
|
3
|
+
import { a as grSelectPanelClasses, i as grSelectNativeClass, n as grSelectLinkNativeLabelClass, o as grSelectTriggerClass, r as grSelectLinkNativeOverlayClass, s as linkBaseClass, t as defaultBaseClass } from "../../../chunks/grSelectStyles-DTAYNzxn.js";
|
|
4
4
|
import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onUnmounted, openBlock, ref, renderList, renderSlot, toDisplayString, unref, vShow, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
5
5
|
//#region src/components/GrSelect/GrSelect.vue?vue&type=script&setup=true&lang.ts
|
|
6
6
|
var _hoisted_1 = [
|
package/dist/components/GrSelect/chunks/{GrSelect-BMMT5MrT.js.map → GrSelect-C3NAfTqL.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrSelect-BMMT5MrT.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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"}
|
|
1
|
+
{"version":3,"file":"GrSelect-C3NAfTqL.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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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrSelect_default } from "./chunks/GrSelect-
|
|
2
|
-
import { n as grSelectSafelist, t as grSelectConfig } from "../../chunks/config-
|
|
1
|
+
import { t as GrSelect_default } from "./chunks/GrSelect-C3NAfTqL.js";
|
|
2
|
+
import { n as grSelectSafelist, t as grSelectConfig } from "../../chunks/config-XoANT5mU.js";
|
|
3
3
|
export { GrSelect_default as GrSelect, GrSelect_default as default, grSelectConfig, grSelectSafelist };
|
package/dist/components/GrSidebar/chunks/{GrSidebar-CSZ3RpFB.js.map → GrSidebar-DVucgcfV.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrSidebar-
|
|
1
|
+
{"version":3,"file":"GrSidebar-DVucgcfV.js","names":["$slots"],"sources":["../../../../src/components/GrSidebar/GrSidebar.vue","../../../../src/components/GrSidebar/GrSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrSidebar — боковая панель приложения с заголовком и слотом контента.\n *\n * Слоты `#title` / `#subtitle` имеют приоритет над одноимёнными пропами\n * и позволяют передать произвольную разметку (иконки, бейджи, действия).\n */\nexport interface GrSidebarProps {\n title?: string\n subtitle?: string\n}\n\nwithDefaults(defineProps<GrSidebarProps>(), {\n title: undefined,\n subtitle: undefined,\n})\n</script>\n\n<template>\n <aside\n data-ds-sidebar\n class=\"border-r border-[var(--sidebar-brd)] bg-[var(--sidebar)] text-[var(--sidebar-fg)]\"\n >\n <div\n v-if=\"$slots.title || title || $slots.subtitle || subtitle\"\n data-ds-sidebar-header\n class=\"border-b border-[var(--sidebar-brd)] px-4 py-4\"\n >\n <div\n v-if=\"$slots.subtitle || subtitle\"\n data-ds-sidebar-subtitle\n class=\"text-[14px] text-[var(--muted-fg)]\"\n >\n <slot name=\"subtitle\">\n {{ subtitle }}\n </slot>\n </div>\n <div\n v-if=\"$slots.title || title\"\n data-ds-sidebar-title\n class=\"text-[18px] font-700\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n <div class=\"p-3\">\n <slot />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebar — боковая панель приложения с заголовком и слотом контента.\n *\n * Слоты `#title` / `#subtitle` имеют приоритет над одноимёнными пропами\n * и позволяют передать произвольную разметку (иконки, бейджи, действия).\n */\nexport interface GrSidebarProps {\n title?: string\n subtitle?: string\n}\n\nwithDefaults(defineProps<GrSidebarProps>(), {\n title: undefined,\n subtitle: undefined,\n})\n</script>\n\n<template>\n <aside\n data-ds-sidebar\n class=\"border-r border-[var(--sidebar-brd)] bg-[var(--sidebar)] text-[var(--sidebar-fg)]\"\n >\n <div\n v-if=\"$slots.title || title || $slots.subtitle || subtitle\"\n data-ds-sidebar-header\n class=\"border-b border-[var(--sidebar-brd)] px-4 py-4\"\n >\n <div\n v-if=\"$slots.subtitle || subtitle\"\n data-ds-sidebar-subtitle\n class=\"text-[14px] text-[var(--muted-fg)]\"\n >\n <slot name=\"subtitle\">\n {{ subtitle }}\n </slot>\n </div>\n <div\n v-if=\"$slots.title || title\"\n data-ds-sidebar-title\n class=\"text-[18px] font-700\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n <div class=\"p-3\">\n <slot />\n </div>\n </aside>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAmBE,mBA+BQ,SA/BR,YA+BQ,CA1BEA,KAAAA,OAAO,SAAS,QAAA,SAASA,KAAAA,OAAO,YAAY,QAAA,YAAA,WAAA,EADpD,mBAuBM,OAvBN,YAuBM,CAjBIA,KAAAA,OAAO,YAAY,QAAA,YAAA,WAAA,EAD3B,mBAQM,OARN,YAQM,CAHJ,WAEO,KAAA,QAAA,YAAA,EAAA,QAAA,CAAA,gBAAA,gBADF,QAAA,SAAQ,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAIPA,KAAAA,OAAO,SAAS,QAAA,SAAA,WAAA,EADxB,mBAQM,OARN,YAQM,CAHJ,WAEO,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBADF,QAAA,MAAK,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAId,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrSidebar_default } from "./chunks/GrSidebar-
|
|
2
|
-
import { t as grSidebarConfig } from "../../chunks/config-
|
|
1
|
+
import { t as GrSidebar_default } from "./chunks/GrSidebar-DVucgcfV.js";
|
|
2
|
+
import { t as grSidebarConfig } from "../../chunks/config-DnsISjD8.js";
|
|
3
3
|
export { GrSidebar_default as GrSidebar, GrSidebar_default as default, grSidebarConfig };
|
package/dist/components/GrSkeleton/chunks/{GrSkeleton-DKXkqWDa.js.map → GrSkeleton-CDTvCSoH.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrSkeleton-
|
|
1
|
+
{"version":3,"file":"GrSkeleton-CDTvCSoH.js","names":[],"sources":["../../../../src/components/GrSkeleton/GrSkeleton.vue","../../../../src/components/GrSkeleton/GrSkeleton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrSkeleton — заглушка-плейсхолдер для загружающегося контента.\n *\n * Помечен `aria-hidden=\"true\"`, чтобы скринридер не озвучивал «пустой» узел;\n * внешний контейнер обычно имеет `aria-busy=\"true\"` + `aria-live`.\n */\nexport interface GrSkeletonProps {\n height?: string\n width?: string\n rounded?: string\n}\n\nwithDefaults(defineProps<GrSkeletonProps>(), {\n height: '12px',\n width: '100%',\n rounded: '9999px',\n})\n</script>\n\n<template>\n <div\n data-ds-skeleton\n aria-hidden=\"true\"\n class=\"bg-[var(--muted)] border border-[var(--brd)]\"\n :style=\"{ height, width, borderRadius: rounded }\"\n />\n</template>\n\n<style>\n@keyframes ds-skeleton-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n[data-ds-skeleton] {\n animation: ds-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n [data-ds-skeleton] {\n animation: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * GrSkeleton — заглушка-плейсхолдер для загружающегося контента.\n *\n * Помечен `aria-hidden=\"true\"`, чтобы скринридер не озвучивал «пустой» узел;\n * внешний контейнер обычно имеет `aria-busy=\"true\"` + `aria-live`.\n */\nexport interface GrSkeletonProps {\n height?: string\n width?: string\n rounded?: string\n}\n\nwithDefaults(defineProps<GrSkeletonProps>(), {\n height: '12px',\n width: '100%',\n rounded: '9999px',\n})\n</script>\n\n<template>\n <div\n data-ds-skeleton\n aria-hidden=\"true\"\n class=\"bg-[var(--muted)] border border-[var(--brd)]\"\n :style=\"{ height, width, borderRadius: rounded }\"\n />\n</template>\n\n<style>\n@keyframes ds-skeleton-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n[data-ds-skeleton] {\n animation: ds-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n [data-ds-skeleton] {\n animation: none;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;uBAqBE,mBAKE,OAAA;IAJE,oBAAA;IACA,eAAY;IACZ,OAAM;IACL,OAAK,eAAA;KAAA,QAAI,QAAA;KAAM,OAAE,QAAA;KAAK,cAAgB,QAAA;KAAO,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrSkeleton_default } from "./chunks/GrSkeleton-
|
|
2
|
-
import { t as grSkeletonConfig } from "../../chunks/config-
|
|
1
|
+
import { t as GrSkeleton_default } from "./chunks/GrSkeleton-CDTvCSoH.js";
|
|
2
|
+
import { t as grSkeletonConfig } from "../../chunks/config-Mp9DJ9Mq2.js";
|
|
3
3
|
export { GrSkeleton_default as GrSkeleton, GrSkeleton_default as default, grSkeletonConfig };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as grSwitchTrackClass, i as grSwitchThumbClass, r as grSwitchLabelClass } from "../../../chunks/config-
|
|
1
|
+
import { a as grSwitchTrackClass, i as grSwitchThumbClass, r as grSwitchLabelClass } from "../../../chunks/config-rxFL-6yF2.js";
|
|
2
2
|
import { computed, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderSlot } from "vue";
|
|
3
3
|
//#region src/components/GrSwitch/GrSwitch.vue?vue&type=script&setup=true&lang.ts
|
|
4
4
|
var _hoisted_1 = [
|
package/dist/components/GrSwitch/chunks/{GrSwitch-CdBxwV3k.js.map → GrSwitch-Bnamgqb2.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrSwitch-
|
|
1
|
+
{"version":3,"file":"GrSwitch-Bnamgqb2.js","names":["$slots"],"sources":["../../../../src/components/GrSwitch/GrSwitch.vue","../../../../src/components/GrSwitch/GrSwitch.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {computed} from 'vue'\n\nimport {\n grSwitchLabelClass,\n grSwitchThumbClass,\n grSwitchTrackClass,\n type GrSwitchSize,\n} from './grSwitchStyles'\n\nexport type {GrSwitchSize} from './grSwitchStyles'\n\n/**\n * Пропсы публичного DS-примитива «Switch».\n */\nexport interface GrSwitchProps {\n modelValue: boolean\n disabled?: boolean\n ariaLabel?: string\n size?: GrSwitchSize\n /** Кастомный цвет фона в активном состоянии. Если не задан — `var(--primary)`. */\n activeBackgroundColor?: string\n /** Кастомный цвет фона в неактивном состоянии. Если не задан — `var(--muted)`. */\n inactiveBackgroundColor?: string\n}\n\nconst getCustomColor = (value?: string) => value?.trim() || undefined\n\nconst props = withDefaults(\n defineProps<GrSwitchProps>(),\n {\n disabled: false,\n ariaLabel: undefined,\n size: 'md',\n activeBackgroundColor: undefined,\n inactiveBackgroundColor: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst trackClass = computed(() => grSwitchTrackClass(props.size))\n\nconst trackStyle = computed(() => {\n const isChecked = props.modelValue\n const defaultBackgroundColor = isChecked ? 'var(--primary)' : 'var(--muted)'\n const customBackgroundColor = getCustomColor(\n isChecked ? props.activeBackgroundColor : props.inactiveBackgroundColor,\n )\n const backgroundColor = customBackgroundColor ?? defaultBackgroundColor\n\n return {\n '--ds-switch-track-bg': backgroundColor,\n '--ds-switch-track-brd': customBackgroundColor\n ? backgroundColor\n : isChecked\n ? 'var(--primary)'\n : 'var(--brd)',\n backgroundColor: 'var(--ds-switch-track-bg)',\n }\n})\n\nconst thumbClass = computed(() => grSwitchThumbClass({size: props.size, checked: props.modelValue}))\n\nconst labelClass = computed(() => grSwitchLabelClass(props.size))\n\nfunction toggle(): void {\n if (props.disabled) {\n return\n }\n\n emit('update:modelValue', !props.modelValue)\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n role=\"switch\"\n data-ds-switch\n :aria-checked=\"modelValue ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :disabled=\"disabled\"\n class=\"inline-flex items-center gap-2 select-none disabled:opacity-50 disabled:cursor-not-allowed\"\n @click=\"toggle\"\n >\n <span\n data-testid=\"ds-switch-track\"\n data-ds-switch-track\n :class=\"trackClass\"\n :style=\"trackStyle\"\n >\n <span\n data-testid=\"ds-switch-thumb\"\n data-ds-switch-thumb\n :class=\"thumbClass\"\n aria-hidden=\"true\"\n />\n </span>\n <span\n v-if=\"$slots.default\"\n data-ds-switch-label\n :class=\"labelClass\"\n >\n <slot />\n </span>\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport {computed} from 'vue'\n\nimport {\n grSwitchLabelClass,\n grSwitchThumbClass,\n grSwitchTrackClass,\n type GrSwitchSize,\n} from './grSwitchStyles'\n\nexport type {GrSwitchSize} from './grSwitchStyles'\n\n/**\n * Пропсы публичного DS-примитива «Switch».\n */\nexport interface GrSwitchProps {\n modelValue: boolean\n disabled?: boolean\n ariaLabel?: string\n size?: GrSwitchSize\n /** Кастомный цвет фона в активном состоянии. Если не задан — `var(--primary)`. */\n activeBackgroundColor?: string\n /** Кастомный цвет фона в неактивном состоянии. Если не задан — `var(--muted)`. */\n inactiveBackgroundColor?: string\n}\n\nconst getCustomColor = (value?: string) => value?.trim() || undefined\n\nconst props = withDefaults(\n defineProps<GrSwitchProps>(),\n {\n disabled: false,\n ariaLabel: undefined,\n size: 'md',\n activeBackgroundColor: undefined,\n inactiveBackgroundColor: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst trackClass = computed(() => grSwitchTrackClass(props.size))\n\nconst trackStyle = computed(() => {\n const isChecked = props.modelValue\n const defaultBackgroundColor = isChecked ? 'var(--primary)' : 'var(--muted)'\n const customBackgroundColor = getCustomColor(\n isChecked ? props.activeBackgroundColor : props.inactiveBackgroundColor,\n )\n const backgroundColor = customBackgroundColor ?? defaultBackgroundColor\n\n return {\n '--ds-switch-track-bg': backgroundColor,\n '--ds-switch-track-brd': customBackgroundColor\n ? backgroundColor\n : isChecked\n ? 'var(--primary)'\n : 'var(--brd)',\n backgroundColor: 'var(--ds-switch-track-bg)',\n }\n})\n\nconst thumbClass = computed(() => grSwitchThumbClass({size: props.size, checked: props.modelValue}))\n\nconst labelClass = computed(() => grSwitchLabelClass(props.size))\n\nfunction toggle(): void {\n if (props.disabled) {\n return\n }\n\n emit('update:modelValue', !props.modelValue)\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n role=\"switch\"\n data-ds-switch\n :aria-checked=\"modelValue ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :disabled=\"disabled\"\n class=\"inline-flex items-center gap-2 select-none disabled:opacity-50 disabled:cursor-not-allowed\"\n @click=\"toggle\"\n >\n <span\n data-testid=\"ds-switch-track\"\n data-ds-switch-track\n :class=\"trackClass\"\n :style=\"trackStyle\"\n >\n <span\n data-testid=\"ds-switch-thumb\"\n data-ds-switch-thumb\n :class=\"thumbClass\"\n aria-hidden=\"true\"\n />\n </span>\n <span\n v-if=\"$slots.default\"\n data-ds-switch-label\n :class=\"labelClass\"\n >\n <slot />\n </span>\n </button>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EA0BA,MAAM,kBAAkB,UAAmB,OAAO,MAAM,IAAI,KAAA;EAE5D,MAAM,QAAQ;EAWd,MAAM,OAAO;EAIb,MAAM,aAAa,eAAe,mBAAmB,MAAM,KAAK,CAAA;EAEhE,MAAM,aAAa,eAAe;GAChC,MAAM,YAAY,MAAM;GACxB,MAAM,yBAAyB,YAAY,mBAAmB;GAC9D,MAAM,wBAAwB,eAC1B,YAAY,MAAM,wBAAwB,MAAM,wBACpD;GACA,MAAM,kBAAkB,yBAAyB;AAEjD,UAAO;IACL,wBAAwB;IACxB,yBAAyB,wBACnB,kBACA,YACI,mBACA;IACV,iBAAiB;IACnB;IACD;EAED,MAAM,aAAa,eAAe,mBAAmB;GAAC,MAAM,MAAM;GAAM,SAAS,MAAM;GAAW,CAAC,CAAA;EAEnG,MAAM,aAAa,eAAe,mBAAmB,MAAM,KAAK,CAAA;EAEhE,SAAS,SAAe;AACtB,OAAI,MAAM,SACR;AAGF,QAAK,qBAAqB,CAAC,MAAM,WAAU;;;uBAK3C,mBA8BS,UAAA;IA7BL,MAAK;IACL,MAAK;IACL,kBAAA;IACC,gBAAc,QAAA,aAAU,SAAA;IACxB,cAAY,QAAA;IACZ,UAAU,QAAA;IACX,OAAM;IACL,SAAO;OAEV,mBAYO,QAAA;IAXH,eAAY;IACZ,wBAAA;IACC,OAAK,eAAE,WAAA,MAAU;IACjB,OAAK,eAAE,WAAA,MAAU;OAEpB,mBAKE,QAAA;IAJE,eAAY;IACZ,wBAAA;IACC,OAAK,eAAE,WAAA,MAAU;IAClB,eAAY;qBAIRA,KAAAA,OAAO,WAAA,WAAA,EADjB,mBAMO,QAAA;;IAJH,wBAAA;IACC,OAAK,eAAE,WAAA,MAAU;OAEpB,WAAQ,KAAA,QAAA,UAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,GAAA,WAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as grSwitchSafelist, t as grSwitchConfig } from "../../chunks/config-
|
|
2
|
-
import { t as GrSwitch_default } from "./chunks/GrSwitch-
|
|
1
|
+
import { n as grSwitchSafelist, t as grSwitchConfig } from "../../chunks/config-rxFL-6yF2.js";
|
|
2
|
+
import { t as GrSwitch_default } from "./chunks/GrSwitch-Bnamgqb2.js";
|
|
3
3
|
export { GrSwitch_default as GrSwitch, GrSwitch_default as default, grSwitchConfig, grSwitchSafelist };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { t as GrTable_default } from "./chunks/GrTable-CyQ9uw8A.js";
|
|
2
|
-
import { t as grTableConfig } from "../../chunks/config-
|
|
2
|
+
import { t as grTableConfig } from "../../chunks/config-DQOSlWvp.js";
|
|
3
3
|
export { GrTable_default as GrTable, GrTable_default as default, grTableConfig };
|