@feugene/granularity 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/GrBadge.css +1 -0
  2. package/dist/chunks/{config-Dkq0HeLe.js → config-3I3vqDW4.js} +1 -1
  3. package/dist/chunks/{config-Dkq0HeLe.js.map → config-3I3vqDW4.js.map} +1 -1
  4. package/dist/chunks/{config-BaMO9dQh.js → config-B61cX08u.js} +1 -1
  5. package/dist/chunks/{config-BaMO9dQh.js.map → config-B61cX08u.js.map} +1 -1
  6. package/dist/chunks/{config-C_-02Pu-.js.map → config-BgBWt9-A.js.map} +1 -1
  7. package/dist/chunks/{config-Cn8TG49a.js.map → config-CUwF4E4R.js.map} +1 -1
  8. package/dist/chunks/config-DK2NpdyX2.js.map +1 -0
  9. package/dist/chunks/{config-BXffa3ta.js → config-DZAQpfz0.js} +2 -2
  10. package/dist/chunks/{config-BXffa3ta.js.map → config-DZAQpfz0.js.map} +1 -1
  11. package/dist/chunks/{config-C9AOlKah.js.map → config-V3LF-b_F.js.map} +1 -1
  12. package/dist/chunks/config-iDTkWUdh2.js.map +1 -0
  13. package/dist/chunks/{grBadgeStyles-pqqbVgLk.js → grBadgeStyles-Cq0hH2Ls.js} +7 -5
  14. package/dist/chunks/grBadgeStyles-Cq0hH2Ls.js.map +1 -0
  15. package/dist/chunks/{grButtonStyles-BxZW2x0-.js.map → grButtonStyles-DKuXUemw.js.map} +1 -1
  16. package/dist/chunks/loader-circle-BUntgosj.js.map +1 -0
  17. package/dist/chunks/{safelist-BY7z8fOl.js → safelist-DccvJqCE.js} +1 -1
  18. package/dist/chunks/{safelist-BY7z8fOl.js.map → safelist-DccvJqCE.js.map} +1 -1
  19. package/dist/chunks/{shared-BkIIEolF.js → shared-D7q-upeJ.js} +8 -8
  20. package/dist/chunks/{shared-BkIIEolF.js.map → shared-D7q-upeJ.js.map} +1 -1
  21. package/dist/components/GrBadge/chunks/GrBadge-Cs_cVfuO.js +36 -0
  22. package/dist/components/GrBadge/chunks/GrBadge-Cs_cVfuO.js.map +1 -0
  23. package/dist/components/GrBadge/index.js +2 -2
  24. package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-CxtamOZ-.js.map → GrBadgeWrap-BEnE9DyJ.js.map} +1 -1
  25. package/dist/components/GrBadgeWrap/index.js +2 -2
  26. package/dist/components/GrBottomNav/chunks/{GrBottomNav-CsmQ7mTJ.js.map → GrBottomNav-XPHwd7UQ.js.map} +1 -1
  27. package/dist/components/GrBottomNav/index.js +2 -2
  28. package/dist/components/GrButton/chunks/{GrButton-BmGdp8-4.js → GrButton-CPDKonoB.js} +2 -2
  29. package/dist/components/GrButton/chunks/{GrButton-BmGdp8-4.js.map → GrButton-CPDKonoB.js.map} +1 -1
  30. package/dist/components/GrButton/index.js +4 -4
  31. package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-CBj4tpms.js.map → GrButtonGroup-CPOH6V-P.js.map} +1 -1
  32. package/dist/components/GrButtonGroup/index.js +2 -2
  33. package/dist/components/GrCard/chunks/{GrCard-xtDcXBr7.js → GrCard-6yk1RRAQ.js} +1 -1
  34. package/dist/components/GrCard/chunks/{GrCard-xtDcXBr7.js.map → GrCard-6yk1RRAQ.js.map} +1 -1
  35. package/dist/components/GrCard/index.js +1 -1
  36. package/dist/components/GrCollapse/chunks/{GrCollapse-HU20VTVn.js → GrCollapse-CU94BTAR.js} +1 -1
  37. package/dist/components/GrCollapse/chunks/{GrCollapse-HU20VTVn.js.map → GrCollapse-CU94BTAR.js.map} +1 -1
  38. package/dist/components/GrCollapse/index.js +1 -1
  39. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DjGOX9Hd.js → GrConfirmDialog-DE-d77Us.js} +2 -2
  40. package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DjGOX9Hd.js.map → GrConfirmDialog-DE-d77Us.js.map} +1 -1
  41. package/dist/components/GrConfirmDialog/index.js +2 -2
  42. package/dist/components/GrDataTable/index.js +1 -1
  43. package/dist/components/GrDialog/chunks/{GrDialog-gObmaVEl.js → GrDialog-Ba3Ojcsq.js} +1 -1
  44. package/dist/components/GrDialog/chunks/{GrDialog-gObmaVEl.js.map → GrDialog-Ba3Ojcsq.js.map} +1 -1
  45. package/dist/components/GrDialog/index.js +1 -1
  46. package/dist/components/GrDrawer/chunks/{GrDrawer-BJL_gTxI.js → GrDrawer-h33X403E.js} +1 -1
  47. package/dist/components/GrDrawer/chunks/{GrDrawer-BJL_gTxI.js.map → GrDrawer-h33X403E.js.map} +1 -1
  48. package/dist/components/GrDrawer/index.js +1 -1
  49. package/dist/components/GrFormFile/chunks/{GrFormFile-deDPicM2.js → GrFormFile-WqF-dPTB.js} +2 -2
  50. package/dist/components/GrFormFile/chunks/{GrFormFile-deDPicM2.js.map → GrFormFile-WqF-dPTB.js.map} +1 -1
  51. package/dist/components/GrFormFile/index.js +1 -1
  52. package/dist/components/GrInputTag/chunks/{GrInputTag-G_VYJPF9.js → GrInputTag-CeEy0IwD.js} +1 -1
  53. package/dist/components/GrInputTag/chunks/{GrInputTag-G_VYJPF9.js.map → GrInputTag-CeEy0IwD.js.map} +1 -1
  54. package/dist/components/GrInputTag/index.js +1 -1
  55. package/dist/components/GrList/chunks/{GrList-BPjQf-72.js → GrList-C2GpK56-.js} +1 -1
  56. package/dist/components/GrList/chunks/{GrList-BPjQf-72.js.map → GrList-C2GpK56-.js.map} +1 -1
  57. package/dist/components/GrList/index.js +1 -1
  58. package/dist/components/GrLoading/chunks/{GrLoading-C3QZKwE-.js → GrLoading-CxNgHgNw.js} +1 -1
  59. package/dist/components/GrLoading/chunks/{GrLoading-C3QZKwE-.js.map → GrLoading-CxNgHgNw.js.map} +1 -1
  60. package/dist/components/GrLoading/index.js +1 -1
  61. package/dist/components/GrNavbar/chunks/{GrNavbar-BoVuSZ-V.js → GrNavbar-CxHwBvSG.js} +1 -1
  62. package/dist/components/GrNavbar/chunks/{GrNavbar-BoVuSZ-V.js.map → GrNavbar-CxHwBvSG.js.map} +1 -1
  63. package/dist/components/GrNavbar/index.js +1 -1
  64. package/dist/components/GrPagination/chunks/{GrPagination-DW44c2-a.js → GrPagination-dMXZz120.js} +1 -1
  65. package/dist/components/GrPagination/chunks/{GrPagination-DW44c2-a.js.map → GrPagination-dMXZz120.js.map} +1 -1
  66. package/dist/components/GrPagination/index.js +1 -1
  67. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-CUMW6_58.js → GrPromptDialog-CfKf4tPx.js} +2 -2
  68. package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-CUMW6_58.js.map → GrPromptDialog-CfKf4tPx.js.map} +1 -1
  69. package/dist/components/GrPromptDialog/index.js +1 -1
  70. package/dist/components/GrRadio/chunks/{GrRadio-B_t_ZNVl.js → GrRadio-CQ3w3Bj6.js} +1 -1
  71. package/dist/components/GrRadio/chunks/{GrRadio-B_t_ZNVl.js.map → GrRadio-CQ3w3Bj6.js.map} +1 -1
  72. package/dist/components/GrRadio/index.js +2 -2
  73. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-CVlgpUBr.js → GrRadioGroup-DAaVzlPx.js} +2 -2
  74. package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-CVlgpUBr.js.map → GrRadioGroup-DAaVzlPx.js.map} +1 -1
  75. package/dist/components/GrRadioGroup/index.js +1 -1
  76. package/dist/components/GrToaster/chunks/{GrToaster-I1e-zCpf.js → GrToaster-BGNNmnr9.js} +1 -1
  77. package/dist/components/GrToaster/chunks/{GrToaster-I1e-zCpf.js.map → GrToaster-BGNNmnr9.js.map} +1 -1
  78. package/dist/components/GrToaster/index.js +1 -1
  79. package/dist/components/GrTree/chunks/{GrTree-Dlev9oBd.js → GrTree-mYa3COxG.js} +1 -1
  80. package/dist/components/GrTree/chunks/{GrTree-Dlev9oBd.js.map → GrTree-mYa3COxG.js.map} +1 -1
  81. package/dist/components/GrTree/index.js +1 -1
  82. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DkAIksRW.js → GrTreeSelect-BEdtrjwX.js} +1 -1
  83. package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DkAIksRW.js.map → GrTreeSelect-BEdtrjwX.js.map} +1 -1
  84. package/dist/components/GrTreeSelect/index.js +1 -1
  85. package/dist/directives/loading.js +1 -1
  86. package/dist/granular-provider-node.js +2 -2
  87. package/dist/granular-provider.js +1 -1
  88. package/dist/index.js +32 -32
  89. package/dist/types/src/components/GrBadge/grBadgeStyles.d.ts +1 -1
  90. package/package.json +1 -1
  91. package/dist/chunks/config-C_AOYGBr.js.map +0 -1
  92. package/dist/chunks/config-DJWf0kWe.js.map +0 -1
  93. package/dist/chunks/grBadgeStyles-pqqbVgLk.js.map +0 -1
  94. package/dist/chunks/loader-circle-B2rWDk5u.js.map +0 -1
  95. package/dist/components/GrBadge/chunks/GrBadge-wuIb4_2K.js +0 -32
  96. package/dist/components/GrBadge/chunks/GrBadge-wuIb4_2K.js.map +0 -1
  97. /package/dist/chunks/{_plugin-vue_export-helper-BG_EqiDU.js → _plugin-vue_export-helper-DQR7Sy7Q.js} +0 -0
  98. /package/dist/chunks/{config-C_-02Pu-.js → config-BgBWt9-A.js} +0 -0
  99. /package/dist/chunks/{config-Cn8TG49a.js → config-CUwF4E4R.js} +0 -0
  100. /package/dist/chunks/{config-DJWf0kWe.js → config-DK2NpdyX2.js} +0 -0
  101. /package/dist/chunks/{config-C9AOlKah.js → config-V3LF-b_F.js} +0 -0
  102. /package/dist/chunks/{config-C_AOYGBr.js → config-iDTkWUdh2.js} +0 -0
  103. /package/dist/chunks/{grButtonStyles-BxZW2x0-.js → grButtonStyles-DKuXUemw.js} +0 -0
  104. /package/dist/chunks/{loader-circle-B2rWDk5u.js → loader-circle-BUntgosj.js} +0 -0
  105. /package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-CxtamOZ-.js → GrBadgeWrap-BEnE9DyJ.js} +0 -0
  106. /package/dist/components/GrBottomNav/chunks/{GrBottomNav-CsmQ7mTJ.js → GrBottomNav-XPHwd7UQ.js} +0 -0
  107. /package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-CBj4tpms.js → GrButtonGroup-CPOH6V-P.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"GrCollapse-HU20VTVn.js","names":[],"sources":["../../../../src/components/GrCollapse/grCollapseContext.ts","../../../../src/components/GrCollapse/GrCollapse.vue","../../../../src/components/GrCollapse/GrCollapse.vue","../../../../src/components/GrCollapse/GrCollapseItem.vue","../../../../src/components/GrCollapse/GrCollapseItem.vue"],"sourcesContent":["import type { ComputedRef, InjectionKey } from 'vue'\n\nexport type GrCollapseValue = string | number\n\nexport type GrCollapseModelValue = GrCollapseValue | GrCollapseValue[] | undefined\n\nexport type GrCollapseContext = {\n accordion: ComputedRef<boolean>\n disabled: ComputedRef<boolean>\n isActive: (name: GrCollapseValue) => boolean\n toggle: (name: GrCollapseValue) => void\n}\n\nexport const GR_COLLAPSE_CONTEXT: InjectionKey<GrCollapseContext> = Symbol('GR_COLLAPSE_CONTEXT')","<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\n\nimport GrCard from '../GrCard'\n\nimport {\n GR_COLLAPSE_CONTEXT,\n type GrCollapseContext,\n type GrCollapseModelValue,\n type GrCollapseValue,\n} from './grCollapseContext'\n\n/**\n * GrCollapse — контейнер секций `GrCollapseItem`.\n *\n * @prop modelValue — открытые секции. В `accordion`-режиме — `string|number|undefined`, иначе — массив значений.\n * @prop accordion — режим, когда одновременно может быть открыта только одна секция.\n * @prop disabled — блокирует взаимодействие со всеми секциями.\n * @prop divided — разделители между секциями.\n */\nexport interface GrCollapseProps {\n modelValue?: GrCollapseModelValue\n accordion?: boolean\n disabled?: boolean\n divided?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseProps>(), {\n modelValue: undefined,\n accordion: false,\n disabled: false,\n divided: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrCollapseModelValue): void\n (e: 'change', value: GrCollapseModelValue): void\n}>()\n\nconst activeSet = computed(() => {\n const value = props.modelValue\n\n if (props.accordion) {\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n }\n\n if (Array.isArray(value)) {\n return new Set<GrCollapseValue>(value)\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n})\n\nfunction setModelValue(next: GrCollapseModelValue): void {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction toggle(name: GrCollapseValue): void {\n if (props.disabled)\n return\n\n if (props.accordion) {\n const next = activeSet.value.has(name) ? undefined : name\n setModelValue(next)\n return\n }\n\n const current = Array.isArray(props.modelValue)\n ? [...props.modelValue]\n : typeof props.modelValue === 'string' || typeof props.modelValue === 'number'\n ? [props.modelValue]\n : []\n\n const exists = current.includes(name)\n const next = exists\n ? current.filter(x => x !== name)\n : [...current, name]\n\n setModelValue(next)\n}\n\nconst context: GrCollapseContext = {\n accordion: computed(() => props.accordion),\n disabled: computed(() => props.disabled),\n isActive: name => activeSet.value.has(name),\n toggle,\n}\n\nprovide(GR_COLLAPSE_CONTEXT, context)\n</script>\n\n<template>\n <GrCard data-ds-collapse>\n <div :class=\"divided ? 'divide-y divide-[var(--brd)]' : ''\">\n <slot />\n </div>\n </GrCard>\n</template>","<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\n\nimport GrCard from '../GrCard'\n\nimport {\n GR_COLLAPSE_CONTEXT,\n type GrCollapseContext,\n type GrCollapseModelValue,\n type GrCollapseValue,\n} from './grCollapseContext'\n\n/**\n * GrCollapse — контейнер секций `GrCollapseItem`.\n *\n * @prop modelValue — открытые секции. В `accordion`-режиме — `string|number|undefined`, иначе — массив значений.\n * @prop accordion — режим, когда одновременно может быть открыта только одна секция.\n * @prop disabled — блокирует взаимодействие со всеми секциями.\n * @prop divided — разделители между секциями.\n */\nexport interface GrCollapseProps {\n modelValue?: GrCollapseModelValue\n accordion?: boolean\n disabled?: boolean\n divided?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseProps>(), {\n modelValue: undefined,\n accordion: false,\n disabled: false,\n divided: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrCollapseModelValue): void\n (e: 'change', value: GrCollapseModelValue): void\n}>()\n\nconst activeSet = computed(() => {\n const value = props.modelValue\n\n if (props.accordion) {\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n }\n\n if (Array.isArray(value)) {\n return new Set<GrCollapseValue>(value)\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n})\n\nfunction setModelValue(next: GrCollapseModelValue): void {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction toggle(name: GrCollapseValue): void {\n if (props.disabled)\n return\n\n if (props.accordion) {\n const next = activeSet.value.has(name) ? undefined : name\n setModelValue(next)\n return\n }\n\n const current = Array.isArray(props.modelValue)\n ? [...props.modelValue]\n : typeof props.modelValue === 'string' || typeof props.modelValue === 'number'\n ? [props.modelValue]\n : []\n\n const exists = current.includes(name)\n const next = exists\n ? current.filter(x => x !== name)\n : [...current, name]\n\n setModelValue(next)\n}\n\nconst context: GrCollapseContext = {\n accordion: computed(() => props.accordion),\n disabled: computed(() => props.disabled),\n isActive: name => activeSet.value.has(name),\n toggle,\n}\n\nprovide(GR_COLLAPSE_CONTEXT, context)\n</script>\n\n<template>\n <GrCard data-ds-collapse>\n <div :class=\"divided ? 'divide-y divide-[var(--brd)]' : ''\">\n <slot />\n </div>\n </GrCard>\n</template>","<script setup lang=\"ts\">\nimport ChevronDownIcon from '~icons/lucide/chevron-down'\nimport { computed, getCurrentInstance, inject, ref } from 'vue'\n\nimport GrIcon from '../GrIcon'\n\nimport { GR_COLLAPSE_CONTEXT, type GrCollapseValue } from './grCollapseContext'\n\n/**\n * GrCollapseItem — одна секция `GrCollapse`. Должна использоваться строго внутри `GrCollapse`.\n *\n * @prop name — уникальный идентификатор секции (для `v-model` у `GrCollapse`); по умолчанию генерируется из `uid`.\n * @prop title — текст заголовка; можно заменить слотом `#title`.\n * @prop disabled — запрещает раскрытие/сворачивание секции.\n */\nexport interface GrCollapseItemProps {\n name?: GrCollapseValue\n title?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseItemProps>(), {\n name: undefined,\n title: undefined,\n disabled: false,\n})\n\nconst collapse = inject(GR_COLLAPSE_CONTEXT)\n\nif (!collapse) {\n throw new Error('GrCollapseItem must be used inside GrCollapse')\n}\n\nconst collapseContext = collapse\n\nconst instance = getCurrentInstance()\nconst uid = instance?.uid ?? Math.random()\n\nconst resolvedName = computed<GrCollapseValue>(() => {\n if (typeof props.name === 'string' || typeof props.name === 'number')\n return props.name\n\n return uid\n})\n\nconst resolvedDisabled = computed(() => collapseContext.disabled.value || props.disabled)\nconst expanded = computed(() => collapseContext.isActive(resolvedName.value))\n\nconst headerId = `ds-collapse-header-${uid}`\nconst panelId = `ds-collapse-panel-${uid}`\n\nconst triggerEl = ref<HTMLElement | null>(null)\n\nfunction onToggle(): void {\n if (resolvedDisabled.value)\n return\n\n collapseContext.toggle(resolvedName.value)\n}\n\nfunction getAllTriggers(): HTMLElement[] {\n const root = triggerEl.value?.closest('[data-ds-collapse]')\n if (!root)\n return []\n\n return Array.prototype.slice.call(root.querySelectorAll<HTMLElement>('[data-ds-collapse-trigger]')) as HTMLElement[]\n}\n\nfunction focusRelative(direction: 1 | -1): void {\n const current = triggerEl.value\n if (!current)\n return\n\n const triggers = getAllTriggers()\n const index = triggers.indexOf(current)\n if (index === -1 || triggers.length === 0)\n return\n\n const nextIndex = (index + direction + triggers.length) % triggers.length\n triggers[nextIndex]?.focus()\n}\n\nfunction focusEdge(edge: 'start' | 'end'): void {\n const triggers = getAllTriggers()\n if (triggers.length === 0)\n return\n\n ;(edge === 'start' ? triggers[0] : triggers.at(-1))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n focusRelative(1)\n return\n }\n\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n focusRelative(-1)\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n focusEdge('start')\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n focusEdge('end')\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n onToggle()\n }\n}\n</script>\n\n<template>\n <div\n data-ds-collapse-item\n class=\"text-[var(--fg)]\"\n >\n <h3 class=\"m-0\">\n <button\n :id=\"headerId\"\n ref=\"triggerEl\"\n data-ds-collapse-trigger\n type=\"button\"\n :aria-expanded=\"expanded ? 'true' : 'false'\"\n :aria-controls=\"panelId\"\n :disabled=\"resolvedDisabled\"\n class=\"w-full px-4 py-3 flex items-center justify-between gap-4 text-left transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"resolvedDisabled ? '' : 'hover:bg-[var(--muted)]'\"\n @click=\"onToggle\"\n @keydown=\"onKeydown\"\n >\n <span class=\"min-w-0\">\n <slot name=\"title\">\n <span class=\"text-sm font-600\">\n {{ title }}\n </span>\n </slot>\n </span>\n\n <GrIcon\n size=\"sm\"\n aria-hidden=\"true\"\n data-ds-collapse-chevron\n class=\"shrink-0 transition-transform duration-150 text-[var(--muted-fg)]\"\n :class=\"expanded ? 'rotate-180' : ''\"\n >\n <ChevronDownIcon />\n </GrIcon>\n </button>\n </h3>\n\n <div\n :id=\"panelId\"\n role=\"region\"\n :aria-labelledby=\"headerId\"\n class=\"grid transition-[grid-template-rows] duration-200\"\n :class=\"expanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\"\n >\n <div class=\"overflow-hidden\">\n <div class=\"px-4 pb-4 text-sm text-[var(--muted-fg)]\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>","<script setup lang=\"ts\">\nimport ChevronDownIcon from '~icons/lucide/chevron-down'\nimport { computed, getCurrentInstance, inject, ref } from 'vue'\n\nimport GrIcon from '../GrIcon'\n\nimport { GR_COLLAPSE_CONTEXT, type GrCollapseValue } from './grCollapseContext'\n\n/**\n * GrCollapseItem — одна секция `GrCollapse`. Должна использоваться строго внутри `GrCollapse`.\n *\n * @prop name — уникальный идентификатор секции (для `v-model` у `GrCollapse`); по умолчанию генерируется из `uid`.\n * @prop title — текст заголовка; можно заменить слотом `#title`.\n * @prop disabled — запрещает раскрытие/сворачивание секции.\n */\nexport interface GrCollapseItemProps {\n name?: GrCollapseValue\n title?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseItemProps>(), {\n name: undefined,\n title: undefined,\n disabled: false,\n})\n\nconst collapse = inject(GR_COLLAPSE_CONTEXT)\n\nif (!collapse) {\n throw new Error('GrCollapseItem must be used inside GrCollapse')\n}\n\nconst collapseContext = collapse\n\nconst instance = getCurrentInstance()\nconst uid = instance?.uid ?? Math.random()\n\nconst resolvedName = computed<GrCollapseValue>(() => {\n if (typeof props.name === 'string' || typeof props.name === 'number')\n return props.name\n\n return uid\n})\n\nconst resolvedDisabled = computed(() => collapseContext.disabled.value || props.disabled)\nconst expanded = computed(() => collapseContext.isActive(resolvedName.value))\n\nconst headerId = `ds-collapse-header-${uid}`\nconst panelId = `ds-collapse-panel-${uid}`\n\nconst triggerEl = ref<HTMLElement | null>(null)\n\nfunction onToggle(): void {\n if (resolvedDisabled.value)\n return\n\n collapseContext.toggle(resolvedName.value)\n}\n\nfunction getAllTriggers(): HTMLElement[] {\n const root = triggerEl.value?.closest('[data-ds-collapse]')\n if (!root)\n return []\n\n return Array.prototype.slice.call(root.querySelectorAll<HTMLElement>('[data-ds-collapse-trigger]')) as HTMLElement[]\n}\n\nfunction focusRelative(direction: 1 | -1): void {\n const current = triggerEl.value\n if (!current)\n return\n\n const triggers = getAllTriggers()\n const index = triggers.indexOf(current)\n if (index === -1 || triggers.length === 0)\n return\n\n const nextIndex = (index + direction + triggers.length) % triggers.length\n triggers[nextIndex]?.focus()\n}\n\nfunction focusEdge(edge: 'start' | 'end'): void {\n const triggers = getAllTriggers()\n if (triggers.length === 0)\n return\n\n ;(edge === 'start' ? triggers[0] : triggers.at(-1))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n focusRelative(1)\n return\n }\n\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n focusRelative(-1)\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n focusEdge('start')\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n focusEdge('end')\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n onToggle()\n }\n}\n</script>\n\n<template>\n <div\n data-ds-collapse-item\n class=\"text-[var(--fg)]\"\n >\n <h3 class=\"m-0\">\n <button\n :id=\"headerId\"\n ref=\"triggerEl\"\n data-ds-collapse-trigger\n type=\"button\"\n :aria-expanded=\"expanded ? 'true' : 'false'\"\n :aria-controls=\"panelId\"\n :disabled=\"resolvedDisabled\"\n class=\"w-full px-4 py-3 flex items-center justify-between gap-4 text-left transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"resolvedDisabled ? '' : 'hover:bg-[var(--muted)]'\"\n @click=\"onToggle\"\n @keydown=\"onKeydown\"\n >\n <span class=\"min-w-0\">\n <slot name=\"title\">\n <span class=\"text-sm font-600\">\n {{ title }}\n </span>\n </slot>\n </span>\n\n <GrIcon\n size=\"sm\"\n aria-hidden=\"true\"\n data-ds-collapse-chevron\n class=\"shrink-0 transition-transform duration-150 text-[var(--muted-fg)]\"\n :class=\"expanded ? 'rotate-180' : ''\"\n >\n <ChevronDownIcon />\n </GrIcon>\n </button>\n </h3>\n\n <div\n :id=\"panelId\"\n role=\"region\"\n :aria-labelledby=\"headerId\"\n class=\"grid transition-[grid-template-rows] duration-200\"\n :class=\"expanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\"\n >\n <div class=\"overflow-hidden\">\n <div class=\"px-4 pb-4 text-sm text-[var(--muted-fg)]\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>"],"mappings":"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"}
1
+ {"version":3,"file":"GrCollapse-CU94BTAR.js","names":[],"sources":["../../../../src/components/GrCollapse/grCollapseContext.ts","../../../../src/components/GrCollapse/GrCollapse.vue","../../../../src/components/GrCollapse/GrCollapse.vue","../../../../src/components/GrCollapse/GrCollapseItem.vue","../../../../src/components/GrCollapse/GrCollapseItem.vue"],"sourcesContent":["import type { ComputedRef, InjectionKey } from 'vue'\n\nexport type GrCollapseValue = string | number\n\nexport type GrCollapseModelValue = GrCollapseValue | GrCollapseValue[] | undefined\n\nexport type GrCollapseContext = {\n accordion: ComputedRef<boolean>\n disabled: ComputedRef<boolean>\n isActive: (name: GrCollapseValue) => boolean\n toggle: (name: GrCollapseValue) => void\n}\n\nexport const GR_COLLAPSE_CONTEXT: InjectionKey<GrCollapseContext> = Symbol('GR_COLLAPSE_CONTEXT')","<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\n\nimport GrCard from '../GrCard'\n\nimport {\n GR_COLLAPSE_CONTEXT,\n type GrCollapseContext,\n type GrCollapseModelValue,\n type GrCollapseValue,\n} from './grCollapseContext'\n\n/**\n * GrCollapse — контейнер секций `GrCollapseItem`.\n *\n * @prop modelValue — открытые секции. В `accordion`-режиме — `string|number|undefined`, иначе — массив значений.\n * @prop accordion — режим, когда одновременно может быть открыта только одна секция.\n * @prop disabled — блокирует взаимодействие со всеми секциями.\n * @prop divided — разделители между секциями.\n */\nexport interface GrCollapseProps {\n modelValue?: GrCollapseModelValue\n accordion?: boolean\n disabled?: boolean\n divided?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseProps>(), {\n modelValue: undefined,\n accordion: false,\n disabled: false,\n divided: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrCollapseModelValue): void\n (e: 'change', value: GrCollapseModelValue): void\n}>()\n\nconst activeSet = computed(() => {\n const value = props.modelValue\n\n if (props.accordion) {\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n }\n\n if (Array.isArray(value)) {\n return new Set<GrCollapseValue>(value)\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n})\n\nfunction setModelValue(next: GrCollapseModelValue): void {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction toggle(name: GrCollapseValue): void {\n if (props.disabled)\n return\n\n if (props.accordion) {\n const next = activeSet.value.has(name) ? undefined : name\n setModelValue(next)\n return\n }\n\n const current = Array.isArray(props.modelValue)\n ? [...props.modelValue]\n : typeof props.modelValue === 'string' || typeof props.modelValue === 'number'\n ? [props.modelValue]\n : []\n\n const exists = current.includes(name)\n const next = exists\n ? current.filter(x => x !== name)\n : [...current, name]\n\n setModelValue(next)\n}\n\nconst context: GrCollapseContext = {\n accordion: computed(() => props.accordion),\n disabled: computed(() => props.disabled),\n isActive: name => activeSet.value.has(name),\n toggle,\n}\n\nprovide(GR_COLLAPSE_CONTEXT, context)\n</script>\n\n<template>\n <GrCard data-ds-collapse>\n <div :class=\"divided ? 'divide-y divide-[var(--brd)]' : ''\">\n <slot />\n </div>\n </GrCard>\n</template>","<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\n\nimport GrCard from '../GrCard'\n\nimport {\n GR_COLLAPSE_CONTEXT,\n type GrCollapseContext,\n type GrCollapseModelValue,\n type GrCollapseValue,\n} from './grCollapseContext'\n\n/**\n * GrCollapse — контейнер секций `GrCollapseItem`.\n *\n * @prop modelValue — открытые секции. В `accordion`-режиме — `string|number|undefined`, иначе — массив значений.\n * @prop accordion — режим, когда одновременно может быть открыта только одна секция.\n * @prop disabled — блокирует взаимодействие со всеми секциями.\n * @prop divided — разделители между секциями.\n */\nexport interface GrCollapseProps {\n modelValue?: GrCollapseModelValue\n accordion?: boolean\n disabled?: boolean\n divided?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseProps>(), {\n modelValue: undefined,\n accordion: false,\n disabled: false,\n divided: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrCollapseModelValue): void\n (e: 'change', value: GrCollapseModelValue): void\n}>()\n\nconst activeSet = computed(() => {\n const value = props.modelValue\n\n if (props.accordion) {\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n }\n\n if (Array.isArray(value)) {\n return new Set<GrCollapseValue>(value)\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n return new Set<GrCollapseValue>([value])\n }\n\n return new Set<GrCollapseValue>()\n})\n\nfunction setModelValue(next: GrCollapseModelValue): void {\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction toggle(name: GrCollapseValue): void {\n if (props.disabled)\n return\n\n if (props.accordion) {\n const next = activeSet.value.has(name) ? undefined : name\n setModelValue(next)\n return\n }\n\n const current = Array.isArray(props.modelValue)\n ? [...props.modelValue]\n : typeof props.modelValue === 'string' || typeof props.modelValue === 'number'\n ? [props.modelValue]\n : []\n\n const exists = current.includes(name)\n const next = exists\n ? current.filter(x => x !== name)\n : [...current, name]\n\n setModelValue(next)\n}\n\nconst context: GrCollapseContext = {\n accordion: computed(() => props.accordion),\n disabled: computed(() => props.disabled),\n isActive: name => activeSet.value.has(name),\n toggle,\n}\n\nprovide(GR_COLLAPSE_CONTEXT, context)\n</script>\n\n<template>\n <GrCard data-ds-collapse>\n <div :class=\"divided ? 'divide-y divide-[var(--brd)]' : ''\">\n <slot />\n </div>\n </GrCard>\n</template>","<script setup lang=\"ts\">\nimport ChevronDownIcon from '~icons/lucide/chevron-down'\nimport { computed, getCurrentInstance, inject, ref } from 'vue'\n\nimport GrIcon from '../GrIcon'\n\nimport { GR_COLLAPSE_CONTEXT, type GrCollapseValue } from './grCollapseContext'\n\n/**\n * GrCollapseItem — одна секция `GrCollapse`. Должна использоваться строго внутри `GrCollapse`.\n *\n * @prop name — уникальный идентификатор секции (для `v-model` у `GrCollapse`); по умолчанию генерируется из `uid`.\n * @prop title — текст заголовка; можно заменить слотом `#title`.\n * @prop disabled — запрещает раскрытие/сворачивание секции.\n */\nexport interface GrCollapseItemProps {\n name?: GrCollapseValue\n title?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseItemProps>(), {\n name: undefined,\n title: undefined,\n disabled: false,\n})\n\nconst collapse = inject(GR_COLLAPSE_CONTEXT)\n\nif (!collapse) {\n throw new Error('GrCollapseItem must be used inside GrCollapse')\n}\n\nconst collapseContext = collapse\n\nconst instance = getCurrentInstance()\nconst uid = instance?.uid ?? Math.random()\n\nconst resolvedName = computed<GrCollapseValue>(() => {\n if (typeof props.name === 'string' || typeof props.name === 'number')\n return props.name\n\n return uid\n})\n\nconst resolvedDisabled = computed(() => collapseContext.disabled.value || props.disabled)\nconst expanded = computed(() => collapseContext.isActive(resolvedName.value))\n\nconst headerId = `ds-collapse-header-${uid}`\nconst panelId = `ds-collapse-panel-${uid}`\n\nconst triggerEl = ref<HTMLElement | null>(null)\n\nfunction onToggle(): void {\n if (resolvedDisabled.value)\n return\n\n collapseContext.toggle(resolvedName.value)\n}\n\nfunction getAllTriggers(): HTMLElement[] {\n const root = triggerEl.value?.closest('[data-ds-collapse]')\n if (!root)\n return []\n\n return Array.prototype.slice.call(root.querySelectorAll<HTMLElement>('[data-ds-collapse-trigger]')) as HTMLElement[]\n}\n\nfunction focusRelative(direction: 1 | -1): void {\n const current = triggerEl.value\n if (!current)\n return\n\n const triggers = getAllTriggers()\n const index = triggers.indexOf(current)\n if (index === -1 || triggers.length === 0)\n return\n\n const nextIndex = (index + direction + triggers.length) % triggers.length\n triggers[nextIndex]?.focus()\n}\n\nfunction focusEdge(edge: 'start' | 'end'): void {\n const triggers = getAllTriggers()\n if (triggers.length === 0)\n return\n\n ;(edge === 'start' ? triggers[0] : triggers.at(-1))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n focusRelative(1)\n return\n }\n\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n focusRelative(-1)\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n focusEdge('start')\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n focusEdge('end')\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n onToggle()\n }\n}\n</script>\n\n<template>\n <div\n data-ds-collapse-item\n class=\"text-[var(--fg)]\"\n >\n <h3 class=\"m-0\">\n <button\n :id=\"headerId\"\n ref=\"triggerEl\"\n data-ds-collapse-trigger\n type=\"button\"\n :aria-expanded=\"expanded ? 'true' : 'false'\"\n :aria-controls=\"panelId\"\n :disabled=\"resolvedDisabled\"\n class=\"w-full px-4 py-3 flex items-center justify-between gap-4 text-left transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"resolvedDisabled ? '' : 'hover:bg-[var(--muted)]'\"\n @click=\"onToggle\"\n @keydown=\"onKeydown\"\n >\n <span class=\"min-w-0\">\n <slot name=\"title\">\n <span class=\"text-sm font-600\">\n {{ title }}\n </span>\n </slot>\n </span>\n\n <GrIcon\n size=\"sm\"\n aria-hidden=\"true\"\n data-ds-collapse-chevron\n class=\"shrink-0 transition-transform duration-150 text-[var(--muted-fg)]\"\n :class=\"expanded ? 'rotate-180' : ''\"\n >\n <ChevronDownIcon />\n </GrIcon>\n </button>\n </h3>\n\n <div\n :id=\"panelId\"\n role=\"region\"\n :aria-labelledby=\"headerId\"\n class=\"grid transition-[grid-template-rows] duration-200\"\n :class=\"expanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\"\n >\n <div class=\"overflow-hidden\">\n <div class=\"px-4 pb-4 text-sm text-[var(--muted-fg)]\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>","<script setup lang=\"ts\">\nimport ChevronDownIcon from '~icons/lucide/chevron-down'\nimport { computed, getCurrentInstance, inject, ref } from 'vue'\n\nimport GrIcon from '../GrIcon'\n\nimport { GR_COLLAPSE_CONTEXT, type GrCollapseValue } from './grCollapseContext'\n\n/**\n * GrCollapseItem — одна секция `GrCollapse`. Должна использоваться строго внутри `GrCollapse`.\n *\n * @prop name — уникальный идентификатор секции (для `v-model` у `GrCollapse`); по умолчанию генерируется из `uid`.\n * @prop title — текст заголовка; можно заменить слотом `#title`.\n * @prop disabled — запрещает раскрытие/сворачивание секции.\n */\nexport interface GrCollapseItemProps {\n name?: GrCollapseValue\n title?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<GrCollapseItemProps>(), {\n name: undefined,\n title: undefined,\n disabled: false,\n})\n\nconst collapse = inject(GR_COLLAPSE_CONTEXT)\n\nif (!collapse) {\n throw new Error('GrCollapseItem must be used inside GrCollapse')\n}\n\nconst collapseContext = collapse\n\nconst instance = getCurrentInstance()\nconst uid = instance?.uid ?? Math.random()\n\nconst resolvedName = computed<GrCollapseValue>(() => {\n if (typeof props.name === 'string' || typeof props.name === 'number')\n return props.name\n\n return uid\n})\n\nconst resolvedDisabled = computed(() => collapseContext.disabled.value || props.disabled)\nconst expanded = computed(() => collapseContext.isActive(resolvedName.value))\n\nconst headerId = `ds-collapse-header-${uid}`\nconst panelId = `ds-collapse-panel-${uid}`\n\nconst triggerEl = ref<HTMLElement | null>(null)\n\nfunction onToggle(): void {\n if (resolvedDisabled.value)\n return\n\n collapseContext.toggle(resolvedName.value)\n}\n\nfunction getAllTriggers(): HTMLElement[] {\n const root = triggerEl.value?.closest('[data-ds-collapse]')\n if (!root)\n return []\n\n return Array.prototype.slice.call(root.querySelectorAll<HTMLElement>('[data-ds-collapse-trigger]')) as HTMLElement[]\n}\n\nfunction focusRelative(direction: 1 | -1): void {\n const current = triggerEl.value\n if (!current)\n return\n\n const triggers = getAllTriggers()\n const index = triggers.indexOf(current)\n if (index === -1 || triggers.length === 0)\n return\n\n const nextIndex = (index + direction + triggers.length) % triggers.length\n triggers[nextIndex]?.focus()\n}\n\nfunction focusEdge(edge: 'start' | 'end'): void {\n const triggers = getAllTriggers()\n if (triggers.length === 0)\n return\n\n ;(edge === 'start' ? triggers[0] : triggers.at(-1))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n focusRelative(1)\n return\n }\n\n if (event.key === 'ArrowUp') {\n event.preventDefault()\n focusRelative(-1)\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n focusEdge('start')\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n focusEdge('end')\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n onToggle()\n }\n}\n</script>\n\n<template>\n <div\n data-ds-collapse-item\n class=\"text-[var(--fg)]\"\n >\n <h3 class=\"m-0\">\n <button\n :id=\"headerId\"\n ref=\"triggerEl\"\n data-ds-collapse-trigger\n type=\"button\"\n :aria-expanded=\"expanded ? 'true' : 'false'\"\n :aria-controls=\"panelId\"\n :disabled=\"resolvedDisabled\"\n class=\"w-full px-4 py-3 flex items-center justify-between gap-4 text-left transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"resolvedDisabled ? '' : 'hover:bg-[var(--muted)]'\"\n @click=\"onToggle\"\n @keydown=\"onKeydown\"\n >\n <span class=\"min-w-0\">\n <slot name=\"title\">\n <span class=\"text-sm font-600\">\n {{ title }}\n </span>\n </slot>\n </span>\n\n <GrIcon\n size=\"sm\"\n aria-hidden=\"true\"\n data-ds-collapse-chevron\n class=\"shrink-0 transition-transform duration-150 text-[var(--muted-fg)]\"\n :class=\"expanded ? 'rotate-180' : ''\"\n >\n <ChevronDownIcon />\n </GrIcon>\n </button>\n </h3>\n\n <div\n :id=\"panelId\"\n role=\"region\"\n :aria-labelledby=\"headerId\"\n class=\"grid transition-[grid-template-rows] duration-200\"\n :class=\"expanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\"\n >\n <div class=\"overflow-hidden\">\n <div class=\"px-4 pb-4 text-sm text-[var(--muted-fg)]\">\n <slot />\n </div>\n </div>\n </div>\n </div>\n</template>"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { n as GrCollapse_default, r as GR_COLLAPSE_CONTEXT, t as GrCollapseItem_default } from "./chunks/GrCollapse-HU20VTVn.js";
1
+ import { n as GrCollapse_default, r as GR_COLLAPSE_CONTEXT, t as GrCollapseItem_default } from "./chunks/GrCollapse-CU94BTAR.js";
2
2
  import { t as grCollapseConfig } from "../../chunks/config-D5ek7VSi.js";
3
3
  export { GR_COLLAPSE_CONTEXT, GrCollapse_default as GrCollapse, GrCollapse_default as default, GrCollapseItem_default as GrCollapseItem, grCollapseConfig };
@@ -1,5 +1,5 @@
1
- import { t as GrButton_default } from "../../GrButton/chunks/GrButton-BmGdp8-4.js";
2
- import { t as GrDialog_default } from "../../GrDialog/chunks/GrDialog-gObmaVEl.js";
1
+ import { t as GrButton_default } from "../../GrButton/chunks/GrButton-CPDKonoB.js";
2
+ import { t as GrDialog_default } from "../../GrDialog/chunks/GrDialog-Ba3Ojcsq.js";
3
3
  import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, openBlock, renderSlot, toDisplayString, withCtx } from "vue";
4
4
  //#region src/components/GrConfirmDialog/GrConfirmDialog.vue?vue&type=script&setup=true&lang.ts
5
5
  var _hoisted_1 = {
@@ -1 +1 @@
1
- {"version":3,"file":"GrConfirmDialog-DjGOX9Hd.js","names":[],"sources":["../../../../src/components/GrConfirmDialog/GrConfirmDialog.vue","../../../../src/components/GrConfirmDialog/GrConfirmDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrConfirmDialog — DS-примитив диалога подтверждения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog`/`GrPromptDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Клик по «Confirm»/«Cancel» эмитит одноимённое событие и закрывает диалог\n * через `update:modelValue`.\n */\nimport { computed } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\nexport interface GrConfirmDialogProps {\n modelValue: boolean\n title?: string\n description?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n}\n\nconst props = withDefaults(defineProps<GrConfirmDialogProps>(), {\n title: 'Confirm',\n description: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n buttonSize: undefined,\n confirmText: 'Confirm',\n cancelText: 'Cancel',\n confirmVariant: 'primary',\n confirmTone: 'primary',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'confirm'): void\n (e: 'cancel'): void\n}>()\n\nconst open = computed({\n get: () => props.modelValue,\n set: (value: boolean) => emit('update:modelValue', value),\n})\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n emit('confirm')\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"title\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"closeLabel\"\n >\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"ds-confirm-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ cancelText }}\n </GrButton>\n <GrButton\n data-testid=\"ds-confirm-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n @click=\"onConfirm\"\n >\n {{ confirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrConfirmDialog — DS-примитив диалога подтверждения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog`/`GrPromptDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Клик по «Confirm»/«Cancel» эмитит одноимённое событие и закрывает диалог\n * через `update:modelValue`.\n */\nimport { computed } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\nexport interface GrConfirmDialogProps {\n modelValue: boolean\n title?: string\n description?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n}\n\nconst props = withDefaults(defineProps<GrConfirmDialogProps>(), {\n title: 'Confirm',\n description: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n buttonSize: undefined,\n confirmText: 'Confirm',\n cancelText: 'Cancel',\n confirmVariant: 'primary',\n confirmTone: 'primary',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'confirm'): void\n (e: 'cancel'): void\n}>()\n\nconst open = computed({\n get: () => props.modelValue,\n set: (value: boolean) => emit('update:modelValue', value),\n})\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n emit('confirm')\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"title\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"closeLabel\"\n >\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"ds-confirm-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ cancelText }}\n </GrButton>\n <GrButton\n data-testid=\"ds-confirm-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n @click=\"onConfirm\"\n >\n {{ confirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuCA,MAAM,QAAQ;EAmBd,MAAM,OAAO;EAMb,MAAM,OAAO,SAAS;GACpB,WAAW,MAAM;GACjB,MAAM,UAAmB,KAAK,qBAAqB,MAAM;GAC1D,CAAA;EAED,SAAS,WAAiB;AACxB,QAAK,SAAQ;AACb,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,YAAkB;AACzB,QAAK,UAAS;AACd,QAAK,qBAAqB,MAAK;;;uBAK/B,YAqCW,kBAAA;gBApCA,KAAA;sEAAI,QAAA;IACZ,OAAO,QAAA;IACP,MAAM,QAAA;IACN,qBAAmB,QAAA;IACnB,gBAAc,QAAA;IACd,eAAa,QAAA;IACb,qBAAmB,QAAA;IACnB,iBAAe,QAAA;IACf,iBAAe,QAAA;IACf,eAAa,QAAA;IACb,eAAa,QAAA;;IAQH,QAAM,cAgBR,CAfP,WAeO,KAAA,QAAA,UAAA,EAAA,QAAA,CAdL,mBAaM,OAbN,YAaM,CAZJ,YAEW,kBAAA;KAFD,eAAY;KAAoB,SAAQ;KAAW,MAAM,QAAA;KAAa,SAAO;;4BACrE,CAAA,gBAAA,gBAAb,QAAA,WAAU,EAAA,EAAA,CAAA,CAAA;;qBAEf,YAQW,kBAAA;KAPT,eAAY;KACX,SAAS,QAAA;KACT,MAAM,QAAA;KACN,MAAM,QAAA;KACN,SAAO;;4BAES,CAAA,gBAAA,gBAAd,QAAA,YAAW,EAAA,EAAA,CAAA,CAAA;;;;;;;2BAff,CAJP,WAIO,KAAA,QAAA,WAAA,EAAA,QAAA,CAHM,QAAA,eAAA,WAAA,EAAX,mBAEM,OAFN,YAEM,gBADD,QAAA,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"GrConfirmDialog-DE-d77Us.js","names":[],"sources":["../../../../src/components/GrConfirmDialog/GrConfirmDialog.vue","../../../../src/components/GrConfirmDialog/GrConfirmDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrConfirmDialog — DS-примитив диалога подтверждения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog`/`GrPromptDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Клик по «Confirm»/«Cancel» эмитит одноимённое событие и закрывает диалог\n * через `update:modelValue`.\n */\nimport { computed } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\nexport interface GrConfirmDialogProps {\n modelValue: boolean\n title?: string\n description?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n}\n\nconst props = withDefaults(defineProps<GrConfirmDialogProps>(), {\n title: 'Confirm',\n description: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n buttonSize: undefined,\n confirmText: 'Confirm',\n cancelText: 'Cancel',\n confirmVariant: 'primary',\n confirmTone: 'primary',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'confirm'): void\n (e: 'cancel'): void\n}>()\n\nconst open = computed({\n get: () => props.modelValue,\n set: (value: boolean) => emit('update:modelValue', value),\n})\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n emit('confirm')\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"title\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"closeLabel\"\n >\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"ds-confirm-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ cancelText }}\n </GrButton>\n <GrButton\n data-testid=\"ds-confirm-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n @click=\"onConfirm\"\n >\n {{ confirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrConfirmDialog — DS-примитив диалога подтверждения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog`/`GrPromptDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Клик по «Confirm»/«Cancel» эмитит одноимённое событие и закрывает диалог\n * через `update:modelValue`.\n */\nimport { computed } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\nexport interface GrConfirmDialogProps {\n modelValue: boolean\n title?: string\n description?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n}\n\nconst props = withDefaults(defineProps<GrConfirmDialogProps>(), {\n title: 'Confirm',\n description: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n buttonSize: undefined,\n confirmText: 'Confirm',\n cancelText: 'Cancel',\n confirmVariant: 'primary',\n confirmTone: 'primary',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'confirm'): void\n (e: 'cancel'): void\n}>()\n\nconst open = computed({\n get: () => props.modelValue,\n set: (value: boolean) => emit('update:modelValue', value),\n})\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n emit('confirm')\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"title\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"closeLabel\"\n >\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"ds-confirm-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ cancelText }}\n </GrButton>\n <GrButton\n data-testid=\"ds-confirm-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n @click=\"onConfirm\"\n >\n {{ confirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuCA,MAAM,QAAQ;EAmBd,MAAM,OAAO;EAMb,MAAM,OAAO,SAAS;GACpB,WAAW,MAAM;GACjB,MAAM,UAAmB,KAAK,qBAAqB,MAAM;GAC1D,CAAA;EAED,SAAS,WAAiB;AACxB,QAAK,SAAQ;AACb,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,YAAkB;AACzB,QAAK,UAAS;AACd,QAAK,qBAAqB,MAAK;;;uBAK/B,YAqCW,kBAAA;gBApCA,KAAA;sEAAI,QAAA;IACZ,OAAO,QAAA;IACP,MAAM,QAAA;IACN,qBAAmB,QAAA;IACnB,gBAAc,QAAA;IACd,eAAa,QAAA;IACb,qBAAmB,QAAA;IACnB,iBAAe,QAAA;IACf,iBAAe,QAAA;IACf,eAAa,QAAA;IACb,eAAa,QAAA;;IAQH,QAAM,cAgBR,CAfP,WAeO,KAAA,QAAA,UAAA,EAAA,QAAA,CAdL,mBAaM,OAbN,YAaM,CAZJ,YAEW,kBAAA;KAFD,eAAY;KAAoB,SAAQ;KAAW,MAAM,QAAA;KAAa,SAAO;;4BACrE,CAAA,gBAAA,gBAAb,QAAA,WAAU,EAAA,EAAA,CAAA,CAAA;;qBAEf,YAQW,kBAAA;KAPT,eAAY;KACX,SAAS,QAAA;KACT,MAAM,QAAA;KACN,MAAM,QAAA;KACN,SAAO;;4BAES,CAAA,gBAAA,gBAAd,QAAA,YAAW,EAAA,EAAA,CAAA,CAAA;;;;;;;2BAff,CAJP,WAIO,KAAA,QAAA,WAAA,EAAA,QAAA,CAHM,QAAA,eAAA,WAAA,EAAX,mBAEM,OAFN,YAEM,gBADD,QAAA,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA"}
@@ -1,3 +1,3 @@
1
- import { t as GrConfirmDialog_default } from "./chunks/GrConfirmDialog-DjGOX9Hd.js";
2
- import { t as grConfirmDialogConfig } from "../../chunks/config-C_AOYGBr.js";
1
+ import { t as GrConfirmDialog_default } from "./chunks/GrConfirmDialog-DE-d77Us.js";
2
+ import { t as grConfirmDialogConfig } from "../../chunks/config-iDTkWUdh2.js";
3
3
  export { GrConfirmDialog_default as GrConfirmDialog, GrConfirmDialog_default as default, grConfirmDialogConfig };
@@ -1,3 +1,3 @@
1
1
  import { t as GrDataTable_default } from "./chunks/GrDataTable-QDOJWmdY.js";
2
- import { t as grDataTableConfig } from "../../chunks/config-DJWf0kWe.js";
2
+ import { t as grDataTableConfig } from "../../chunks/config-DK2NpdyX2.js";
3
3
  export { GrDataTable_default as GrDataTable, GrDataTable_default as default, grDataTableConfig };
@@ -1,5 +1,5 @@
1
1
  import { t as x_default } from "../../../chunks/x-Pa6C2hwi.js";
2
- import { t as GrButton_default } from "../../GrButton/chunks/GrButton-BmGdp8-4.js";
2
+ import { t as GrButton_default } from "../../GrButton/chunks/GrButton-CPDKonoB.js";
3
3
  import { a as Ve } from "../../../chunks/transition-mJ3PZCHH.js";
4
4
  import { t as GrModal_default } from "../../GrModal/chunks/GrModal-gvfvlp7k.js";
5
5
  import { a as resolveGrDialogTitle, i as resolveGrDialogSectionConfig, n as DEFAULT_GR_DIALOG_FOOTER_CONFIG, r as DEFAULT_GR_DIALOG_HEADER_CONFIG, t as DEFAULT_GR_DIALOG_BODY_CONFIG } from "../../../chunks/dialogShared-BtXUk6Zc.js";
@@ -1 +1 @@
1
- {"version":3,"file":"GrDialog-gObmaVEl.js","names":["$emit","$emit"],"sources":["../../../../src/components/GrDialog/GrDialogFooter.vue","../../../../src/components/GrDialog/GrDialogFooter.vue","../../../../src/components/GrDialog/GrDialogCloseButton.vue","../../../../src/components/GrDialog/GrDialogCloseButton.vue","../../../../src/components/GrDialog/GrDialogHeader.vue","../../../../src/components/GrDialog/GrDialogHeader.vue","../../../../src/components/GrDialog/GrDialog.vue","../../../../src/components/GrDialog/GrDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport {\n DEFAULT_GR_DIALOG_FOOTER_CONFIG,\n resolveGrDialogSectionConfig,\n type GrDialogSectionConfig,\n} from './dialogShared'\n\nexport interface GrDialogFooterProps {\n config?: GrDialogSectionConfig\n}\n\nconst props = withDefaults(defineProps<GrDialogFooterProps>(), {\n config: undefined,\n})\n\nconst resolvedConfig = computed(() => resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_FOOTER_CONFIG))\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-t border-[var(--brd)]' : '',\n])\n</script>\n\n<template>\n <div data-ds-dialog-footer :class=\"rootClass\">\n <slot />\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport {\n DEFAULT_GR_DIALOG_FOOTER_CONFIG,\n resolveGrDialogSectionConfig,\n type GrDialogSectionConfig,\n} from './dialogShared'\n\nexport interface GrDialogFooterProps {\n config?: GrDialogSectionConfig\n}\n\nconst props = withDefaults(defineProps<GrDialogFooterProps>(), {\n config: undefined,\n})\n\nconst resolvedConfig = computed(() => resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_FOOTER_CONFIG))\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-t border-[var(--brd)]' : '',\n])\n</script>\n\n<template>\n <div data-ds-dialog-footer :class=\"rootClass\">\n <slot />\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport IconClose from '~icons/lucide/x'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\n// A11y-лейбл: берём из атрибутов (родитель прокидывает `:aria-label=\"closeLabel\"`)\n// или используем дефолт. Не хардкодим английское \"Close\" — DS должен быть i18n-friendly.\nconst attrs = useAttrs()\nconst ariaLabel = computed(() => (attrs['aria-label'] as string | undefined) ?? 'Close')\n\ndefineEmits<{\n (e: 'click'): void\n}>()\n</script>\n\n<template>\n <GrButton\n v-bind=\"attrs\"\n data-ds-dialog-close\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"ariaLabel\"\n @click=\"$emit('click')\"\n >\n <IconClose class=\"h-4 w-4\" aria-hidden=\"true\" />\n </GrButton>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport IconClose from '~icons/lucide/x'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\n// A11y-лейбл: берём из атрибутов (родитель прокидывает `:aria-label=\"closeLabel\"`)\n// или используем дефолт. Не хардкодим английское \"Close\" — DS должен быть i18n-friendly.\nconst attrs = useAttrs()\nconst ariaLabel = computed(() => (attrs['aria-label'] as string | undefined) ?? 'Close')\n\ndefineEmits<{\n (e: 'click'): void\n}>()\n</script>\n\n<template>\n <GrButton\n v-bind=\"attrs\"\n data-ds-dialog-close\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"ariaLabel\"\n @click=\"$emit('click')\"\n >\n <IconClose class=\"h-4 w-4\" aria-hidden=\"true\" />\n </GrButton>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { DialogTitle } from '@headlessui/vue'\n\nimport GrDialogCloseButton from './GrDialogCloseButton.vue'\nimport {\n DEFAULT_GR_DIALOG_HEADER_CONFIG,\n type GrDialogSectionConfig,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogHeaderProps {\n title?: string\n showCloseButton?: boolean\n config?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogHeaderProps>(), {\n title: undefined,\n showCloseButton: true,\n config: undefined,\n closeLabel: 'Close',\n})\n\nconst slots = defineSlots<{\n default?: (props: { title?: string }) => any\n}>()\n\ndefineEmits<{\n (e: 'close'): void\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst resolvedConfig = computed(() =>\n resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_HEADER_CONFIG),\n)\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-b border-[var(--brd)]' : '',\n 'flex items-center justify-between gap-4',\n])\n\n// Видимый `DialogTitle` рендерим только когда нет пользовательского слота:\n// если слот `#header` задан, a11y-title отдаётся через `GrModal #title`\n// на уровне `GrDialog` (см. JSDoc у `GrDialog.vue`).\nconst showVisibleTitle = computed(() => !slots.default && !!resolvedTitle.value)\n</script>\n\n<template>\n <div data-ds-dialog-header :class=\"rootClass\">\n <template v-if=\"slots.default\">\n <div class=\"flex-1 min-w-0 flex items-center\">\n <slot :title=\"resolvedTitle\" />\n </div>\n </template>\n <DialogTitle\n v-else-if=\"showVisibleTitle\"\n as=\"div\"\n class=\"flex-1 min-w-0 text-[14px] font-700\"\n >\n {{ resolvedTitle }}\n </DialogTitle>\n <div v-else class=\"flex-1 min-w-0\" />\n <GrDialogCloseButton\n v-if=\"showCloseButton\"\n :aria-label=\"closeLabel\"\n @click=\"$emit('close')\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { DialogTitle } from '@headlessui/vue'\n\nimport GrDialogCloseButton from './GrDialogCloseButton.vue'\nimport {\n DEFAULT_GR_DIALOG_HEADER_CONFIG,\n type GrDialogSectionConfig,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogHeaderProps {\n title?: string\n showCloseButton?: boolean\n config?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogHeaderProps>(), {\n title: undefined,\n showCloseButton: true,\n config: undefined,\n closeLabel: 'Close',\n})\n\nconst slots = defineSlots<{\n default?: (props: { title?: string }) => any\n}>()\n\ndefineEmits<{\n (e: 'close'): void\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst resolvedConfig = computed(() =>\n resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_HEADER_CONFIG),\n)\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-b border-[var(--brd)]' : '',\n 'flex items-center justify-between gap-4',\n])\n\n// Видимый `DialogTitle` рендерим только когда нет пользовательского слота:\n// если слот `#header` задан, a11y-title отдаётся через `GrModal #title`\n// на уровне `GrDialog` (см. JSDoc у `GrDialog.vue`).\nconst showVisibleTitle = computed(() => !slots.default && !!resolvedTitle.value)\n</script>\n\n<template>\n <div data-ds-dialog-header :class=\"rootClass\">\n <template v-if=\"slots.default\">\n <div class=\"flex-1 min-w-0 flex items-center\">\n <slot :title=\"resolvedTitle\" />\n </div>\n </template>\n <DialogTitle\n v-else-if=\"showVisibleTitle\"\n as=\"div\"\n class=\"flex-1 min-w-0 text-[14px] font-700\"\n >\n {{ resolvedTitle }}\n </DialogTitle>\n <div v-else class=\"flex-1 min-w-0\" />\n <GrDialogCloseButton\n v-if=\"showCloseButton\"\n :aria-label=\"closeLabel\"\n @click=\"$emit('close')\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialog — DS-примитив диалога поверх `GrModal`.\n *\n * Архитектурно:\n * - ширина/радиус/скролл-лок/Esc/бэкдроп — ответственность `GrModal`;\n * - заголовок/подвал/кнопка закрытия/паддинги секций — ответственность `GrDialog`.\n *\n * A11y: ровно один `DialogTitle` на открытое окно.\n * - Если хедер отображается без слота `#header` — видимый `DialogTitle` рендерит\n * `GrDialogHeader`.\n * - В остальных случаях (`!showHeader`, либо `showHeader` со слотом `#header`)\n * `GrDialog` отдаёт sr-only title в слот `#title` у `GrModal`, чтобы\n * HeadlessUI корректно проставил `aria-labelledby` на панель.\n */\nimport { computed } from 'vue'\n\nimport GrModal from '../GrModal/GrModal.vue'\nimport GrDialogFooter from './GrDialogFooter.vue'\nimport GrDialogHeader from './GrDialogHeader.vue'\nimport {\n DEFAULT_GR_DIALOG_BODY_CONFIG,\n type GrDialogSectionConfig,\n type GrDialogSize,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogProps {\n modelValue: boolean\n title?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst slots = defineSlots<{\n default?: () => any\n header?: (props: { title?: string }) => any\n footer?: () => any\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst bodyClass = computed(() => {\n const cfg = resolveGrDialogSectionConfig(props.bodyConfig, DEFAULT_GR_DIALOG_BODY_CONFIG)\n return [cfg.paddingX, cfg.paddingY]\n})\n\n// Нужен ли sr-only title через `GrModal #title`:\n// - `!showHeader` — вообще нет заголовка в разметке;\n// - `showHeader && $slots.header` — кастомный хедер не обязан содержать DialogTitle.\n// В обоих случаях даём HeadlessUI a11y-title, не дублируя его визуально.\nconst useModalSrOnlyTitle = computed(\n () => !!resolvedTitle.value && (!props.showHeader || !!slots.header),\n)\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrModal\n :model-value=\"modelValue\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <template v-if=\"useModalSrOnlyTitle\" #title>\n <span class=\"sr-only\">{{ resolvedTitle }}</span>\n </template>\n\n <div class=\"overflow-hidden rounded-[inherit]\">\n <GrDialogHeader\n v-if=\"showHeader\"\n :title=\"resolvedTitle\"\n :show-close-button=\"showCloseButton\"\n :config=\"headerConfig\"\n :close-label=\"closeLabel\"\n @close=\"close\"\n >\n <template v-if=\"slots.header\" #default=\"slotProps\">\n <slot name=\"header\" v-bind=\"slotProps\" />\n </template>\n </GrDialogHeader>\n\n <div :class=\"bodyClass\">\n <slot />\n </div>\n\n <GrDialogFooter v-if=\"slots.footer\" :config=\"footerConfig\">\n <slot name=\"footer\" />\n </GrDialogFooter>\n </div>\n </GrModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialog — DS-примитив диалога поверх `GrModal`.\n *\n * Архитектурно:\n * - ширина/радиус/скролл-лок/Esc/бэкдроп — ответственность `GrModal`;\n * - заголовок/подвал/кнопка закрытия/паддинги секций — ответственность `GrDialog`.\n *\n * A11y: ровно один `DialogTitle` на открытое окно.\n * - Если хедер отображается без слота `#header` — видимый `DialogTitle` рендерит\n * `GrDialogHeader`.\n * - В остальных случаях (`!showHeader`, либо `showHeader` со слотом `#header`)\n * `GrDialog` отдаёт sr-only title в слот `#title` у `GrModal`, чтобы\n * HeadlessUI корректно проставил `aria-labelledby` на панель.\n */\nimport { computed } from 'vue'\n\nimport GrModal from '../GrModal/GrModal.vue'\nimport GrDialogFooter from './GrDialogFooter.vue'\nimport GrDialogHeader from './GrDialogHeader.vue'\nimport {\n DEFAULT_GR_DIALOG_BODY_CONFIG,\n type GrDialogSectionConfig,\n type GrDialogSize,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogProps {\n modelValue: boolean\n title?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst slots = defineSlots<{\n default?: () => any\n header?: (props: { title?: string }) => any\n footer?: () => any\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst bodyClass = computed(() => {\n const cfg = resolveGrDialogSectionConfig(props.bodyConfig, DEFAULT_GR_DIALOG_BODY_CONFIG)\n return [cfg.paddingX, cfg.paddingY]\n})\n\n// Нужен ли sr-only title через `GrModal #title`:\n// - `!showHeader` — вообще нет заголовка в разметке;\n// - `showHeader && $slots.header` — кастомный хедер не обязан содержать DialogTitle.\n// В обоих случаях даём HeadlessUI a11y-title, не дублируя его визуально.\nconst useModalSrOnlyTitle = computed(\n () => !!resolvedTitle.value && (!props.showHeader || !!slots.header),\n)\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrModal\n :model-value=\"modelValue\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <template v-if=\"useModalSrOnlyTitle\" #title>\n <span class=\"sr-only\">{{ resolvedTitle }}</span>\n </template>\n\n <div class=\"overflow-hidden rounded-[inherit]\">\n <GrDialogHeader\n v-if=\"showHeader\"\n :title=\"resolvedTitle\"\n :show-close-button=\"showCloseButton\"\n :config=\"headerConfig\"\n :close-label=\"closeLabel\"\n @close=\"close\"\n >\n <template v-if=\"slots.header\" #default=\"slotProps\">\n <slot name=\"header\" v-bind=\"slotProps\" />\n </template>\n </GrDialogHeader>\n\n <div :class=\"bodyClass\">\n <slot />\n </div>\n\n <GrDialogFooter v-if=\"slots.footer\" :config=\"footerConfig\">\n <slot name=\"footer\" />\n </GrDialogFooter>\n </div>\n </GrModal>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"GrDialog-Ba3Ojcsq.js","names":["$emit","$emit"],"sources":["../../../../src/components/GrDialog/GrDialogFooter.vue","../../../../src/components/GrDialog/GrDialogFooter.vue","../../../../src/components/GrDialog/GrDialogCloseButton.vue","../../../../src/components/GrDialog/GrDialogCloseButton.vue","../../../../src/components/GrDialog/GrDialogHeader.vue","../../../../src/components/GrDialog/GrDialogHeader.vue","../../../../src/components/GrDialog/GrDialog.vue","../../../../src/components/GrDialog/GrDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport {\n DEFAULT_GR_DIALOG_FOOTER_CONFIG,\n resolveGrDialogSectionConfig,\n type GrDialogSectionConfig,\n} from './dialogShared'\n\nexport interface GrDialogFooterProps {\n config?: GrDialogSectionConfig\n}\n\nconst props = withDefaults(defineProps<GrDialogFooterProps>(), {\n config: undefined,\n})\n\nconst resolvedConfig = computed(() => resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_FOOTER_CONFIG))\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-t border-[var(--brd)]' : '',\n])\n</script>\n\n<template>\n <div data-ds-dialog-footer :class=\"rootClass\">\n <slot />\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport {\n DEFAULT_GR_DIALOG_FOOTER_CONFIG,\n resolveGrDialogSectionConfig,\n type GrDialogSectionConfig,\n} from './dialogShared'\n\nexport interface GrDialogFooterProps {\n config?: GrDialogSectionConfig\n}\n\nconst props = withDefaults(defineProps<GrDialogFooterProps>(), {\n config: undefined,\n})\n\nconst resolvedConfig = computed(() => resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_FOOTER_CONFIG))\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-t border-[var(--brd)]' : '',\n])\n</script>\n\n<template>\n <div data-ds-dialog-footer :class=\"rootClass\">\n <slot />\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport IconClose from '~icons/lucide/x'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\n// A11y-лейбл: берём из атрибутов (родитель прокидывает `:aria-label=\"closeLabel\"`)\n// или используем дефолт. Не хардкодим английское \"Close\" — DS должен быть i18n-friendly.\nconst attrs = useAttrs()\nconst ariaLabel = computed(() => (attrs['aria-label'] as string | undefined) ?? 'Close')\n\ndefineEmits<{\n (e: 'click'): void\n}>()\n</script>\n\n<template>\n <GrButton\n v-bind=\"attrs\"\n data-ds-dialog-close\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"ariaLabel\"\n @click=\"$emit('click')\"\n >\n <IconClose class=\"h-4 w-4\" aria-hidden=\"true\" />\n </GrButton>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport IconClose from '~icons/lucide/x'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\n// A11y-лейбл: берём из атрибутов (родитель прокидывает `:aria-label=\"closeLabel\"`)\n// или используем дефолт. Не хардкодим английское \"Close\" — DS должен быть i18n-friendly.\nconst attrs = useAttrs()\nconst ariaLabel = computed(() => (attrs['aria-label'] as string | undefined) ?? 'Close')\n\ndefineEmits<{\n (e: 'click'): void\n}>()\n</script>\n\n<template>\n <GrButton\n v-bind=\"attrs\"\n data-ds-dialog-close\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"ariaLabel\"\n @click=\"$emit('click')\"\n >\n <IconClose class=\"h-4 w-4\" aria-hidden=\"true\" />\n </GrButton>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { DialogTitle } from '@headlessui/vue'\n\nimport GrDialogCloseButton from './GrDialogCloseButton.vue'\nimport {\n DEFAULT_GR_DIALOG_HEADER_CONFIG,\n type GrDialogSectionConfig,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogHeaderProps {\n title?: string\n showCloseButton?: boolean\n config?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogHeaderProps>(), {\n title: undefined,\n showCloseButton: true,\n config: undefined,\n closeLabel: 'Close',\n})\n\nconst slots = defineSlots<{\n default?: (props: { title?: string }) => any\n}>()\n\ndefineEmits<{\n (e: 'close'): void\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst resolvedConfig = computed(() =>\n resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_HEADER_CONFIG),\n)\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-b border-[var(--brd)]' : '',\n 'flex items-center justify-between gap-4',\n])\n\n// Видимый `DialogTitle` рендерим только когда нет пользовательского слота:\n// если слот `#header` задан, a11y-title отдаётся через `GrModal #title`\n// на уровне `GrDialog` (см. JSDoc у `GrDialog.vue`).\nconst showVisibleTitle = computed(() => !slots.default && !!resolvedTitle.value)\n</script>\n\n<template>\n <div data-ds-dialog-header :class=\"rootClass\">\n <template v-if=\"slots.default\">\n <div class=\"flex-1 min-w-0 flex items-center\">\n <slot :title=\"resolvedTitle\" />\n </div>\n </template>\n <DialogTitle\n v-else-if=\"showVisibleTitle\"\n as=\"div\"\n class=\"flex-1 min-w-0 text-[14px] font-700\"\n >\n {{ resolvedTitle }}\n </DialogTitle>\n <div v-else class=\"flex-1 min-w-0\" />\n <GrDialogCloseButton\n v-if=\"showCloseButton\"\n :aria-label=\"closeLabel\"\n @click=\"$emit('close')\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { DialogTitle } from '@headlessui/vue'\n\nimport GrDialogCloseButton from './GrDialogCloseButton.vue'\nimport {\n DEFAULT_GR_DIALOG_HEADER_CONFIG,\n type GrDialogSectionConfig,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogHeaderProps {\n title?: string\n showCloseButton?: boolean\n config?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogHeaderProps>(), {\n title: undefined,\n showCloseButton: true,\n config: undefined,\n closeLabel: 'Close',\n})\n\nconst slots = defineSlots<{\n default?: (props: { title?: string }) => any\n}>()\n\ndefineEmits<{\n (e: 'close'): void\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst resolvedConfig = computed(() =>\n resolveGrDialogSectionConfig(props.config, DEFAULT_GR_DIALOG_HEADER_CONFIG),\n)\n\nconst rootClass = computed(() => [\n resolvedConfig.value.paddingX,\n resolvedConfig.value.paddingY,\n resolvedConfig.value.bordered ? 'border-b border-[var(--brd)]' : '',\n 'flex items-center justify-between gap-4',\n])\n\n// Видимый `DialogTitle` рендерим только когда нет пользовательского слота:\n// если слот `#header` задан, a11y-title отдаётся через `GrModal #title`\n// на уровне `GrDialog` (см. JSDoc у `GrDialog.vue`).\nconst showVisibleTitle = computed(() => !slots.default && !!resolvedTitle.value)\n</script>\n\n<template>\n <div data-ds-dialog-header :class=\"rootClass\">\n <template v-if=\"slots.default\">\n <div class=\"flex-1 min-w-0 flex items-center\">\n <slot :title=\"resolvedTitle\" />\n </div>\n </template>\n <DialogTitle\n v-else-if=\"showVisibleTitle\"\n as=\"div\"\n class=\"flex-1 min-w-0 text-[14px] font-700\"\n >\n {{ resolvedTitle }}\n </DialogTitle>\n <div v-else class=\"flex-1 min-w-0\" />\n <GrDialogCloseButton\n v-if=\"showCloseButton\"\n :aria-label=\"closeLabel\"\n @click=\"$emit('close')\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialog — DS-примитив диалога поверх `GrModal`.\n *\n * Архитектурно:\n * - ширина/радиус/скролл-лок/Esc/бэкдроп — ответственность `GrModal`;\n * - заголовок/подвал/кнопка закрытия/паддинги секций — ответственность `GrDialog`.\n *\n * A11y: ровно один `DialogTitle` на открытое окно.\n * - Если хедер отображается без слота `#header` — видимый `DialogTitle` рендерит\n * `GrDialogHeader`.\n * - В остальных случаях (`!showHeader`, либо `showHeader` со слотом `#header`)\n * `GrDialog` отдаёт sr-only title в слот `#title` у `GrModal`, чтобы\n * HeadlessUI корректно проставил `aria-labelledby` на панель.\n */\nimport { computed } from 'vue'\n\nimport GrModal from '../GrModal/GrModal.vue'\nimport GrDialogFooter from './GrDialogFooter.vue'\nimport GrDialogHeader from './GrDialogHeader.vue'\nimport {\n DEFAULT_GR_DIALOG_BODY_CONFIG,\n type GrDialogSectionConfig,\n type GrDialogSize,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogProps {\n modelValue: boolean\n title?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst slots = defineSlots<{\n default?: () => any\n header?: (props: { title?: string }) => any\n footer?: () => any\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst bodyClass = computed(() => {\n const cfg = resolveGrDialogSectionConfig(props.bodyConfig, DEFAULT_GR_DIALOG_BODY_CONFIG)\n return [cfg.paddingX, cfg.paddingY]\n})\n\n// Нужен ли sr-only title через `GrModal #title`:\n// - `!showHeader` — вообще нет заголовка в разметке;\n// - `showHeader && $slots.header` — кастомный хедер не обязан содержать DialogTitle.\n// В обоих случаях даём HeadlessUI a11y-title, не дублируя его визуально.\nconst useModalSrOnlyTitle = computed(\n () => !!resolvedTitle.value && (!props.showHeader || !!slots.header),\n)\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrModal\n :model-value=\"modelValue\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <template v-if=\"useModalSrOnlyTitle\" #title>\n <span class=\"sr-only\">{{ resolvedTitle }}</span>\n </template>\n\n <div class=\"overflow-hidden rounded-[inherit]\">\n <GrDialogHeader\n v-if=\"showHeader\"\n :title=\"resolvedTitle\"\n :show-close-button=\"showCloseButton\"\n :config=\"headerConfig\"\n :close-label=\"closeLabel\"\n @close=\"close\"\n >\n <template v-if=\"slots.header\" #default=\"slotProps\">\n <slot name=\"header\" v-bind=\"slotProps\" />\n </template>\n </GrDialogHeader>\n\n <div :class=\"bodyClass\">\n <slot />\n </div>\n\n <GrDialogFooter v-if=\"slots.footer\" :config=\"footerConfig\">\n <slot name=\"footer\" />\n </GrDialogFooter>\n </div>\n </GrModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialog — DS-примитив диалога поверх `GrModal`.\n *\n * Архитектурно:\n * - ширина/радиус/скролл-лок/Esc/бэкдроп — ответственность `GrModal`;\n * - заголовок/подвал/кнопка закрытия/паддинги секций — ответственность `GrDialog`.\n *\n * A11y: ровно один `DialogTitle` на открытое окно.\n * - Если хедер отображается без слота `#header` — видимый `DialogTitle` рендерит\n * `GrDialogHeader`.\n * - В остальных случаях (`!showHeader`, либо `showHeader` со слотом `#header`)\n * `GrDialog` отдаёт sr-only title в слот `#title` у `GrModal`, чтобы\n * HeadlessUI корректно проставил `aria-labelledby` на панель.\n */\nimport { computed } from 'vue'\n\nimport GrModal from '../GrModal/GrModal.vue'\nimport GrDialogFooter from './GrDialogFooter.vue'\nimport GrDialogHeader from './GrDialogHeader.vue'\nimport {\n DEFAULT_GR_DIALOG_BODY_CONFIG,\n type GrDialogSectionConfig,\n type GrDialogSize,\n resolveGrDialogSectionConfig,\n resolveGrDialogTitle,\n} from './dialogShared'\n\nexport interface GrDialogProps {\n modelValue: boolean\n title?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDialogProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst slots = defineSlots<{\n default?: () => any\n header?: (props: { title?: string }) => any\n footer?: () => any\n}>()\n\nconst resolvedTitle = computed(() => resolveGrDialogTitle(props.title))\n\nconst bodyClass = computed(() => {\n const cfg = resolveGrDialogSectionConfig(props.bodyConfig, DEFAULT_GR_DIALOG_BODY_CONFIG)\n return [cfg.paddingX, cfg.paddingY]\n})\n\n// Нужен ли sr-only title через `GrModal #title`:\n// - `!showHeader` — вообще нет заголовка в разметке;\n// - `showHeader && $slots.header` — кастомный хедер не обязан содержать DialogTitle.\n// В обоих случаях даём HeadlessUI a11y-title, не дублируя его визуально.\nconst useModalSrOnlyTitle = computed(\n () => !!resolvedTitle.value && (!props.showHeader || !!slots.header),\n)\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrModal\n :model-value=\"modelValue\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <template v-if=\"useModalSrOnlyTitle\" #title>\n <span class=\"sr-only\">{{ resolvedTitle }}</span>\n </template>\n\n <div class=\"overflow-hidden rounded-[inherit]\">\n <GrDialogHeader\n v-if=\"showHeader\"\n :title=\"resolvedTitle\"\n :show-close-button=\"showCloseButton\"\n :config=\"headerConfig\"\n :close-label=\"closeLabel\"\n @close=\"close\"\n >\n <template v-if=\"slots.header\" #default=\"slotProps\">\n <slot name=\"header\" v-bind=\"slotProps\" />\n </template>\n </GrDialogHeader>\n\n <div :class=\"bodyClass\">\n <slot />\n </div>\n\n <GrDialogFooter v-if=\"slots.footer\" :config=\"footerConfig\">\n <slot name=\"footer\" />\n </GrDialogFooter>\n </div>\n </GrModal>\n</template>\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  import { a as resolveGrDialogTitle, i as resolveGrDialogSectionConfig, n as DEFAULT_GR_DIALOG_FOOTER_CONFIG, r as DEFAULT_GR_DIALOG_HEADER_CONFIG, t as DEFAULT_GR_DIALOG_BODY_CONFIG } from "../../chunks/dialogShared-BtXUk6Zc.js";
2
- import { i as GrDialogFooter_default, n as GrDialogHeader_default, r as GrDialogCloseButton_default, t as GrDialog_default } from "./chunks/GrDialog-gObmaVEl.js";
2
+ import { i as GrDialogFooter_default, n as GrDialogHeader_default, r as GrDialogCloseButton_default, t as GrDialog_default } from "./chunks/GrDialog-Ba3Ojcsq.js";
3
3
  import { t as grDialogSafelist } from "../../chunks/safelist-BF822K15.js";
4
4
  export { DEFAULT_GR_DIALOG_BODY_CONFIG, DEFAULT_GR_DIALOG_FOOTER_CONFIG, DEFAULT_GR_DIALOG_HEADER_CONFIG, GrDialog_default as GrDialog, GrDialog_default as default, GrDialogCloseButton_default as GrDialogCloseButton, GrDialogFooter_default as GrDialogFooter, GrDialogHeader_default as GrDialogHeader, grDialogSafelist, resolveGrDialogSectionConfig, resolveGrDialogTitle };
@@ -1,5 +1,5 @@
1
1
  import { t as x_default } from "../../../chunks/x-Pa6C2hwi.js";
2
- import { t as GrButton_default } from "../../GrButton/chunks/GrButton-BmGdp8-4.js";
2
+ import { t as GrButton_default } from "../../GrButton/chunks/GrButton-CPDKonoB.js";
3
3
  import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-BV7RBU_0.js";
4
4
  import { a as Ve, n as he, o as Ye, r as Ge, t as Se } from "../../../chunks/transition-mJ3PZCHH.js";
5
5
  import { i as grDrawerPanelEnterFrom, r as grDrawerPanelClass } from "../../../chunks/config-DxqpV-KM.js";
@@ -1 +1 @@
1
- {"version":3,"file":"GrDrawer-BJL_gTxI.js","names":["$slots"],"sources":["../../../../src/components/GrDrawer/GrDrawer.vue","../../../../src/components/GrDrawer/GrDrawer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\nimport { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport {\n grDrawerPanelClass,\n grDrawerPanelEnterFrom,\n type GrDrawerSide,\n type GrDrawerSize,\n} from './grDrawerStyles'\n\nimport IconClose from '~icons/lucide/x'\n\nexport interface GrDrawerProps {\n /** Контроль открытия через v-model. */\n modelValue: boolean\n /** Заголовок; если передан — покажется в хедере. Можно переопределить слотом `#title`. */\n title?: string\n /** Закрывать при клике по бэкдропу / через HeadlessUI close. */\n closeOnBackdrop?: boolean\n /** Закрывать по Esc. */\n closeOnEsc?: boolean\n /** Сторона, с которой выезжает панель. */\n side?: GrDrawerSide\n /** Размер панели. */\n size?: GrDrawerSize\n /** i18n-friendly aria-label для кнопки закрытия. */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDrawerProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n side: 'right',\n size: 'md',\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst titleId = useId()\n\nconst panelClass = computed(() => {\n return grDrawerPanelClass({ side: props.side, size: props.size })\n})\n\nconst panelEnterFrom = computed(() => grDrawerPanelEnterFrom(props.side))\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n\nfunction onDialogClose(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onOverlayClick(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (e.key !== 'Escape') return\n if (!props.closeOnEsc) return\n e.stopPropagation()\n e.preventDefault()\n close()\n}\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"modelValue\" as=\"template\">\n <Dialog\n as=\"div\"\n data-ds-drawer\n class=\"fixed inset-0 z-50\"\n :static=\"true\"\n :aria-labelledby=\"titleId\"\n @close=\"onDialogClose\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-150 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-drawer-overlay\n class=\"fixed inset-0 bg-black/40\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n :enter-from=\"panelEnterFrom\"\n enter-to=\"translate-x-0\"\n leave=\"duration-150 ease-in\"\n leave-from=\"translate-x-0\"\n :leave-to=\"panelEnterFrom\"\n >\n <DialogPanel\n data-ds-drawer-panel\n tabindex=\"-1\"\n class=\"fixed inset-y-0 flex flex-col\"\n :class=\"panelClass\"\n >\n <div\n data-ds-drawer-header\n class=\"px-5 py-4 border-b border-[var(--brd)] flex items-center justify-between gap-4\"\n >\n <DialogTitle :id=\"titleId\" as=\"div\" data-ds-drawer-title class=\"text-[14px] font-700 min-w-0 truncate\">\n <slot name=\"title\">\n {{ title ?? 'Drawer' }}\n </slot>\n </DialogTitle>\n\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"closeLabel\"\n @click=\"close\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <div data-ds-drawer-body class=\"flex-1 overflow-y-auto\">\n <div class=\"p-5\">\n <slot />\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n data-ds-drawer-footer\n class=\"px-5 py-4 border-t border-[var(--brd)]\"\n >\n <slot name=\"footer\" />\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\nimport { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport {\n grDrawerPanelClass,\n grDrawerPanelEnterFrom,\n type GrDrawerSide,\n type GrDrawerSize,\n} from './grDrawerStyles'\n\nimport IconClose from '~icons/lucide/x'\n\nexport interface GrDrawerProps {\n /** Контроль открытия через v-model. */\n modelValue: boolean\n /** Заголовок; если передан — покажется в хедере. Можно переопределить слотом `#title`. */\n title?: string\n /** Закрывать при клике по бэкдропу / через HeadlessUI close. */\n closeOnBackdrop?: boolean\n /** Закрывать по Esc. */\n closeOnEsc?: boolean\n /** Сторона, с которой выезжает панель. */\n side?: GrDrawerSide\n /** Размер панели. */\n size?: GrDrawerSize\n /** i18n-friendly aria-label для кнопки закрытия. */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDrawerProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n side: 'right',\n size: 'md',\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst titleId = useId()\n\nconst panelClass = computed(() => {\n return grDrawerPanelClass({ side: props.side, size: props.size })\n})\n\nconst panelEnterFrom = computed(() => grDrawerPanelEnterFrom(props.side))\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n\nfunction onDialogClose(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onOverlayClick(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (e.key !== 'Escape') return\n if (!props.closeOnEsc) return\n e.stopPropagation()\n e.preventDefault()\n close()\n}\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"modelValue\" as=\"template\">\n <Dialog\n as=\"div\"\n data-ds-drawer\n class=\"fixed inset-0 z-50\"\n :static=\"true\"\n :aria-labelledby=\"titleId\"\n @close=\"onDialogClose\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-150 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-drawer-overlay\n class=\"fixed inset-0 bg-black/40\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n :enter-from=\"panelEnterFrom\"\n enter-to=\"translate-x-0\"\n leave=\"duration-150 ease-in\"\n leave-from=\"translate-x-0\"\n :leave-to=\"panelEnterFrom\"\n >\n <DialogPanel\n data-ds-drawer-panel\n tabindex=\"-1\"\n class=\"fixed inset-y-0 flex flex-col\"\n :class=\"panelClass\"\n >\n <div\n data-ds-drawer-header\n class=\"px-5 py-4 border-b border-[var(--brd)] flex items-center justify-between gap-4\"\n >\n <DialogTitle :id=\"titleId\" as=\"div\" data-ds-drawer-title class=\"text-[14px] font-700 min-w-0 truncate\">\n <slot name=\"title\">\n {{ title ?? 'Drawer' }}\n </slot>\n </DialogTitle>\n\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"closeLabel\"\n @click=\"close\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <div data-ds-drawer-body class=\"flex-1 overflow-y-auto\">\n <div class=\"p-5\">\n <slot />\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n data-ds-drawer-footer\n class=\"px-5 py-4 border-t border-[var(--brd)]\"\n >\n <slot name=\"footer\" />\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCA,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,MAAM,UAAU,OAAM;EAEtB,MAAM,aAAa,eAAe;AAChC,UAAO,mBAAmB;IAAE,MAAM,MAAM;IAAM,MAAM,MAAM;IAAM,CAAA;IACjE;EAED,MAAM,iBAAiB,eAAe,uBAAuB,MAAM,KAAK,CAAA;EAExE,SAAS,QAAc;AACrB,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,gBAAsB;AAC7B,OAAI,CAAC,MAAM,gBAAiB;AAC5B,UAAM;;EAGR,SAAS,iBAAuB;AAC9B,OAAI,CAAC,MAAM,gBAAiB;AAC5B,UAAM;;EAGR,SAAS,UAAU,GAAwB;AACzC,OAAI,EAAE,QAAQ,SAAU;AACxB,OAAI,CAAC,MAAM,WAAY;AACvB,KAAE,iBAAgB;AAClB,KAAE,gBAAe;AACjB,UAAM;;;uBAKN,YAqFW,UAAA,EArFD,IAAG,QAAM,EAAA,CACjB,YAmFiB,MAAA,GAAA,EAAA;IAnFA,MAAM,QAAA;IAAY,IAAG;;2BAkF3B,CAjFT,YAiFS,MAAA,GAAA,EAAA;KAhFP,IAAG;KACH,kBAAA;KACA,OAAM;KACL,QAAQ;KACR,mBAAiB,MAAA,QAAO;KACxB,SAAO;KACE;;4BAyEJ,CAvEN,mBAuEM,OAvEN,YAuEM,CAtEJ,YAekB,MAAA,GAAA,EAAA;MAdhB,IAAG;MACH,OAAM;MACN,cAAW;MACX,YAAS;MACT,OAAM;MACN,cAAW;MACX,YAAS;;6BAOP,CALF,mBAKE,OAAA;OAJA,0BAAA;OACA,OAAM;OACN,eAAY;OACX,SAAO;;;SAIZ,YAoDkB,MAAA,GAAA,EAAA;MAnDhB,IAAG;MACH,OAAM;MACL,cAAY,eAAA;MACb,YAAS;MACT,OAAM;MACN,cAAW;MACV,YAAU,eAAA;;6BA4CG,CA1Cd,YA0Cc,MAAA,GAAA,EAAA;OAzCZ,wBAAA;OACA,UAAS;OACT,OAAK,eAAA,CAAC,iCACE,WAAA,MAAU,CAAA;;8BAuBZ;QArBN,mBAqBM,OArBN,YAqBM,CAjBJ,YAIc,MAAA,GAAA,EAAA;SAJA,IAAI,MAAA,QAAO;SAAE,IAAG;SAAM,wBAAA;SAAqB,OAAM;;gCAGtD,CAFP,WAEO,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBADF,QAAA,SAAK,SAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA;;uBAIZ,YAUW,kBAAA;SATT,SAAQ;SACR,MAAK;SACL,QAAA;SACC,cAAY,QAAA;SACZ,SAAO;;gCAIC,CAFT,YAES,gBAAA;UAFD,MAAK;UAAK,eAAY;;iCACf,CAAb,YAAa,MAAA,UAAA,CAAA,CAAA,CAAA;;;;;QAKnB,mBAIM,OAJN,YAIM,CAHJ,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA,CAAA,CAAA;QAKJA,KAAAA,OAAO,UAAA,WAAA,EADf,mBAMM,OANN,YAMM,CADJ,WAAsB,KAAA,QAAA,SAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA"}
1
+ {"version":3,"file":"GrDrawer-h33X403E.js","names":["$slots"],"sources":["../../../../src/components/GrDrawer/GrDrawer.vue","../../../../src/components/GrDrawer/GrDrawer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\nimport { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport {\n grDrawerPanelClass,\n grDrawerPanelEnterFrom,\n type GrDrawerSide,\n type GrDrawerSize,\n} from './grDrawerStyles'\n\nimport IconClose from '~icons/lucide/x'\n\nexport interface GrDrawerProps {\n /** Контроль открытия через v-model. */\n modelValue: boolean\n /** Заголовок; если передан — покажется в хедере. Можно переопределить слотом `#title`. */\n title?: string\n /** Закрывать при клике по бэкдропу / через HeadlessUI close. */\n closeOnBackdrop?: boolean\n /** Закрывать по Esc. */\n closeOnEsc?: boolean\n /** Сторона, с которой выезжает панель. */\n side?: GrDrawerSide\n /** Размер панели. */\n size?: GrDrawerSize\n /** i18n-friendly aria-label для кнопки закрытия. */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDrawerProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n side: 'right',\n size: 'md',\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst titleId = useId()\n\nconst panelClass = computed(() => {\n return grDrawerPanelClass({ side: props.side, size: props.size })\n})\n\nconst panelEnterFrom = computed(() => grDrawerPanelEnterFrom(props.side))\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n\nfunction onDialogClose(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onOverlayClick(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (e.key !== 'Escape') return\n if (!props.closeOnEsc) return\n e.stopPropagation()\n e.preventDefault()\n close()\n}\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"modelValue\" as=\"template\">\n <Dialog\n as=\"div\"\n data-ds-drawer\n class=\"fixed inset-0 z-50\"\n :static=\"true\"\n :aria-labelledby=\"titleId\"\n @close=\"onDialogClose\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-150 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-drawer-overlay\n class=\"fixed inset-0 bg-black/40\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n :enter-from=\"panelEnterFrom\"\n enter-to=\"translate-x-0\"\n leave=\"duration-150 ease-in\"\n leave-from=\"translate-x-0\"\n :leave-to=\"panelEnterFrom\"\n >\n <DialogPanel\n data-ds-drawer-panel\n tabindex=\"-1\"\n class=\"fixed inset-y-0 flex flex-col\"\n :class=\"panelClass\"\n >\n <div\n data-ds-drawer-header\n class=\"px-5 py-4 border-b border-[var(--brd)] flex items-center justify-between gap-4\"\n >\n <DialogTitle :id=\"titleId\" as=\"div\" data-ds-drawer-title class=\"text-[14px] font-700 min-w-0 truncate\">\n <slot name=\"title\">\n {{ title ?? 'Drawer' }}\n </slot>\n </DialogTitle>\n\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"closeLabel\"\n @click=\"close\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <div data-ds-drawer-body class=\"flex-1 overflow-y-auto\">\n <div class=\"p-5\">\n <slot />\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n data-ds-drawer-footer\n class=\"px-5 py-4 border-t border-[var(--brd)]\"\n >\n <slot name=\"footer\" />\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, useId } from 'vue'\nimport { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport {\n grDrawerPanelClass,\n grDrawerPanelEnterFrom,\n type GrDrawerSide,\n type GrDrawerSize,\n} from './grDrawerStyles'\n\nimport IconClose from '~icons/lucide/x'\n\nexport interface GrDrawerProps {\n /** Контроль открытия через v-model. */\n modelValue: boolean\n /** Заголовок; если передан — покажется в хедере. Можно переопределить слотом `#title`. */\n title?: string\n /** Закрывать при клике по бэкдропу / через HeadlessUI close. */\n closeOnBackdrop?: boolean\n /** Закрывать по Esc. */\n closeOnEsc?: boolean\n /** Сторона, с которой выезжает панель. */\n side?: GrDrawerSide\n /** Размер панели. */\n size?: GrDrawerSize\n /** i18n-friendly aria-label для кнопки закрытия. */\n closeLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrDrawerProps>(), {\n title: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n side: 'right',\n size: 'md',\n closeLabel: 'Close',\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst titleId = useId()\n\nconst panelClass = computed(() => {\n return grDrawerPanelClass({ side: props.side, size: props.size })\n})\n\nconst panelEnterFrom = computed(() => grDrawerPanelEnterFrom(props.side))\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n\nfunction onDialogClose(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onOverlayClick(): void {\n if (!props.closeOnBackdrop) return\n close()\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (e.key !== 'Escape') return\n if (!props.closeOnEsc) return\n e.stopPropagation()\n e.preventDefault()\n close()\n}\n</script>\n\n<template>\n <teleport to=\"body\">\n <TransitionRoot :show=\"modelValue\" as=\"template\">\n <Dialog\n as=\"div\"\n data-ds-drawer\n class=\"fixed inset-0 z-50\"\n :static=\"true\"\n :aria-labelledby=\"titleId\"\n @close=\"onDialogClose\"\n @keydown=\"onKeydown\"\n >\n <div class=\"fixed inset-0\">\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n enter-from=\"opacity-0\"\n enter-to=\"opacity-100\"\n leave=\"duration-150 ease-in\"\n leave-from=\"opacity-100\"\n leave-to=\"opacity-0\"\n >\n <div\n data-ds-drawer-overlay\n class=\"fixed inset-0 bg-black/40\"\n aria-hidden=\"true\"\n @click=\"onOverlayClick\"\n />\n </TransitionChild>\n\n <TransitionChild\n as=\"template\"\n enter=\"duration-200 ease-out\"\n :enter-from=\"panelEnterFrom\"\n enter-to=\"translate-x-0\"\n leave=\"duration-150 ease-in\"\n leave-from=\"translate-x-0\"\n :leave-to=\"panelEnterFrom\"\n >\n <DialogPanel\n data-ds-drawer-panel\n tabindex=\"-1\"\n class=\"fixed inset-y-0 flex flex-col\"\n :class=\"panelClass\"\n >\n <div\n data-ds-drawer-header\n class=\"px-5 py-4 border-b border-[var(--brd)] flex items-center justify-between gap-4\"\n >\n <DialogTitle :id=\"titleId\" as=\"div\" data-ds-drawer-title class=\"text-[14px] font-700 min-w-0 truncate\">\n <slot name=\"title\">\n {{ title ?? 'Drawer' }}\n </slot>\n </DialogTitle>\n\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"closeLabel\"\n @click=\"close\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <div data-ds-drawer-body class=\"flex-1 overflow-y-auto\">\n <div class=\"p-5\">\n <slot />\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n data-ds-drawer-footer\n class=\"px-5 py-4 border-t border-[var(--brd)]\"\n >\n <slot name=\"footer\" />\n </div>\n </DialogPanel>\n </TransitionChild>\n </div>\n </Dialog>\n </TransitionRoot>\n </teleport>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCA,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,MAAM,UAAU,OAAM;EAEtB,MAAM,aAAa,eAAe;AAChC,UAAO,mBAAmB;IAAE,MAAM,MAAM;IAAM,MAAM,MAAM;IAAM,CAAA;IACjE;EAED,MAAM,iBAAiB,eAAe,uBAAuB,MAAM,KAAK,CAAA;EAExE,SAAS,QAAc;AACrB,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,gBAAsB;AAC7B,OAAI,CAAC,MAAM,gBAAiB;AAC5B,UAAM;;EAGR,SAAS,iBAAuB;AAC9B,OAAI,CAAC,MAAM,gBAAiB;AAC5B,UAAM;;EAGR,SAAS,UAAU,GAAwB;AACzC,OAAI,EAAE,QAAQ,SAAU;AACxB,OAAI,CAAC,MAAM,WAAY;AACvB,KAAE,iBAAgB;AAClB,KAAE,gBAAe;AACjB,UAAM;;;uBAKN,YAqFW,UAAA,EArFD,IAAG,QAAM,EAAA,CACjB,YAmFiB,MAAA,GAAA,EAAA;IAnFA,MAAM,QAAA;IAAY,IAAG;;2BAkF3B,CAjFT,YAiFS,MAAA,GAAA,EAAA;KAhFP,IAAG;KACH,kBAAA;KACA,OAAM;KACL,QAAQ;KACR,mBAAiB,MAAA,QAAO;KACxB,SAAO;KACE;;4BAyEJ,CAvEN,mBAuEM,OAvEN,YAuEM,CAtEJ,YAekB,MAAA,GAAA,EAAA;MAdhB,IAAG;MACH,OAAM;MACN,cAAW;MACX,YAAS;MACT,OAAM;MACN,cAAW;MACX,YAAS;;6BAOP,CALF,mBAKE,OAAA;OAJA,0BAAA;OACA,OAAM;OACN,eAAY;OACX,SAAO;;;SAIZ,YAoDkB,MAAA,GAAA,EAAA;MAnDhB,IAAG;MACH,OAAM;MACL,cAAY,eAAA;MACb,YAAS;MACT,OAAM;MACN,cAAW;MACV,YAAU,eAAA;;6BA4CG,CA1Cd,YA0Cc,MAAA,GAAA,EAAA;OAzCZ,wBAAA;OACA,UAAS;OACT,OAAK,eAAA,CAAC,iCACE,WAAA,MAAU,CAAA;;8BAuBZ;QArBN,mBAqBM,OArBN,YAqBM,CAjBJ,YAIc,MAAA,GAAA,EAAA;SAJA,IAAI,MAAA,QAAO;SAAE,IAAG;SAAM,wBAAA;SAAqB,OAAM;;gCAGtD,CAFP,WAEO,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBADF,QAAA,SAAK,SAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA;;uBAIZ,YAUW,kBAAA;SATT,SAAQ;SACR,MAAK;SACL,QAAA;SACC,cAAY,QAAA;SACZ,SAAO;;gCAIC,CAFT,YAES,gBAAA;UAFD,MAAK;UAAK,eAAY;;iCACf,CAAb,YAAa,MAAA,UAAA,CAAA,CAAA,CAAA;;;;;QAKnB,mBAIM,OAJN,YAIM,CAHJ,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA,CAAA,CAAA;QAKJA,KAAAA,OAAO,UAAA,WAAA,EADf,mBAMM,OANN,YAMM,CADJ,WAAsB,KAAA,QAAA,SAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA"}
@@ -1,3 +1,3 @@
1
1
  import { n as grDrawerSafelist, t as grDrawerConfig } from "../../chunks/config-DxqpV-KM.js";
2
- import { t as GrDrawer_default } from "./chunks/GrDrawer-BJL_gTxI.js";
2
+ import { t as GrDrawer_default } from "./chunks/GrDrawer-h33X403E.js";
3
3
  export { GrDrawer_default as GrDrawer, GrDrawer_default as default, grDrawerConfig, grDrawerSafelist };
@@ -1,6 +1,6 @@
1
1
  import { t as x_default } from "../../../chunks/x-Pa6C2hwi.js";
2
- import { t as GrButton_default } from "../../GrButton/chunks/GrButton-BmGdp8-4.js";
3
- import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-BG_EqiDU.js";
2
+ import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-DQR7Sy7Q.js";
3
+ import { t as GrButton_default } from "../../GrButton/chunks/GrButton-CPDKonoB.js";
4
4
  import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-BV7RBU_0.js";
5
5
  import { l as runFileValidators, o as acceptValidator, u as FileValidationError } from "../../../chunks/fileValidation-CwJA8Vmn.js";
6
6
  import { vDropzone } from "../../../directives/dropzone.js";
@@ -1 +1 @@
1
- {"version":3,"file":"GrFormFile-deDPicM2.js","names":[],"sources":["../../../../src/components/GrFormFile/GrFormFile.vue","../../../../src/components/GrFormFile/GrFormFile.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport IconUpload from '~icons/lucide/upload'\nimport IconX from '~icons/lucide/x'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport { vDropzone } from '../../directives'\nimport { acceptValidator, FileValidationError, runFileValidators } from '../../fileValidation'\nimport type { FileValidationIssue, FileValidator } from '../../fileValidation'\n\ndefineOptions({\n name: 'GrFormFile',\n})\n\nexport type GrFormFileError = FileValidationIssue\n\n/**\n * Пропы `GrFormFile` — форма-поле для выбора файлов с встроенной валидацией,\n * drag&drop (через `v-dropzone`) и списком выбранных файлов.\n *\n * Все `*Text`/`placeholder` — i18n-friendly, принимают готовые строки локали.\n */\nexport interface GrFormFileProps {\n modelValue: File | File[] | null\n multiple?: boolean\n disabled?: boolean\n /** W3C `accept` для `<input type=\"file\">` + sugar к `acceptValidator(...)`. */\n accept?: string\n validators?: FileValidator[]\n uploadText?: string\n changeText?: string\n removeText?: string\n clearAllText?: string\n placeholder?: string\n /** Дополнительная (кастомная) валидация на стороне потребителя. */\n validate?: (files: File[]) => GrFormFileError[] | Promise<GrFormFileError[]>\n}\n\nconst props = withDefaults(\n defineProps<GrFormFileProps>(),\n {\n multiple: false,\n disabled: false,\n accept: undefined,\n uploadText: 'Upload file',\n changeText: 'Change file',\n removeText: 'Remove',\n clearAllText: 'Clear all',\n placeholder: 'No files selected',\n validators: undefined,\n validate: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: File | File[] | null): void\n (e: 'change', value: File | File[] | null): void\n (e: 'clear'): void\n (e: 'validation', errors: GrFormFileError[]): void\n (e: 'update:errors', errors: GrFormFileError[]): void\n}>()\n\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst localErrors = ref<GrFormFileError[]>([])\n\nconst files = computed<File[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n return props.modelValue instanceof File ? [props.modelValue] : []\n})\n\nconst hasFiles = computed(() => files.value.length > 0)\n\nfunction clearInputValue() {\n if (!inputRef.value) return\n inputRef.value.value = ''\n}\n\nfunction setErrors(next: GrFormFileError[]) {\n localErrors.value = next\n emit('validation', next)\n emit('update:errors', next)\n}\n\nfunction clearErrors() {\n if (localErrors.value.length === 0) return\n setErrors([])\n}\n\nfunction openDialog() {\n if (props.disabled) return\n inputRef.value?.click()\n}\n\nfunction emitModel(nextFiles: File[]) {\n const value: File | File[] | null = props.multiple\n ? nextFiles\n : (nextFiles[0] ?? null)\n\n emit('update:modelValue', value)\n emit('change', value)\n}\n\nasync function applyFiles(nextFiles: File[]) {\n const isPromiseLike = (value: unknown): value is PromiseLike<any> => {\n return !!value && typeof (value as any).then === 'function'\n }\n\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n const res = runFileValidators(nextFiles, validators, {\n source: 'input',\n multiple: props.multiple,\n })\n\n const { files: picked, issues } = isPromiseLike(res) ? await res : res\n\n if (issues.length > 0) {\n setErrors(issues)\n clearInputValue()\n await nextTick()\n return\n }\n\n clearErrors()\n emitModel(picked)\n clearInputValue()\n await nextTick()\n}\n\nasync function onInputChange(event: Event) {\n if (props.disabled) return\n\n const target = event.target as HTMLInputElement | null\n const nextFiles = target?.files ? Array.prototype.slice.call(target.files) as File[] : []\n\n // Reset value so selecting the same file twice triggers change.\n if (target) target.value = ''\n await nextTick()\n\n await applyFiles(nextFiles)\n}\n\nfunction clearAll() {\n if (props.disabled) return\n clearErrors()\n clearInputValue()\n emit('clear')\n\n emit('update:modelValue', props.multiple ? [] : null)\n emit('change', props.multiple ? [] : null)\n}\n\nfunction removeAt(index: number) {\n if (props.disabled) return\n if (!props.multiple) {\n clearAll()\n return\n }\n\n const next = files.value.slice()\n next.splice(index, 1)\n\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction issueMessage(issue: GrFormFileError): string {\n if (issue.fileName) return `${issue.fileName}: ${issue.message}`\n return issue.message\n}\n\nconst dropzone = computed(() => {\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n return {\n enabled: !props.disabled,\n multiple: props.multiple,\n validators,\n onFiles: async (dropped: File[]) => {\n // `v-dropzone` уже выполнил валидаторы и нормализацию по `multiple`.\n clearErrors()\n emitModel(dropped)\n clearInputValue()\n await nextTick()\n },\n onError: (error: unknown) => {\n if (error instanceof FileValidationError) {\n setErrors(error.issues)\n }\n else {\n setErrors([\n {\n code: 'accept',\n message: 'Failed to validate dropped files',\n },\n ])\n }\n },\n }\n})\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!inputRef.value) return\n\n if (!value || (Array.isArray(value) && value.length === 0)) {\n // Позволяет выбрать тот же файл снова после внешнего сброса.\n inputRef.value.value = ''\n }\n },\n)\n</script>\n\n<template>\n <div\n v-dropzone=\"dropzone\"\n data-ds-form-file\n class=\"rounded-[var(--ds-radius-md)]\"\n :class=\"disabled ? 'opacity-60 cursor-not-allowed' : ''\"\n >\n <input\n ref=\"inputRef\"\n data-ds-form-file-input\n type=\"file\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n class=\"absolute opacity-0 w-px h-px pointer-events-none\"\n :multiple=\"multiple\"\n :accept=\"accept\"\n :disabled=\"disabled\"\n @change=\"onInputChange\"\n >\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <GrButton\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-upload-btn\n :disabled=\"disabled\"\n @click.prevent=\"openDialog\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconUpload />\n </GrIcon>\n <span class=\"ml-2\">{{ hasFiles ? changeText : uploadText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"hasFiles && !multiple\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ removeText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"multiple && hasFiles\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-all-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ clearAllText }}</span>\n </GrButton>\n\n <span\n v-if=\"!multiple && hasFiles\"\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n data-ds-form-file-single-name\n :title=\"files[0]?.name\"\n >\n {{ files[0]?.name }}\n </span>\n\n <span\n v-if=\"!hasFiles\"\n class=\"text-sm text-[var(--muted-fg)]\"\n data-ds-form-file-placeholder\n >\n {{ placeholder }}\n </span>\n </div>\n\n <div v-if=\"multiple && hasFiles\" class=\"flex flex-col gap-2\">\n <div\n v-for=\"(file, index) in files\"\n :key=\"`${file.name}-${file.size}-${index}`\"\n class=\"flex items-center gap-2\"\n data-ds-form-file-item\n >\n <span\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n :title=\"file.name\"\n data-ds-form-file-item-name\n >\n {{ file.name }}\n </span>\n\n <button\n type=\"button\"\n class=\"text-xs text-[var(--muted-fg)] hover:text-[var(--fg)]\"\n data-ds-form-file-item-remove\n :disabled=\"disabled\"\n @click.prevent=\"removeAt(index)\"\n >\n {{ removeText }}\n </button>\n </div>\n </div>\n\n <slot name=\"errors\" :errors=\"localErrors\">\n <div v-if=\"localErrors.length\" class=\"text-sm text-[var(--ds-danger)]\" data-ds-form-file-errors>\n <div v-for=\"(e, i) in localErrors\" :key=\"i\" data-ds-form-file-error>\n {{ issueMessage(e) }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style scoped>\n[data-ds-form-file].ds-dropzone--over {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport IconUpload from '~icons/lucide/upload'\nimport IconX from '~icons/lucide/x'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport { vDropzone } from '../../directives'\nimport { acceptValidator, FileValidationError, runFileValidators } from '../../fileValidation'\nimport type { FileValidationIssue, FileValidator } from '../../fileValidation'\n\ndefineOptions({\n name: 'GrFormFile',\n})\n\nexport type GrFormFileError = FileValidationIssue\n\n/**\n * Пропы `GrFormFile` — форма-поле для выбора файлов с встроенной валидацией,\n * drag&drop (через `v-dropzone`) и списком выбранных файлов.\n *\n * Все `*Text`/`placeholder` — i18n-friendly, принимают готовые строки локали.\n */\nexport interface GrFormFileProps {\n modelValue: File | File[] | null\n multiple?: boolean\n disabled?: boolean\n /** W3C `accept` для `<input type=\"file\">` + sugar к `acceptValidator(...)`. */\n accept?: string\n validators?: FileValidator[]\n uploadText?: string\n changeText?: string\n removeText?: string\n clearAllText?: string\n placeholder?: string\n /** Дополнительная (кастомная) валидация на стороне потребителя. */\n validate?: (files: File[]) => GrFormFileError[] | Promise<GrFormFileError[]>\n}\n\nconst props = withDefaults(\n defineProps<GrFormFileProps>(),\n {\n multiple: false,\n disabled: false,\n accept: undefined,\n uploadText: 'Upload file',\n changeText: 'Change file',\n removeText: 'Remove',\n clearAllText: 'Clear all',\n placeholder: 'No files selected',\n validators: undefined,\n validate: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: File | File[] | null): void\n (e: 'change', value: File | File[] | null): void\n (e: 'clear'): void\n (e: 'validation', errors: GrFormFileError[]): void\n (e: 'update:errors', errors: GrFormFileError[]): void\n}>()\n\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst localErrors = ref<GrFormFileError[]>([])\n\nconst files = computed<File[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n return props.modelValue instanceof File ? [props.modelValue] : []\n})\n\nconst hasFiles = computed(() => files.value.length > 0)\n\nfunction clearInputValue() {\n if (!inputRef.value) return\n inputRef.value.value = ''\n}\n\nfunction setErrors(next: GrFormFileError[]) {\n localErrors.value = next\n emit('validation', next)\n emit('update:errors', next)\n}\n\nfunction clearErrors() {\n if (localErrors.value.length === 0) return\n setErrors([])\n}\n\nfunction openDialog() {\n if (props.disabled) return\n inputRef.value?.click()\n}\n\nfunction emitModel(nextFiles: File[]) {\n const value: File | File[] | null = props.multiple\n ? nextFiles\n : (nextFiles[0] ?? null)\n\n emit('update:modelValue', value)\n emit('change', value)\n}\n\nasync function applyFiles(nextFiles: File[]) {\n const isPromiseLike = (value: unknown): value is PromiseLike<any> => {\n return !!value && typeof (value as any).then === 'function'\n }\n\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n const res = runFileValidators(nextFiles, validators, {\n source: 'input',\n multiple: props.multiple,\n })\n\n const { files: picked, issues } = isPromiseLike(res) ? await res : res\n\n if (issues.length > 0) {\n setErrors(issues)\n clearInputValue()\n await nextTick()\n return\n }\n\n clearErrors()\n emitModel(picked)\n clearInputValue()\n await nextTick()\n}\n\nasync function onInputChange(event: Event) {\n if (props.disabled) return\n\n const target = event.target as HTMLInputElement | null\n const nextFiles = target?.files ? Array.prototype.slice.call(target.files) as File[] : []\n\n // Reset value so selecting the same file twice triggers change.\n if (target) target.value = ''\n await nextTick()\n\n await applyFiles(nextFiles)\n}\n\nfunction clearAll() {\n if (props.disabled) return\n clearErrors()\n clearInputValue()\n emit('clear')\n\n emit('update:modelValue', props.multiple ? [] : null)\n emit('change', props.multiple ? [] : null)\n}\n\nfunction removeAt(index: number) {\n if (props.disabled) return\n if (!props.multiple) {\n clearAll()\n return\n }\n\n const next = files.value.slice()\n next.splice(index, 1)\n\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction issueMessage(issue: GrFormFileError): string {\n if (issue.fileName) return `${issue.fileName}: ${issue.message}`\n return issue.message\n}\n\nconst dropzone = computed(() => {\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n return {\n enabled: !props.disabled,\n multiple: props.multiple,\n validators,\n onFiles: async (dropped: File[]) => {\n // `v-dropzone` уже выполнил валидаторы и нормализацию по `multiple`.\n clearErrors()\n emitModel(dropped)\n clearInputValue()\n await nextTick()\n },\n onError: (error: unknown) => {\n if (error instanceof FileValidationError) {\n setErrors(error.issues)\n }\n else {\n setErrors([\n {\n code: 'accept',\n message: 'Failed to validate dropped files',\n },\n ])\n }\n },\n }\n})\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!inputRef.value) return\n\n if (!value || (Array.isArray(value) && value.length === 0)) {\n // Позволяет выбрать тот же файл снова после внешнего сброса.\n inputRef.value.value = ''\n }\n },\n)\n</script>\n\n<template>\n <div\n v-dropzone=\"dropzone\"\n data-ds-form-file\n class=\"rounded-[var(--ds-radius-md)]\"\n :class=\"disabled ? 'opacity-60 cursor-not-allowed' : ''\"\n >\n <input\n ref=\"inputRef\"\n data-ds-form-file-input\n type=\"file\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n class=\"absolute opacity-0 w-px h-px pointer-events-none\"\n :multiple=\"multiple\"\n :accept=\"accept\"\n :disabled=\"disabled\"\n @change=\"onInputChange\"\n >\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <GrButton\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-upload-btn\n :disabled=\"disabled\"\n @click.prevent=\"openDialog\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconUpload />\n </GrIcon>\n <span class=\"ml-2\">{{ hasFiles ? changeText : uploadText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"hasFiles && !multiple\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ removeText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"multiple && hasFiles\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-all-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ clearAllText }}</span>\n </GrButton>\n\n <span\n v-if=\"!multiple && hasFiles\"\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n data-ds-form-file-single-name\n :title=\"files[0]?.name\"\n >\n {{ files[0]?.name }}\n </span>\n\n <span\n v-if=\"!hasFiles\"\n class=\"text-sm text-[var(--muted-fg)]\"\n data-ds-form-file-placeholder\n >\n {{ placeholder }}\n </span>\n </div>\n\n <div v-if=\"multiple && hasFiles\" class=\"flex flex-col gap-2\">\n <div\n v-for=\"(file, index) in files\"\n :key=\"`${file.name}-${file.size}-${index}`\"\n class=\"flex items-center gap-2\"\n data-ds-form-file-item\n >\n <span\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n :title=\"file.name\"\n data-ds-form-file-item-name\n >\n {{ file.name }}\n </span>\n\n <button\n type=\"button\"\n class=\"text-xs text-[var(--muted-fg)] hover:text-[var(--fg)]\"\n data-ds-form-file-item-remove\n :disabled=\"disabled\"\n @click.prevent=\"removeAt(index)\"\n >\n {{ removeText }}\n </button>\n </div>\n </div>\n\n <slot name=\"errors\" :errors=\"localErrors\">\n <div v-if=\"localErrors.length\" class=\"text-sm text-[var(--ds-danger)]\" data-ds-form-file-errors>\n <div v-for=\"(e, i) in localErrors\" :key=\"i\" data-ds-form-file-error>\n {{ issueMessage(e) }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style scoped>\n[data-ds-form-file].ds-dropzone--over {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"GrFormFile-WqF-dPTB.js","names":[],"sources":["../../../../src/components/GrFormFile/GrFormFile.vue","../../../../src/components/GrFormFile/GrFormFile.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport IconUpload from '~icons/lucide/upload'\nimport IconX from '~icons/lucide/x'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport { vDropzone } from '../../directives'\nimport { acceptValidator, FileValidationError, runFileValidators } from '../../fileValidation'\nimport type { FileValidationIssue, FileValidator } from '../../fileValidation'\n\ndefineOptions({\n name: 'GrFormFile',\n})\n\nexport type GrFormFileError = FileValidationIssue\n\n/**\n * Пропы `GrFormFile` — форма-поле для выбора файлов с встроенной валидацией,\n * drag&drop (через `v-dropzone`) и списком выбранных файлов.\n *\n * Все `*Text`/`placeholder` — i18n-friendly, принимают готовые строки локали.\n */\nexport interface GrFormFileProps {\n modelValue: File | File[] | null\n multiple?: boolean\n disabled?: boolean\n /** W3C `accept` для `<input type=\"file\">` + sugar к `acceptValidator(...)`. */\n accept?: string\n validators?: FileValidator[]\n uploadText?: string\n changeText?: string\n removeText?: string\n clearAllText?: string\n placeholder?: string\n /** Дополнительная (кастомная) валидация на стороне потребителя. */\n validate?: (files: File[]) => GrFormFileError[] | Promise<GrFormFileError[]>\n}\n\nconst props = withDefaults(\n defineProps<GrFormFileProps>(),\n {\n multiple: false,\n disabled: false,\n accept: undefined,\n uploadText: 'Upload file',\n changeText: 'Change file',\n removeText: 'Remove',\n clearAllText: 'Clear all',\n placeholder: 'No files selected',\n validators: undefined,\n validate: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: File | File[] | null): void\n (e: 'change', value: File | File[] | null): void\n (e: 'clear'): void\n (e: 'validation', errors: GrFormFileError[]): void\n (e: 'update:errors', errors: GrFormFileError[]): void\n}>()\n\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst localErrors = ref<GrFormFileError[]>([])\n\nconst files = computed<File[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n return props.modelValue instanceof File ? [props.modelValue] : []\n})\n\nconst hasFiles = computed(() => files.value.length > 0)\n\nfunction clearInputValue() {\n if (!inputRef.value) return\n inputRef.value.value = ''\n}\n\nfunction setErrors(next: GrFormFileError[]) {\n localErrors.value = next\n emit('validation', next)\n emit('update:errors', next)\n}\n\nfunction clearErrors() {\n if (localErrors.value.length === 0) return\n setErrors([])\n}\n\nfunction openDialog() {\n if (props.disabled) return\n inputRef.value?.click()\n}\n\nfunction emitModel(nextFiles: File[]) {\n const value: File | File[] | null = props.multiple\n ? nextFiles\n : (nextFiles[0] ?? null)\n\n emit('update:modelValue', value)\n emit('change', value)\n}\n\nasync function applyFiles(nextFiles: File[]) {\n const isPromiseLike = (value: unknown): value is PromiseLike<any> => {\n return !!value && typeof (value as any).then === 'function'\n }\n\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n const res = runFileValidators(nextFiles, validators, {\n source: 'input',\n multiple: props.multiple,\n })\n\n const { files: picked, issues } = isPromiseLike(res) ? await res : res\n\n if (issues.length > 0) {\n setErrors(issues)\n clearInputValue()\n await nextTick()\n return\n }\n\n clearErrors()\n emitModel(picked)\n clearInputValue()\n await nextTick()\n}\n\nasync function onInputChange(event: Event) {\n if (props.disabled) return\n\n const target = event.target as HTMLInputElement | null\n const nextFiles = target?.files ? Array.prototype.slice.call(target.files) as File[] : []\n\n // Reset value so selecting the same file twice triggers change.\n if (target) target.value = ''\n await nextTick()\n\n await applyFiles(nextFiles)\n}\n\nfunction clearAll() {\n if (props.disabled) return\n clearErrors()\n clearInputValue()\n emit('clear')\n\n emit('update:modelValue', props.multiple ? [] : null)\n emit('change', props.multiple ? [] : null)\n}\n\nfunction removeAt(index: number) {\n if (props.disabled) return\n if (!props.multiple) {\n clearAll()\n return\n }\n\n const next = files.value.slice()\n next.splice(index, 1)\n\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction issueMessage(issue: GrFormFileError): string {\n if (issue.fileName) return `${issue.fileName}: ${issue.message}`\n return issue.message\n}\n\nconst dropzone = computed(() => {\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n return {\n enabled: !props.disabled,\n multiple: props.multiple,\n validators,\n onFiles: async (dropped: File[]) => {\n // `v-dropzone` уже выполнил валидаторы и нормализацию по `multiple`.\n clearErrors()\n emitModel(dropped)\n clearInputValue()\n await nextTick()\n },\n onError: (error: unknown) => {\n if (error instanceof FileValidationError) {\n setErrors(error.issues)\n }\n else {\n setErrors([\n {\n code: 'accept',\n message: 'Failed to validate dropped files',\n },\n ])\n }\n },\n }\n})\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!inputRef.value) return\n\n if (!value || (Array.isArray(value) && value.length === 0)) {\n // Позволяет выбрать тот же файл снова после внешнего сброса.\n inputRef.value.value = ''\n }\n },\n)\n</script>\n\n<template>\n <div\n v-dropzone=\"dropzone\"\n data-ds-form-file\n class=\"rounded-[var(--ds-radius-md)]\"\n :class=\"disabled ? 'opacity-60 cursor-not-allowed' : ''\"\n >\n <input\n ref=\"inputRef\"\n data-ds-form-file-input\n type=\"file\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n class=\"absolute opacity-0 w-px h-px pointer-events-none\"\n :multiple=\"multiple\"\n :accept=\"accept\"\n :disabled=\"disabled\"\n @change=\"onInputChange\"\n >\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <GrButton\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-upload-btn\n :disabled=\"disabled\"\n @click.prevent=\"openDialog\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconUpload />\n </GrIcon>\n <span class=\"ml-2\">{{ hasFiles ? changeText : uploadText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"hasFiles && !multiple\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ removeText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"multiple && hasFiles\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-all-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ clearAllText }}</span>\n </GrButton>\n\n <span\n v-if=\"!multiple && hasFiles\"\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n data-ds-form-file-single-name\n :title=\"files[0]?.name\"\n >\n {{ files[0]?.name }}\n </span>\n\n <span\n v-if=\"!hasFiles\"\n class=\"text-sm text-[var(--muted-fg)]\"\n data-ds-form-file-placeholder\n >\n {{ placeholder }}\n </span>\n </div>\n\n <div v-if=\"multiple && hasFiles\" class=\"flex flex-col gap-2\">\n <div\n v-for=\"(file, index) in files\"\n :key=\"`${file.name}-${file.size}-${index}`\"\n class=\"flex items-center gap-2\"\n data-ds-form-file-item\n >\n <span\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n :title=\"file.name\"\n data-ds-form-file-item-name\n >\n {{ file.name }}\n </span>\n\n <button\n type=\"button\"\n class=\"text-xs text-[var(--muted-fg)] hover:text-[var(--fg)]\"\n data-ds-form-file-item-remove\n :disabled=\"disabled\"\n @click.prevent=\"removeAt(index)\"\n >\n {{ removeText }}\n </button>\n </div>\n </div>\n\n <slot name=\"errors\" :errors=\"localErrors\">\n <div v-if=\"localErrors.length\" class=\"text-sm text-[var(--ds-danger)]\" data-ds-form-file-errors>\n <div v-for=\"(e, i) in localErrors\" :key=\"i\" data-ds-form-file-error>\n {{ issueMessage(e) }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style scoped>\n[data-ds-form-file].ds-dropzone--over {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport IconUpload from '~icons/lucide/upload'\nimport IconX from '~icons/lucide/x'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport { vDropzone } from '../../directives'\nimport { acceptValidator, FileValidationError, runFileValidators } from '../../fileValidation'\nimport type { FileValidationIssue, FileValidator } from '../../fileValidation'\n\ndefineOptions({\n name: 'GrFormFile',\n})\n\nexport type GrFormFileError = FileValidationIssue\n\n/**\n * Пропы `GrFormFile` — форма-поле для выбора файлов с встроенной валидацией,\n * drag&drop (через `v-dropzone`) и списком выбранных файлов.\n *\n * Все `*Text`/`placeholder` — i18n-friendly, принимают готовые строки локали.\n */\nexport interface GrFormFileProps {\n modelValue: File | File[] | null\n multiple?: boolean\n disabled?: boolean\n /** W3C `accept` для `<input type=\"file\">` + sugar к `acceptValidator(...)`. */\n accept?: string\n validators?: FileValidator[]\n uploadText?: string\n changeText?: string\n removeText?: string\n clearAllText?: string\n placeholder?: string\n /** Дополнительная (кастомная) валидация на стороне потребителя. */\n validate?: (files: File[]) => GrFormFileError[] | Promise<GrFormFileError[]>\n}\n\nconst props = withDefaults(\n defineProps<GrFormFileProps>(),\n {\n multiple: false,\n disabled: false,\n accept: undefined,\n uploadText: 'Upload file',\n changeText: 'Change file',\n removeText: 'Remove',\n clearAllText: 'Clear all',\n placeholder: 'No files selected',\n validators: undefined,\n validate: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: File | File[] | null): void\n (e: 'change', value: File | File[] | null): void\n (e: 'clear'): void\n (e: 'validation', errors: GrFormFileError[]): void\n (e: 'update:errors', errors: GrFormFileError[]): void\n}>()\n\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst localErrors = ref<GrFormFileError[]>([])\n\nconst files = computed<File[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n return props.modelValue instanceof File ? [props.modelValue] : []\n})\n\nconst hasFiles = computed(() => files.value.length > 0)\n\nfunction clearInputValue() {\n if (!inputRef.value) return\n inputRef.value.value = ''\n}\n\nfunction setErrors(next: GrFormFileError[]) {\n localErrors.value = next\n emit('validation', next)\n emit('update:errors', next)\n}\n\nfunction clearErrors() {\n if (localErrors.value.length === 0) return\n setErrors([])\n}\n\nfunction openDialog() {\n if (props.disabled) return\n inputRef.value?.click()\n}\n\nfunction emitModel(nextFiles: File[]) {\n const value: File | File[] | null = props.multiple\n ? nextFiles\n : (nextFiles[0] ?? null)\n\n emit('update:modelValue', value)\n emit('change', value)\n}\n\nasync function applyFiles(nextFiles: File[]) {\n const isPromiseLike = (value: unknown): value is PromiseLike<any> => {\n return !!value && typeof (value as any).then === 'function'\n }\n\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n const res = runFileValidators(nextFiles, validators, {\n source: 'input',\n multiple: props.multiple,\n })\n\n const { files: picked, issues } = isPromiseLike(res) ? await res : res\n\n if (issues.length > 0) {\n setErrors(issues)\n clearInputValue()\n await nextTick()\n return\n }\n\n clearErrors()\n emitModel(picked)\n clearInputValue()\n await nextTick()\n}\n\nasync function onInputChange(event: Event) {\n if (props.disabled) return\n\n const target = event.target as HTMLInputElement | null\n const nextFiles = target?.files ? Array.prototype.slice.call(target.files) as File[] : []\n\n // Reset value so selecting the same file twice triggers change.\n if (target) target.value = ''\n await nextTick()\n\n await applyFiles(nextFiles)\n}\n\nfunction clearAll() {\n if (props.disabled) return\n clearErrors()\n clearInputValue()\n emit('clear')\n\n emit('update:modelValue', props.multiple ? [] : null)\n emit('change', props.multiple ? [] : null)\n}\n\nfunction removeAt(index: number) {\n if (props.disabled) return\n if (!props.multiple) {\n clearAll()\n return\n }\n\n const next = files.value.slice()\n next.splice(index, 1)\n\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction issueMessage(issue: GrFormFileError): string {\n if (issue.fileName) return `${issue.fileName}: ${issue.message}`\n return issue.message\n}\n\nconst dropzone = computed(() => {\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n return {\n enabled: !props.disabled,\n multiple: props.multiple,\n validators,\n onFiles: async (dropped: File[]) => {\n // `v-dropzone` уже выполнил валидаторы и нормализацию по `multiple`.\n clearErrors()\n emitModel(dropped)\n clearInputValue()\n await nextTick()\n },\n onError: (error: unknown) => {\n if (error instanceof FileValidationError) {\n setErrors(error.issues)\n }\n else {\n setErrors([\n {\n code: 'accept',\n message: 'Failed to validate dropped files',\n },\n ])\n }\n },\n }\n})\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!inputRef.value) return\n\n if (!value || (Array.isArray(value) && value.length === 0)) {\n // Позволяет выбрать тот же файл снова после внешнего сброса.\n inputRef.value.value = ''\n }\n },\n)\n</script>\n\n<template>\n <div\n v-dropzone=\"dropzone\"\n data-ds-form-file\n class=\"rounded-[var(--ds-radius-md)]\"\n :class=\"disabled ? 'opacity-60 cursor-not-allowed' : ''\"\n >\n <input\n ref=\"inputRef\"\n data-ds-form-file-input\n type=\"file\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n class=\"absolute opacity-0 w-px h-px pointer-events-none\"\n :multiple=\"multiple\"\n :accept=\"accept\"\n :disabled=\"disabled\"\n @change=\"onInputChange\"\n >\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <GrButton\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-upload-btn\n :disabled=\"disabled\"\n @click.prevent=\"openDialog\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconUpload />\n </GrIcon>\n <span class=\"ml-2\">{{ hasFiles ? changeText : uploadText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"hasFiles && !multiple\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ removeText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"multiple && hasFiles\"\n variant=\"secondary\"\n size=\"sm\"\n data-ds-form-file-clear-all-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ clearAllText }}</span>\n </GrButton>\n\n <span\n v-if=\"!multiple && hasFiles\"\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n data-ds-form-file-single-name\n :title=\"files[0]?.name\"\n >\n {{ files[0]?.name }}\n </span>\n\n <span\n v-if=\"!hasFiles\"\n class=\"text-sm text-[var(--muted-fg)]\"\n data-ds-form-file-placeholder\n >\n {{ placeholder }}\n </span>\n </div>\n\n <div v-if=\"multiple && hasFiles\" class=\"flex flex-col gap-2\">\n <div\n v-for=\"(file, index) in files\"\n :key=\"`${file.name}-${file.size}-${index}`\"\n class=\"flex items-center gap-2\"\n data-ds-form-file-item\n >\n <span\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n :title=\"file.name\"\n data-ds-form-file-item-name\n >\n {{ file.name }}\n </span>\n\n <button\n type=\"button\"\n class=\"text-xs text-[var(--muted-fg)] hover:text-[var(--fg)]\"\n data-ds-form-file-item-remove\n :disabled=\"disabled\"\n @click.prevent=\"removeAt(index)\"\n >\n {{ removeText }}\n </button>\n </div>\n </div>\n\n <slot name=\"errors\" :errors=\"localErrors\">\n <div v-if=\"localErrors.length\" class=\"text-sm text-[var(--ds-danger)]\" data-ds-form-file-errors>\n <div v-for=\"(e, i) in localErrors\" :key=\"i\" data-ds-form-file-error>\n {{ issueMessage(e) }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style scoped>\n[data-ds-form-file].ds-dropzone--over {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n</style>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { t as GrFormFile_default } from "./chunks/GrFormFile-deDPicM2.js";
1
+ import { t as GrFormFile_default } from "./chunks/GrFormFile-WqF-dPTB.js";
2
2
  import { t as grFormFileConfig } from "../../chunks/config-DbqFk-IW.js";
3
3
  export { GrFormFile_default as GrFormFile, GrFormFile_default as default, grFormFileConfig };
@@ -1,5 +1,5 @@
1
1
  import { t as x_default } from "../../../chunks/x-Pa6C2hwi.js";
2
- import { t as GrBadge_default } from "../../GrBadge/chunks/GrBadge-wuIb4_2K.js";
2
+ import { t as GrBadge_default } from "../../GrBadge/chunks/GrBadge-Cs_cVfuO.js";
3
3
  import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-BV7RBU_0.js";
4
4
  import { i as grInputTagWrapperClass, r as grInputTagInputClass } from "../../../chunks/config-Dusr9jcE.js";
5
5
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, renderList, renderSlot, toDisplayString, unref, withCtx, withModifiers } from "vue";