@feugene/granularity 0.29.1 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/dist/chunks/{config-DO8mjeIo.js → config-CIkdsWUk.js} +6 -2
- package/dist/chunks/config-CIkdsWUk.js.map +1 -0
- package/dist/chunks/{config-Dpk-eVkS.js → config-DUbbGcfw.js} +12 -1
- package/dist/chunks/config-DUbbGcfw.js.map +1 -0
- package/dist/chunks/{grInputStyles-CAk3xKIb.js → grInputStyles-DgQYjJvD.js} +17 -1
- package/dist/chunks/{grInputStyles-CAk3xKIb.js.map → grInputStyles-DgQYjJvD.js.map} +1 -1
- package/dist/chunks/grSelectStyles-Cdtd10eY.js.map +1 -1
- package/dist/chunks/{granular-provider-NtErwzYB.js → granular-provider-yJNX1g1G.js} +2 -2
- package/dist/chunks/{granular-provider-NtErwzYB.js.map → granular-provider-yJNX1g1G.js.map} +1 -1
- package/dist/chunks/renamedProp-BwcN-SJg.js +33 -0
- package/dist/chunks/renamedProp-BwcN-SJg.js.map +1 -0
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-CaHys_A_.js → GrAutocomplete-X6RlmiJQ.js} +1 -1
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-CaHys_A_.js.map → GrAutocomplete-X6RlmiJQ.js.map} +1 -1
- package/dist/components/GrAutocomplete/index.js +1 -1
- package/dist/components/GrBadge/chunks/{GrBadge-BM3f9PME.js → GrBadge-BsoocpF-.js} +4 -2
- package/dist/components/GrBadge/chunks/GrBadge-BsoocpF-.js.map +1 -0
- package/dist/components/GrBadge/index.js +1 -1
- package/dist/components/GrBadge/styles.css +1 -1
- package/dist/components/GrCard/chunks/{GrCard-DR3ehBt8.js → GrCard-BttXz1pQ.js} +19 -6
- package/dist/components/GrCard/chunks/GrCard-BttXz1pQ.js.map +1 -0
- package/dist/components/GrCard/index.js +2 -2
- package/dist/components/GrChip/chunks/{GrChip-Df4orxRH.js → GrChip-C0vU69tf.js} +3 -1
- package/dist/components/GrChip/chunks/GrChip-C0vU69tf.js.map +1 -0
- package/dist/components/GrChip/index.js +1 -1
- package/dist/components/GrCollapse/chunks/{GrCollapse-Bw9_JatF.js → GrCollapse-BlW6ybdL.js} +1 -1
- package/dist/components/GrCollapse/chunks/{GrCollapse-Bw9_JatF.js.map → GrCollapse-BlW6ybdL.js.map} +1 -1
- package/dist/components/GrCollapse/index.js +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-CE-xtNP-.js → GrColorPicker-C7ifyQ0R.js} +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-CE-xtNP-.js.map → GrColorPicker-C7ifyQ0R.js.map} +1 -1
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrContextMenu/chunks/{GrContextMenu-BNQDbAsY.js → GrContextMenu-_FKwQvuQ.js} +1 -1
- package/dist/components/GrContextMenu/chunks/{GrContextMenu-BNQDbAsY.js.map → GrContextMenu-_FKwQvuQ.js.map} +1 -1
- package/dist/components/GrContextMenu/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-k4SYIv64.js → GrDialogService-6s2cQ-_6.js} +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-k4SYIv64.js.map → GrDialogService-6s2cQ-_6.js.map} +1 -1
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-Cg7qHV9J.js → GrDropdownMenu-GOzuAXsi.js} +1 -1
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-Cg7qHV9J.js.map → GrDropdownMenu-GOzuAXsi.js.map} +1 -1
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenuList-RW5IBvmu.js → GrDropdownMenuList-B33ZzwfB.js} +13 -3
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenuList-RW5IBvmu.js.map → GrDropdownMenuList-B33ZzwfB.js.map} +1 -1
- package/dist/components/GrDropdownMenu/index.js +2 -2
- package/dist/components/GrInput/chunks/{GrInput-BqqoEl1b.js → GrInput-BLywRZUN.js} +10 -6
- package/dist/components/GrInput/chunks/GrInput-BLywRZUN.js.map +1 -0
- package/dist/components/GrInput/index.js +2 -2
- package/dist/components/GrInputTag/chunks/{GrInputTag-5wVcB0zG.js → GrInputTag-Cj4A9Xgs.js} +1 -1
- package/dist/components/GrInputTag/chunks/{GrInputTag-5wVcB0zG.js.map → GrInputTag-Cj4A9Xgs.js.map} +1 -1
- package/dist/components/GrInputTag/index.js +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-CgwjoUJt.js → GrJsonViewer-Cy2bknHO.js} +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-CgwjoUJt.js.map → GrJsonViewer-Cy2bknHO.js.map} +1 -1
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrList/chunks/{GrList-DrXrKU37.js → GrList-DmT4qzLO.js} +1 -1
- package/dist/components/GrList/chunks/{GrList-DrXrKU37.js.map → GrList-DmT4qzLO.js.map} +1 -1
- package/dist/components/GrList/index.js +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-CX9ZV8mR.js → GrPagination-Cw3R21dp.js} +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-CX9ZV8mR.js.map → GrPagination-Cw3R21dp.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-D1ADRFxx.js → GrPromptDialog-DLcl1xiq.js} +1 -1
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-D1ADRFxx.js.map → GrPromptDialog-DLcl1xiq.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-DKoQu3w9.js → GrSelect-BiUrSSxc.js} +5 -5
- package/dist/components/GrSelect/chunks/GrSelect-BiUrSSxc.js.map +1 -0
- package/dist/components/GrSelect/index.js +1 -1
- package/dist/components/GrSelect/styles.css +1 -1
- package/dist/components/GrSortableList/chunks/{GrSortableList-DGs3Pi2J.js → GrSortableList-HyKX--rO.js} +1 -1
- package/dist/components/GrSortableList/chunks/{GrSortableList-DGs3Pi2J.js.map → GrSortableList-HyKX--rO.js.map} +1 -1
- package/dist/components/GrSortableList/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-CymO68Cv.js → GrTreeSelect-iJnMl2t9.js} +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-CymO68Cv.js.map → GrTreeSelect-iJnMl2t9.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/index.js +21 -21
- package/dist/types/components/GrCard/GrCard.vue.d.ts +8 -0
- package/dist/types/components/GrCard/grCardStyles.d.ts +9 -0
- package/dist/types/components/GrDropdownMenu/menuModel.d.ts +20 -2
- package/dist/types/components/GrInput/GrInput.vue.d.ts +16 -0
- package/dist/types/components/GrInput/grInputStyles.d.ts +16 -0
- package/dist/types/components/GrSelect/grSelectStyles.d.ts +12 -0
- package/dist/types/components/shared/renamedProp.d.ts +19 -0
- package/dist/web-types.json +14 -1
- package/package.json +2 -2
- package/dist/chunks/config-DO8mjeIo.js.map +0 -1
- package/dist/chunks/config-Dpk-eVkS.js.map +0 -1
- package/dist/components/GrBadge/chunks/GrBadge-BM3f9PME.js.map +0 -1
- package/dist/components/GrCard/chunks/GrCard-DR3ehBt8.js.map +0 -1
- package/dist/components/GrChip/chunks/GrChip-Df4orxRH.js.map +0 -1
- package/dist/components/GrInput/chunks/GrInput-BqqoEl1b.js.map +0 -1
- package/dist/components/GrSelect/chunks/GrSelect-DKoQu3w9.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrDialogService-k4SYIv64.js","names":[],"sources":["../../../../src/components/GrDialogService/store.ts","../../../../src/components/GrDialogService/useDialogService.ts","../../../../src/components/GrDialogService/GrDialogServiceItem.vue","../../../../src/components/GrDialogService/GrDialogServiceItem.vue","../../../../src/components/GrDialogService/GrDialogServiceHost.vue","../../../../src/components/GrDialogService/GrDialogServiceHost.vue"],"sourcesContent":["import { reactive } from 'vue'\nimport type { AppContext, Raw } from 'vue'\n\nimport type { GrConfigContext } from '../GrConfigProvider/context'\nimport type { GranularityI18nLike } from '../../internal/granularityI18n'\n\nimport type {\n DialogBaseOptions,\n DialogKind,\n DialogOnConfirm,\n DialogResult,\n} from './types'\n\n/**\n * Внутреннее описание одного запроса диалога в очереди. Создаётся методами\n * сервиса (`confirm`/`alert`/`prompt`/`open`) и потребляется хостом\n * (`GrDialogServiceHost`), который рендерит видимую часть очереди.\n */\nexport interface DialogRequest {\n id: string\n kind: DialogKind\n message: string\n options: DialogBaseOptions\n onConfirm?: DialogOnConfirm<any>\n /** Позиция среди ожидающих: больше — раньше. При равенстве порядок FIFO. */\n priority: number\n /**\n * Заявка создана из `onConfirm` уже открытого диалога и показывается **поверх**\n * него, а не за ним. Без этого вложенный вызов вставал бы в очередь за тем,\n * кто его ждёт: внешнее окно висело бы в загрузке, а промис не резолвился бы\n * никогда.\n */\n nested: boolean\n /**\n * Заявка уже завершена. Флаг живёт на самой заявке, а не на хосте: завершить\n * её могут трое (кнопка в окне, `close()` промиса, `closeAll()`), и общее\n * состояние на хосте означало бы, что второй и третий путь про первый не\n * знают.\n */\n settled: boolean\n /** Резолвит промис вызова детальным результатом `{ action, value }`. */\n resolve: (result: DialogResult<any>) => void\n /**\n * Конфиг и i18n, захваченные в месте вызова сервиса (в `setup`, где вызывающий\n * компонент ещё находится внутри дерева провайдера). Хост монтируется в\n * `document.body` вне дерева, поэтому сам их не увидит — см.\n * `SPEC-GrConfig-resolver.md`.\n *\n * Лежат в запросе, а не в состоянии сервиса: два поддерева с разными\n * провайдерами должны получать каждое свой конфиг.\n *\n * `Raw<…>` обязателен: очередь — `reactive`, а он разворачивает вложенные рефы.\n * Без `markRaw` контекст превратился бы в объект значений, `size.value` стало\n * бы `undefined`, и мост молча перестал бы работать.\n */\n config?: Raw<GrConfigContext> | null\n i18n?: Raw<GranularityI18nLike> | null\n}\n\n/** Контекст, захваченный вызовом `useDialogService()` внутри `setup`. */\nexport interface CapturedDialogContext {\n config: Raw<GrConfigContext> | null\n i18n: Raw<GranularityI18nLike> | null\n}\n\n/**\n * Состояние одного инстанса сервиса: очередь, смонтированный хост и захваченный\n * контекст приложения. Живёт либо в приложении (через\n * `granularityDialogServicePlugin`), либо в ленивом модульном фолбэке для\n * простых SPA — см. `useDialogService.ts`.\n */\nexport interface DialogServiceState {\n /** Очередь заявок. Видимую часть выбирает хост, остальные ждут. */\n queue: DialogRequest[]\n mounted: boolean\n container: HTMLElement | null\n appContext: AppContext | null\n lastCapturedContext: CapturedDialogContext | null\n contextlessWarned: boolean\n /**\n * Заявки, чей `onConfirm` сейчас в полёте, от внешней к самой глубокой.\n * По ним определяется вложенность нового вызова.\n */\n inFlight: DialogRequest[]\n}\n\nexport function createDialogServiceState(): DialogServiceState {\n return {\n queue: reactive<DialogRequest[]>([]),\n mounted: false,\n container: null,\n appContext: null,\n lastCapturedContext: null,\n contextlessWarned: false,\n inFlight: [],\n }\n}\n\nlet counter = 0\n\nexport function makeDialogId(): string {\n counter += 1\n return `gr-dialog-${Date.now()}-${counter}`\n}\n\n/**\n * Видимая часть очереди: голова и непрерывный хвост вложенных заявок за ней.\n * Всё остальное ждёт — обычные вызовы по-прежнему идут по одному, чтобы три\n * алерта из цикла не легли стопкой.\n */\nexport function visibleDialogRequests(state: DialogServiceState): DialogRequest[] {\n const visible: DialogRequest[] = []\n\n for (const request of state.queue) {\n if (visible.length === 0 || request.nested)\n visible.push(request)\n else\n break\n }\n\n return visible\n}\n\n/**\n * Ставит заявку в очередь. Единственное место, где решается её позиция:\n *\n * - вложенная (создана, пока `onConfirm` другой заявки в полёте) встаёт сразу\n * за своим родителем и показывается поверх него;\n * - обычная — перед всеми ожидающими с меньшим приоритетом; показанные окна не\n * трогаются, выдёргивать фокус-ловушку из-под пользователя нельзя.\n */\nexport function enqueueDialogRequest(state: DialogServiceState, request: DialogRequest): void {\n const parent = state.inFlight[state.inFlight.length - 1]\n\n if (parent) {\n request.nested = true\n const parentIndex = state.queue.findIndex(item => item.id === parent.id)\n state.queue.splice(parentIndex + 1, 0, request)\n return\n }\n\n const visibleCount = visibleDialogRequests(state).length\n const insertAt = state.queue.findIndex(\n (item, index) => index >= visibleCount && item.priority < request.priority,\n )\n\n if (insertAt < 0)\n state.queue.push(request)\n else\n state.queue.splice(insertAt, 0, request)\n}\n\n/**\n * Заявка входит в «полёт»: пока её `onConfirm` не вернулся, новые вызовы\n * считаются вложенными в неё.\n */\nexport function startDialogInFlight(state: DialogServiceState, request: DialogRequest): void {\n state.inFlight.push(request)\n}\n\n/** Заявка вышла из полёта (колбэк вернулся или окно размонтировано). */\nexport function finishDialogInFlight(state: DialogServiceState, request: DialogRequest): void {\n const index = state.inFlight.indexOf(request)\n if (index >= 0)\n state.inFlight.splice(index, 1)\n}\n\n/**\n * Единственный способ завершить заявку: идемпотентно резолвит промис и снимает\n * её из очереди.\n *\n * `removeFromQueue: false` нужен окну: оно гасит себя и даёт анимации закрытия\n * доиграть, прежде чем заявка исчезнет из очереди. Корректность от этой\n * отсрочки не зависит: повторное завершение отсекается флагом на заявке.\n */\nexport function settleDialogRequest(\n state: DialogServiceState,\n request: DialogRequest,\n result: DialogResult<any>,\n { removeFromQueue = true }: { removeFromQueue?: boolean } = {},\n): boolean {\n if (request.settled)\n return false\n\n request.settled = true\n request.resolve(result)\n\n if (removeFromQueue)\n removeDialogRequest(state, request)\n\n return true\n}\n\n/** Снимает заявку из очереди, если она ещё там. */\nexport function removeDialogRequest(state: DialogServiceState, request: DialogRequest): void {\n const index = state.queue.findIndex(item => item.id === request.id)\n if (index >= 0)\n state.queue.splice(index, 1)\n}\n","import { createVNode, getCurrentInstance, inject, markRaw, render } from 'vue'\nimport type { App, AppContext, InjectionKey } from 'vue'\n\nimport { ensurePortalRoot } from '../../composables/internal/portalRoot'\nimport GrDialogServiceHost from './GrDialogServiceHost.vue'\nimport { useGrConfig } from '../GrConfigProvider/context'\nimport { resolveGranularityI18n } from '../../internal/granularityI18n'\nimport {\n createDialogServiceState,\n enqueueDialogRequest,\n makeDialogId,\n settleDialogRequest,\n} from './store'\nimport type { CapturedDialogContext, DialogRequest, DialogServiceState } from './store'\nimport type {\n DialogAlertOptions,\n DialogBaseOptions,\n DialogConfirmOptions,\n DialogKind,\n DialogOnConfirm,\n DialogPromise,\n DialogPromptOptions,\n DialogResult,\n DialogService,\n DialogServiceDefaults,\n} from './types'\n\n/**\n * `useDialogService` — императивный сервис диалогов (в духе Element Plus\n * `ElMessageBox`). Позволяет вызывать `confirm`/`alert`/`prompt` из `<script>`\n * или `.ts` без вставки компонента в шаблон.\n *\n * Поверх существующих `GrConfirmDialog`/`GrPromptDialog`: лениво монтирует\n * единый хост (`GrDialogServiceHost`) в `document.body`, наследуя контекст\n * приложения (i18n / тема / `granular-provider`).\n */\n\n/** Ключ provide/inject для app-scoped состояния сервиса (устанавливает плагин ниже). */\nexport const GRANULARITY_DIALOG_SERVICE_STATE: InjectionKey<DialogServiceState>\n = Symbol.for('@feugene/granularity/dialog-service-state')\n\n/**\n * Инстансы, заведённые плагином. Реестр нужен готовому синглтону\n * `dialogService`: он создаётся на импорте модуля, где `inject` не работает\n * вовсе, и сам до app-scoped состояния не дотянется. Ровно один\n * зарегистрированный — ответ однозначен; несколько — выбирать за пользователя\n * нечего, и он получает предупреждение с модульным фолбэком.\n */\nconst registeredStates = new Set<DialogServiceState>()\n\n/**\n * Vue-плагин: даёт каждому приложению собственное состояние сервиса и снимает\n * его хост вместе с приложением. Обязателен для нескольких Vue-приложений на\n * одной странице (микрофронтенды — иначе они делят одну очередь и один хост) и\n * для HMR, где контейнер прошлого приложения иначе остаётся в `document.body`.\n *\n * ```ts\n * app.use(granularityDialogServicePlugin)\n * ```\n */\nexport const granularityDialogServicePlugin = {\n install(app: App) {\n const state = createDialogServiceState()\n\n app.provide(GRANULARITY_DIALOG_SERVICE_STATE, state)\n registeredStates.add(state)\n\n app.onUnmount(() => {\n teardownDialogServiceState(state)\n registeredStates.delete(state)\n })\n },\n}\n\n// Ленивый модульный фолбэк — канонический вариант для простого SPA без плагина.\nlet moduleState: DialogServiceState | null = null\n\nfunction getModuleState(): DialogServiceState {\n if (!moduleState)\n moduleState = createDialogServiceState()\n\n return moduleState\n}\n\nlet ambiguousStateWarned = false\n\nfunction warnAmbiguousState(): void {\n if (ambiguousStateWarned) return\n if (!__GR_DEV__) return\n\n ambiguousStateWarned = true\n console.warn(\n '[granularity] useDialogService: плагин установлен в нескольких приложениях, '\n + 'а сервис запрошен вне `setup` — какое из них имелось в виду, знать неоткуда. '\n + 'Используется модульное состояние. Вызовите `useDialogService()` внутри `setup` нужного приложения.',\n )\n}\n\n/**\n * Состояние текущего вызова: app-scoped (плагин) → единственный\n * зарегистрированный инстанс → модульный фолбэк.\n */\nfunction resolveDialogServiceState(): DialogServiceState {\n // `inject` работает только в `setup` — там ответ точный.\n if (getCurrentInstance()) {\n const provided = inject(GRANULARITY_DIALOG_SERVICE_STATE, null)\n if (provided) return provided\n }\n\n if (registeredStates.size === 1)\n return registeredStates.values().next().value!\n\n if (registeredStates.size > 1)\n warnAmbiguousState()\n\n return getModuleState()\n}\n\nfunction ensureMounted(state: DialogServiceState, appContext?: AppContext | null): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return\n\n if (appContext) state.appContext = appContext\n if (state.mounted) return\n\n const container = document.createElement('div')\n container.setAttribute('data-gr-dialog-service-host', '')\n container.setAttribute('data-gr-overlay-root', '')\n // Хост уезжает в общий портал оверлеев: он должен лежать в той же ветке, что\n // и остальные слои, иначе правило `inert` пометит его вместе со страницей.\n ;(ensurePortalRoot() ?? document.body).appendChild(container)\n state.container = container\n\n // Состояние отдаём пропом: хост монтируется вне дерева и своим `inject` до\n // app-scoped состояния не дотянулся бы. `markRaw` — чтобы Vue не пытался\n // сделать реактивной саму обёртку: реактивна очередь внутри неё.\n const vnode = createVNode(GrDialogServiceHost, { state: markRaw(state) })\n vnode.appContext = state.appContext ?? null\n render(vnode, container)\n state.mounted = true\n}\n\nfunction warnIfContextless(state: DialogServiceState, context: CapturedDialogContext | null): void {\n if (state.contextlessWarned || context?.config || context?.i18n) return\n if (!__GR_DEV__) return\n\n state.contextlessWarned = true\n console.warn(\n '[granularity] useDialogService: диалог открыт без контекста приложения — '\n + 'ни `GrConfigProvider`, ни адаптер i18n не найдены, будут использованы дефолты. '\n + 'Вызовите `useDialogService()` внутри `setup` хотя бы один раз или передайте `setAppContext`.',\n )\n}\n\n/** Снимает смонтированный хост и сбрасывает захваченный контекст одного инстанса. */\nfunction teardownDialogServiceState(state: DialogServiceState): void {\n if (state.container) {\n render(null, state.container)\n state.container.remove()\n state.container = null\n }\n state.mounted = false\n // Сбрасываем и кэш контекста приложения: без этого он переживает teardown и\n // протекает в следующее приложение (в тестах — в следующий тест), из-за чего\n // диалог может подхватить чужие provides и проверка соврёт.\n state.appContext = null\n state.lastCapturedContext = null\n state.contextlessWarned = false\n state.queue.splice(0, state.queue.length)\n state.inFlight.splice(0, state.inFlight.length)\n}\n\n/**\n * Тестовая/служебная очистка смонтированного хоста. Работает над тем же\n * состоянием, что и сам сервис: app-scoped при вызове из `setup` или при\n * единственном зарегистрированном приложении, иначе — модульным.\n */\nexport function teardownDialogService(): void {\n teardownDialogServiceState(resolveDialogServiceState())\n}\n\nfunction enqueue(\n state: DialogServiceState,\n kind: DialogKind,\n message: string,\n options: DialogBaseOptions,\n onConfirm: DialogOnConfirm<any> | undefined,\n captured: CapturedDialogContext,\n): { promise: Promise<DialogResult<any>>, close: () => void } {\n // Императивный сервис клиент-only: монтирует хост в `document.body`. В SSR\n // выполнять его нельзя — очередь мутировалась бы на сервере и текла между\n // запросами. Явно запрещаем вместо тихого no-op.\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n throw new Error(\n '[granularity] useDialogService is client-only and cannot be called during SSR '\n + '(it mounts a host into document.body). Guard imperative dialog calls behind a client-side check.',\n )\n }\n\n ensureMounted(state, options.appContext ?? state.appContext)\n\n const id = makeDialogId()\n let resolveFn!: (result: DialogResult<any>) => void\n const promise = new Promise<DialogResult<any>>((resolve) => {\n resolveFn = resolve\n })\n\n // Синглтон `dialogService` создаётся на импорте модуля, вне `setup`, и своего\n // контекста не имеет в принципе. Берём последний захваченный — иначе такие\n // вызовы навсегда остаются без i18n и без `GrConfigProvider`.\n const context = captured.config || captured.i18n ? captured : state.lastCapturedContext\n warnIfContextless(state, context)\n\n const request: DialogRequest = {\n id,\n kind,\n message,\n options: { ...options, message },\n onConfirm,\n priority: options.priority ?? 0,\n nested: false,\n settled: false,\n resolve: resolveFn,\n config: context?.config ?? null,\n i18n: context?.i18n ?? null,\n }\n\n enqueueDialogRequest(state, request)\n\n // Закрытие через промис идёт тем же путём, что и кнопка в окне: заявка сама\n // знает, завершена ли она, а окно по её исчезновению свернёт всё, что под неё\n // заведено (в т.ч. оборвёт in-flight `onConfirm` через `abort()`).\n const close = (): void => {\n settleDialogRequest(state, request, { action: 'close' })\n }\n\n return { promise, close }\n}\n\nfunction withClose<T>(promise: Promise<T>, close: () => void): DialogPromise<T> {\n const result = promise as DialogPromise<T>\n result.close = close\n return result\n}\n\nfunction mergeErrorDefaults(\n defaults: DialogServiceDefaults,\n options: DialogBaseOptions,\n): DialogBaseOptions & Pick<DialogConfirmOptions, 'cancelText'> {\n return {\n size: defaults.size,\n confirmText: defaults.confirmText,\n confirmVariant: defaults.confirmVariant,\n confirmTone: defaults.confirmTone,\n cancelText: defaults.cancelText,\n buttonSize: defaults.buttonSize,\n closeOnBackdrop: defaults.closeOnBackdrop,\n closeOnEsc: defaults.closeOnEsc,\n errorParsers: defaults.errorParsers,\n errorTexts: defaults.errorTexts,\n errorMessageKey: defaults.errorMessageKey,\n fieldLabels: defaults.fieldLabels,\n ...options,\n }\n}\n\n/**\n * Создаёт сервис диалогов. Опциональные `defaults` применяются ко всем\n * вызовам (можно переопределить в каждом конкретном вызове).\n *\n * Контекст приложения кэшируется автоматически при первом вызове внутри\n * `setup`. Для вызовов вне компонента используйте `setAppContext`.\n */\nexport function useDialogService(defaults: DialogServiceDefaults = {}): DialogService {\n const state = resolveDialogServiceState()\n\n // Авто-кэш appContext из текущего компонента (если вызвано в setup).\n const instance = getCurrentInstance()\n if (instance?.appContext && !state.appContext) {\n state.appContext = instance.appContext\n }\n\n // Конфиг и i18n захватываем здесь и только здесь: `inject` работает лишь в\n // `setup`, а хост монтируется вне дерева и сам до провайдера не дотянется.\n // Храним в замыкании сервиса, а не в состоянии, — иначе два поддерева с\n // разными провайдерами делили бы конфиг первого.\n // i18n захватываем не «на всякий случай»: дочерний `GrConfirmDialog` сам зовёт\n // `useGranularityTranslations()`, но его `inject` из хоста уходит в\n // `appContext.provides` — то есть видит только установку через `app.use()`.\n // Адаптер, переданный пропом `<GrConfigProvider i18n>`, живёт в дереве, и без\n // захвата диалог откатился бы на встроенные английские строки.\n //\n // `markRaw` — потому что дальше контекст ложится в `reactive`-очередь, а она\n // разворачивает вложенные рефы (см. комментарий в `store.ts`).\n const capturedI18n = instance ? resolveGranularityI18n() : null\n const captured: CapturedDialogContext = {\n config: instance ? markRaw(useGrConfig()) : null,\n i18n: capturedI18n ? markRaw(capturedI18n) : null,\n }\n\n if (captured.config || captured.i18n)\n state.lastCapturedContext = captured\n\n function confirm(message: string, options: DialogConfirmOptions = {}): DialogPromise<boolean> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(state, 'confirm', message, merged, options.onConfirm, captured)\n return withClose(promise.then(r => r.action === 'confirm'), close)\n }\n\n function alert(message: string, options: DialogAlertOptions = {}): DialogPromise<void> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(state, 'alert', message, merged, options.onConfirm, captured)\n return withClose(promise.then(() => undefined), close)\n }\n\n function prompt(message: string, options: DialogPromptOptions = {}): DialogPromise<string | null> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(state, 'prompt', message, merged, options.onConfirm, captured)\n return withClose(\n promise.then(r => (r.action === 'confirm' ? ((r.value as string) ?? '') : null)),\n close,\n )\n }\n\n function open<V = unknown>(\n kind: DialogKind,\n message: string,\n options: DialogBaseOptions = {},\n ): DialogPromise<DialogResult<V>> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(\n state,\n kind,\n message,\n merged,\n (options as DialogConfirmOptions).onConfirm,\n captured,\n )\n return withClose(promise as Promise<DialogResult<V>>, close)\n }\n\n // Порядок разбора — FIFO, как и порядок показа: `pop()` резолвил промисы\n // задом наперёд, и вызывающий получал результаты в обратном порядке.\n function closeAll(): void {\n for (const request of [...state.queue])\n settleDialogRequest(state, request, { action: 'close' })\n }\n\n function setAppContext(ctx: AppContext | null): void {\n state.appContext = ctx\n }\n\n return { confirm, alert, prompt, open, closeAll, setAppContext }\n}\n\n/** Готовый синглтон-сервис с дефолтными настройками. */\nexport const dialogService: DialogService = {\n confirm: (message, options) => useDialogService().confirm(message, options),\n alert: (message, options) => useDialogService().alert(message, options),\n prompt: (message, options) => useDialogService().prompt(message, options),\n open: (kind, message, options) => useDialogService().open(kind, message, options),\n closeAll: () => useDialogService().closeAll(),\n setAppContext: ctx => useDialogService().setAppContext(ctx),\n}\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceItem — одно окно императивного сервиса. Внутренний компонент,\n * в шаблонах напрямую не используется.\n *\n * Всё состояние живёт здесь, а не на хосте, ровно потому, что окон может быть\n * несколько: вложенный вызов из `onConfirm` показывается поверх родителя, и\n * общий `loading` с общей ошибкой означали бы, что верхнее окно пишет в нижнее.\n */\nimport { computed, inject, nextTick, onBeforeUnmount, onMounted, provide, ref, shallowRef } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrConfirmDialog from '../GrConfirmDialog/GrConfirmDialog.vue'\nimport GrPromptDialog from '../GrPromptDialog/GrPromptDialog.vue'\nimport {\n coreResponseErrorParsers,\n createResponseErrorClassifier,\n responseErrorParserPresets,\n} from '../GrResponseErrorBanner/parsers'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nimport { GR_CONFIG_KEY, type GrComponentDefaults, type GrConfigContext } from '../GrConfigProvider/context'\nimport { GRANULARITY_I18N_KEY } from '../../i18n/adapter'\nimport { resolveGranularityI18n, useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport { finishDialogInFlight, removeDialogRequest, settleDialogRequest, startDialogInFlight } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\nimport type { DialogCloseAction, DialogConfirmContext, DialogConfirmOptions, DialogPromptOptions } from './types'\n\nconst props = defineProps<{\n request: DialogRequest\n}>()\n\n// Состояние инстанса приходит через `provide` хоста, а не пропом: окно его\n// мутирует (очередь, список заявок в полёте), а мутировать проп — значит писать\n// в чужое владение.\nconst state = inject(GRANULARITY_DIALOG_SERVICE_STATE) as DialogServiceState\n\n// ————— Мост конфига и i18n.\n//\n// Окно рендерится через `render(vnode, container)` в `document.body`, то есть вне\n// дерева компонентов. Vue в этом случае берёт `provides` только из `appContext`,\n// куда попадает лишь `app.provide()` — значения от `<GrConfigProvider>` до сюда\n// не доходят. Поэтому вызов сервиса захватывает их в `setup` и кладёт в заявку,\n// а окно раздаёт вниз конфиг **своей** заявки.\nconst EMPTY_DEFAULTS: GrComponentDefaults = Object.freeze({})\n\nprovide<GrConfigContext>(GR_CONFIG_KEY, {\n size: computed(() => props.request.config?.size.value),\n componentDefaults: computed(() => props.request.config?.componentDefaults.value ?? EMPTY_DEFAULTS),\n})\n\n// i18n: тот же приём. Адаптер хоста (из `appContext`) — запасной вариант для\n// вызова без захваченного: без него установка через `app.use()` осталась бы\n// незамеченной.\nconst hostI18n = resolveGranularityI18n()\n\nprovide(GRANULARITY_I18N_KEY, {\n t: (key: string, params?: Record<string, unknown>) => {\n const adapter = props.request.i18n ?? hostI18n\n // Вернуть сам ключ — принятый в пакете сигнал «перевода нет»: потребитель\n // подставит свой fallback-текст (см. `useGranularityTranslations`).\n return adapter?.t(key, params) ?? key\n },\n get locale() {\n return (props.request.i18n ?? hostI18n)?.locale\n },\n})\n\n// Подвал alert-ветки рисуется здесь, поэтому и перевод кнопки достаётся здесь.\n// Адаптер берём тот же, что раздан вниз: собственный `provide` компонента его\n// же `inject` не видит, и `useGranularityTranslations()` без аргумента ушёл бы\n// мимо захваченного вызовом адаптера — в фолбэк из `appContext`.\nconst alertConfirmText = computed(() => {\n const { t } = useGranularityTranslations(props.request.i18n ?? hostI18n)\n return t('gr.dialog.ok', 'OK')\n})\n\nconst open = ref(false)\nconst loading = ref(false)\nconst currentError = shallowRef<ResponseErrorInfo | null>(null)\n/**\n * Ошибки по именам полей. Карта, а не одна строка: `setFieldError(field, …)`\n * обещает адресность, и обещание должно быть правдой ещё до того, как в\n * диалоге появится второе поле.\n */\nconst fieldErrors = ref<Record<string, string>>({})\nconst promptValue = ref(\n props.request.options && 'value' in props.request.options\n ? ((props.request.options as { value?: string }).value ?? '')\n : '',\n)\n\n/** Единственное поле `GrPromptDialog` называется `value`. */\nconst PROMPT_FIELD = 'value'\n\n// Одна запись — её и показываем: адрес не важен, когда адресат один.\nconst promptFieldError = computed<string | null>(() => {\n const own = fieldErrors.value[PROMPT_FIELD]\n if (own != null) return own\n\n const entries = Object.values(fieldErrors.value)\n return entries.length === 1 ? entries[0] : null\n})\n\nconst hasFieldError = computed(() => Object.keys(fieldErrors.value).length > 0)\n\nconst controller = new AbortController()\nlet externalAbortCleanup: (() => void) | null = null\n\nfunction buildClassifier() {\n const opts = props.request.options\n const parsers = typeof opts.errorParsers === 'function'\n ? opts.errorParsers(responseErrorParserPresets)\n : opts.errorParsers ?? coreResponseErrorParsers\n\n return createResponseErrorClassifier({\n parsers,\n texts: opts.errorTexts,\n messageKey: opts.errorMessageKey,\n })\n}\n\nonMounted(() => {\n // Внешний AbortSignal закрывает диалог как `close`.\n const sig = props.request.options.signal\n if (sig) {\n if (sig.aborted) {\n finish('close')\n return\n }\n const onAbort = () => finish('close')\n sig.addEventListener('abort', onAbort)\n externalAbortCleanup = () => sig.removeEventListener('abort', onAbort)\n }\n\n void nextTick(() => {\n open.value = true\n })\n})\n\nonBeforeUnmount(() => {\n // Снимаем всё, что заведено под заявку: подписку на внешний сигнал и свой\n // контроллер. Размонтирование — единственная точка, которую видят все три\n // пути завершения (кнопка, `close()` промиса, `closeAll()`), поэтому обрыв\n // in-flight `onConfirm` живёт именно здесь.\n externalAbortCleanup?.()\n externalAbortCleanup = null\n controller.abort()\n\n finishDialogInFlight(state, props.request)\n})\n\nfunction finish(action: DialogCloseAction, value?: unknown): void {\n // Снятие заявки из очереди откладываем на тик: окно должно успеть закрыться\n // до того, как исчезнет из разметки.\n if (!settleDialogRequest(state, props.request, { action, value }, { removeFromQueue: false }))\n return\n\n open.value = false\n void nextTick(() => removeDialogRequest(state, props.request))\n}\n\nfunction buildContext(value: unknown): DialogConfirmContext<any> & { _errorSet: () => boolean } {\n let errorSet = false\n\n const setError = (message: string | null): void => {\n if (message == null) {\n currentError.value = null\n return\n }\n errorSet = true\n currentError.value = { kind: 'unknown', message, raw: message }\n }\n\n const setFieldError = (field: string, message: string | null): void => {\n const next = { ...fieldErrors.value }\n if (message == null) {\n delete next[field]\n fieldErrors.value = next\n return\n }\n errorSet = true\n next[field] = message\n fieldErrors.value = next\n }\n\n const clearErrors = (): void => {\n currentError.value = null\n fieldErrors.value = {}\n }\n\n const setRawError = async (raw: unknown, meta?: Record<string, unknown>): Promise<ResponseErrorInfo | null> => {\n const info = await buildClassifier()(raw, meta)\n if (info.fieldErrors?.length) {\n const next = { ...fieldErrors.value }\n for (const entry of info.fieldErrors)\n next[entry.field] = entry.messages.join(' ')\n fieldErrors.value = next\n }\n currentError.value = info\n errorSet = true\n return info\n }\n\n return {\n value,\n signal: controller.signal,\n setError,\n setRawError,\n setFieldError,\n clearErrors,\n setLoading: (v: boolean) => { loading.value = v },\n close: (action: DialogCloseAction = 'close') => finish(action),\n _errorSet: () => errorSet,\n }\n}\n\nasync function handleConfirm(): Promise<void> {\n const req = props.request\n const value = req.kind === 'prompt' ? promptValue.value : undefined\n\n if (!req.onConfirm) {\n finish('confirm', value)\n return\n }\n\n currentError.value = null\n fieldErrors.value = {}\n loading.value = true\n\n const ctx = buildContext(value)\n\n // Пока колбэк в полёте, заявка считается родителем для новых вызовов: диалог,\n // открытый изнутри `onConfirm`, показывается поверх, а не встаёт в очередь за\n // тем, кто его ждёт.\n startDialogInFlight(state, req)\n\n try {\n const result = await req.onConfirm(ctx)\n // Сначала проверка «диалог ещё жив», потом запись состояния: за время\n // `await` заявку мог завершить `ctx.close()` или `closeAll()`.\n if (req.settled) return\n loading.value = false\n if (result === false) return // оставить открытым без ошибки\n if (currentError.value || hasFieldError.value || ctx._errorSet()) return // есть ошибка — не закрывать\n finish('confirm', value)\n }\n catch (error) {\n if (req.settled) return\n loading.value = false\n if (!ctx._errorSet()) await ctx.setRawError(error)\n // оставить диалог открытым с показанной ошибкой\n }\n finally {\n finishDialogInFlight(state, req)\n }\n}\n\nfunction handleCancel(): void {\n finish('cancel')\n}\n\nfunction handleModelUpdate(value: boolean): void {\n if (!value) finish('close')\n}\n\n// Опции веток читаются типизированно, без `as any` в шаблоне.\nconst promptOptions = computed<DialogPromptOptions>(() => props.request.options as DialogPromptOptions)\nconst confirmOptions = computed<DialogConfirmOptions>(() => props.request.options as DialogConfirmOptions)\n\n// Прокидываемые в дочерний диалог общие пропы.\nconst sharedProps = computed(() => {\n const o = props.request.options\n return {\n title: o.title,\n size: o.size,\n closeOnBackdrop: o.closeOnBackdrop,\n closeOnEsc: o.closeOnEsc,\n // Подавление «мягкого» закрытия на время `onConfirm` считает сам диалог по\n // `confirmLoading` — здесь только включаем режим, чтобы механизм жил в\n // одном месте, а не дублировался сервисом.\n persistent: true,\n showHeader: o.showHeader,\n showCloseButton: o.showCloseButton,\n headerConfig: o.headerConfig,\n footerConfig: o.footerConfig,\n bodyConfig: o.bodyConfig,\n closeLabel: o.closeLabel,\n buttonSize: o.buttonSize,\n confirmText: o.confirmText,\n confirmVariant: o.confirmVariant,\n confirmTone: o.confirmTone,\n }\n})\n</script>\n\n<template>\n <!-- prompt -->\n <GrPromptDialog\n v-if=\"request.kind === 'prompt'\"\n v-bind=\"sharedProps\"\n v-model:value=\"promptValue\"\n :model-value=\"open\"\n :description=\"request.options.message ?? request.options.description\"\n :label=\"promptOptions.label\"\n :placeholder=\"promptOptions.placeholder\"\n :required=\"promptOptions.required ?? false\"\n :required-error-text=\"promptOptions.requiredErrorText\"\n :cancel-text=\"promptOptions.cancelText\"\n :input-type=\"promptOptions.inputType\"\n :inputmode=\"promptOptions.inputmode\"\n :maxlength=\"promptOptions.maxlength\"\n :show-count=\"promptOptions.showCount\"\n :multiline=\"promptOptions.multiline\"\n :rows=\"promptOptions.rows\"\n :autosize=\"promptOptions.autosize\"\n :rules=\"promptOptions.rules\"\n :error=\"currentError\"\n :field-error=\"promptFieldError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n\n <!-- alert: confirm-диалог без кнопки Cancel -->\n <GrConfirmDialog\n v-else-if=\"request.kind === 'alert'\"\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n >\n <template #footer>\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton\n data-testid=\"gr-alert-confirm\"\n :variant=\"sharedProps.confirmVariant ?? 'primary'\"\n :tone=\"sharedProps.confirmTone ?? 'primary'\"\n :size=\"sharedProps.buttonSize\"\n :loading=\"loading\"\n @click=\"handleConfirm\"\n >\n {{ sharedProps.confirmText ?? alertConfirmText }}\n </GrButton>\n </div>\n </template>\n </GrConfirmDialog>\n\n <!-- confirm -->\n <GrConfirmDialog\n v-else\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :cancel-text=\"confirmOptions.cancelText\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceItem — одно окно императивного сервиса. Внутренний компонент,\n * в шаблонах напрямую не используется.\n *\n * Всё состояние живёт здесь, а не на хосте, ровно потому, что окон может быть\n * несколько: вложенный вызов из `onConfirm` показывается поверх родителя, и\n * общий `loading` с общей ошибкой означали бы, что верхнее окно пишет в нижнее.\n */\nimport { computed, inject, nextTick, onBeforeUnmount, onMounted, provide, ref, shallowRef } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrConfirmDialog from '../GrConfirmDialog/GrConfirmDialog.vue'\nimport GrPromptDialog from '../GrPromptDialog/GrPromptDialog.vue'\nimport {\n coreResponseErrorParsers,\n createResponseErrorClassifier,\n responseErrorParserPresets,\n} from '../GrResponseErrorBanner/parsers'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nimport { GR_CONFIG_KEY, type GrComponentDefaults, type GrConfigContext } from '../GrConfigProvider/context'\nimport { GRANULARITY_I18N_KEY } from '../../i18n/adapter'\nimport { resolveGranularityI18n, useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport { finishDialogInFlight, removeDialogRequest, settleDialogRequest, startDialogInFlight } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\nimport type { DialogCloseAction, DialogConfirmContext, DialogConfirmOptions, DialogPromptOptions } from './types'\n\nconst props = defineProps<{\n request: DialogRequest\n}>()\n\n// Состояние инстанса приходит через `provide` хоста, а не пропом: окно его\n// мутирует (очередь, список заявок в полёте), а мутировать проп — значит писать\n// в чужое владение.\nconst state = inject(GRANULARITY_DIALOG_SERVICE_STATE) as DialogServiceState\n\n// ————— Мост конфига и i18n.\n//\n// Окно рендерится через `render(vnode, container)` в `document.body`, то есть вне\n// дерева компонентов. Vue в этом случае берёт `provides` только из `appContext`,\n// куда попадает лишь `app.provide()` — значения от `<GrConfigProvider>` до сюда\n// не доходят. Поэтому вызов сервиса захватывает их в `setup` и кладёт в заявку,\n// а окно раздаёт вниз конфиг **своей** заявки.\nconst EMPTY_DEFAULTS: GrComponentDefaults = Object.freeze({})\n\nprovide<GrConfigContext>(GR_CONFIG_KEY, {\n size: computed(() => props.request.config?.size.value),\n componentDefaults: computed(() => props.request.config?.componentDefaults.value ?? EMPTY_DEFAULTS),\n})\n\n// i18n: тот же приём. Адаптер хоста (из `appContext`) — запасной вариант для\n// вызова без захваченного: без него установка через `app.use()` осталась бы\n// незамеченной.\nconst hostI18n = resolveGranularityI18n()\n\nprovide(GRANULARITY_I18N_KEY, {\n t: (key: string, params?: Record<string, unknown>) => {\n const adapter = props.request.i18n ?? hostI18n\n // Вернуть сам ключ — принятый в пакете сигнал «перевода нет»: потребитель\n // подставит свой fallback-текст (см. `useGranularityTranslations`).\n return adapter?.t(key, params) ?? key\n },\n get locale() {\n return (props.request.i18n ?? hostI18n)?.locale\n },\n})\n\n// Подвал alert-ветки рисуется здесь, поэтому и перевод кнопки достаётся здесь.\n// Адаптер берём тот же, что раздан вниз: собственный `provide` компонента его\n// же `inject` не видит, и `useGranularityTranslations()` без аргумента ушёл бы\n// мимо захваченного вызовом адаптера — в фолбэк из `appContext`.\nconst alertConfirmText = computed(() => {\n const { t } = useGranularityTranslations(props.request.i18n ?? hostI18n)\n return t('gr.dialog.ok', 'OK')\n})\n\nconst open = ref(false)\nconst loading = ref(false)\nconst currentError = shallowRef<ResponseErrorInfo | null>(null)\n/**\n * Ошибки по именам полей. Карта, а не одна строка: `setFieldError(field, …)`\n * обещает адресность, и обещание должно быть правдой ещё до того, как в\n * диалоге появится второе поле.\n */\nconst fieldErrors = ref<Record<string, string>>({})\nconst promptValue = ref(\n props.request.options && 'value' in props.request.options\n ? ((props.request.options as { value?: string }).value ?? '')\n : '',\n)\n\n/** Единственное поле `GrPromptDialog` называется `value`. */\nconst PROMPT_FIELD = 'value'\n\n// Одна запись — её и показываем: адрес не важен, когда адресат один.\nconst promptFieldError = computed<string | null>(() => {\n const own = fieldErrors.value[PROMPT_FIELD]\n if (own != null) return own\n\n const entries = Object.values(fieldErrors.value)\n return entries.length === 1 ? entries[0] : null\n})\n\nconst hasFieldError = computed(() => Object.keys(fieldErrors.value).length > 0)\n\nconst controller = new AbortController()\nlet externalAbortCleanup: (() => void) | null = null\n\nfunction buildClassifier() {\n const opts = props.request.options\n const parsers = typeof opts.errorParsers === 'function'\n ? opts.errorParsers(responseErrorParserPresets)\n : opts.errorParsers ?? coreResponseErrorParsers\n\n return createResponseErrorClassifier({\n parsers,\n texts: opts.errorTexts,\n messageKey: opts.errorMessageKey,\n })\n}\n\nonMounted(() => {\n // Внешний AbortSignal закрывает диалог как `close`.\n const sig = props.request.options.signal\n if (sig) {\n if (sig.aborted) {\n finish('close')\n return\n }\n const onAbort = () => finish('close')\n sig.addEventListener('abort', onAbort)\n externalAbortCleanup = () => sig.removeEventListener('abort', onAbort)\n }\n\n void nextTick(() => {\n open.value = true\n })\n})\n\nonBeforeUnmount(() => {\n // Снимаем всё, что заведено под заявку: подписку на внешний сигнал и свой\n // контроллер. Размонтирование — единственная точка, которую видят все три\n // пути завершения (кнопка, `close()` промиса, `closeAll()`), поэтому обрыв\n // in-flight `onConfirm` живёт именно здесь.\n externalAbortCleanup?.()\n externalAbortCleanup = null\n controller.abort()\n\n finishDialogInFlight(state, props.request)\n})\n\nfunction finish(action: DialogCloseAction, value?: unknown): void {\n // Снятие заявки из очереди откладываем на тик: окно должно успеть закрыться\n // до того, как исчезнет из разметки.\n if (!settleDialogRequest(state, props.request, { action, value }, { removeFromQueue: false }))\n return\n\n open.value = false\n void nextTick(() => removeDialogRequest(state, props.request))\n}\n\nfunction buildContext(value: unknown): DialogConfirmContext<any> & { _errorSet: () => boolean } {\n let errorSet = false\n\n const setError = (message: string | null): void => {\n if (message == null) {\n currentError.value = null\n return\n }\n errorSet = true\n currentError.value = { kind: 'unknown', message, raw: message }\n }\n\n const setFieldError = (field: string, message: string | null): void => {\n const next = { ...fieldErrors.value }\n if (message == null) {\n delete next[field]\n fieldErrors.value = next\n return\n }\n errorSet = true\n next[field] = message\n fieldErrors.value = next\n }\n\n const clearErrors = (): void => {\n currentError.value = null\n fieldErrors.value = {}\n }\n\n const setRawError = async (raw: unknown, meta?: Record<string, unknown>): Promise<ResponseErrorInfo | null> => {\n const info = await buildClassifier()(raw, meta)\n if (info.fieldErrors?.length) {\n const next = { ...fieldErrors.value }\n for (const entry of info.fieldErrors)\n next[entry.field] = entry.messages.join(' ')\n fieldErrors.value = next\n }\n currentError.value = info\n errorSet = true\n return info\n }\n\n return {\n value,\n signal: controller.signal,\n setError,\n setRawError,\n setFieldError,\n clearErrors,\n setLoading: (v: boolean) => { loading.value = v },\n close: (action: DialogCloseAction = 'close') => finish(action),\n _errorSet: () => errorSet,\n }\n}\n\nasync function handleConfirm(): Promise<void> {\n const req = props.request\n const value = req.kind === 'prompt' ? promptValue.value : undefined\n\n if (!req.onConfirm) {\n finish('confirm', value)\n return\n }\n\n currentError.value = null\n fieldErrors.value = {}\n loading.value = true\n\n const ctx = buildContext(value)\n\n // Пока колбэк в полёте, заявка считается родителем для новых вызовов: диалог,\n // открытый изнутри `onConfirm`, показывается поверх, а не встаёт в очередь за\n // тем, кто его ждёт.\n startDialogInFlight(state, req)\n\n try {\n const result = await req.onConfirm(ctx)\n // Сначала проверка «диалог ещё жив», потом запись состояния: за время\n // `await` заявку мог завершить `ctx.close()` или `closeAll()`.\n if (req.settled) return\n loading.value = false\n if (result === false) return // оставить открытым без ошибки\n if (currentError.value || hasFieldError.value || ctx._errorSet()) return // есть ошибка — не закрывать\n finish('confirm', value)\n }\n catch (error) {\n if (req.settled) return\n loading.value = false\n if (!ctx._errorSet()) await ctx.setRawError(error)\n // оставить диалог открытым с показанной ошибкой\n }\n finally {\n finishDialogInFlight(state, req)\n }\n}\n\nfunction handleCancel(): void {\n finish('cancel')\n}\n\nfunction handleModelUpdate(value: boolean): void {\n if (!value) finish('close')\n}\n\n// Опции веток читаются типизированно, без `as any` в шаблоне.\nconst promptOptions = computed<DialogPromptOptions>(() => props.request.options as DialogPromptOptions)\nconst confirmOptions = computed<DialogConfirmOptions>(() => props.request.options as DialogConfirmOptions)\n\n// Прокидываемые в дочерний диалог общие пропы.\nconst sharedProps = computed(() => {\n const o = props.request.options\n return {\n title: o.title,\n size: o.size,\n closeOnBackdrop: o.closeOnBackdrop,\n closeOnEsc: o.closeOnEsc,\n // Подавление «мягкого» закрытия на время `onConfirm` считает сам диалог по\n // `confirmLoading` — здесь только включаем режим, чтобы механизм жил в\n // одном месте, а не дублировался сервисом.\n persistent: true,\n showHeader: o.showHeader,\n showCloseButton: o.showCloseButton,\n headerConfig: o.headerConfig,\n footerConfig: o.footerConfig,\n bodyConfig: o.bodyConfig,\n closeLabel: o.closeLabel,\n buttonSize: o.buttonSize,\n confirmText: o.confirmText,\n confirmVariant: o.confirmVariant,\n confirmTone: o.confirmTone,\n }\n})\n</script>\n\n<template>\n <!-- prompt -->\n <GrPromptDialog\n v-if=\"request.kind === 'prompt'\"\n v-bind=\"sharedProps\"\n v-model:value=\"promptValue\"\n :model-value=\"open\"\n :description=\"request.options.message ?? request.options.description\"\n :label=\"promptOptions.label\"\n :placeholder=\"promptOptions.placeholder\"\n :required=\"promptOptions.required ?? false\"\n :required-error-text=\"promptOptions.requiredErrorText\"\n :cancel-text=\"promptOptions.cancelText\"\n :input-type=\"promptOptions.inputType\"\n :inputmode=\"promptOptions.inputmode\"\n :maxlength=\"promptOptions.maxlength\"\n :show-count=\"promptOptions.showCount\"\n :multiline=\"promptOptions.multiline\"\n :rows=\"promptOptions.rows\"\n :autosize=\"promptOptions.autosize\"\n :rules=\"promptOptions.rules\"\n :error=\"currentError\"\n :field-error=\"promptFieldError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n\n <!-- alert: confirm-диалог без кнопки Cancel -->\n <GrConfirmDialog\n v-else-if=\"request.kind === 'alert'\"\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n >\n <template #footer>\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton\n data-testid=\"gr-alert-confirm\"\n :variant=\"sharedProps.confirmVariant ?? 'primary'\"\n :tone=\"sharedProps.confirmTone ?? 'primary'\"\n :size=\"sharedProps.buttonSize\"\n :loading=\"loading\"\n @click=\"handleConfirm\"\n >\n {{ sharedProps.confirmText ?? alertConfirmText }}\n </GrButton>\n </div>\n </template>\n </GrConfirmDialog>\n\n <!-- confirm -->\n <GrConfirmDialog\n v-else\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :cancel-text=\"confirmOptions.cancelText\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceHost — внутренний компонент-оркестратор императивного\n * `useDialogService`. Не предназначен для прямого использования в шаблонах.\n *\n * Держит раскладку очереди и ничего больше: состояние каждой заявки — загрузка,\n * ошибки, введённое значение, мост конфига и i18n — живёт в\n * `GrDialogServiceItem`, потому что окон может быть несколько.\n *\n * Видимы голова очереди и её вложенные окна (`store.visibleDialogRequests`):\n * обычные вызовы по-прежнему показываются по одному, а диалог, открытый из\n * `onConfirm` другого диалога, ложится поверх родителя — иначе он ждал бы в\n * очереди того, кто ждёт его.\n */\nimport { computed, inject, provide } from 'vue'\n\nimport GrDialogServiceItem from './GrDialogServiceItem.vue'\nimport { visibleDialogRequests } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\n\nexport interface GrDialogServiceHostProps {\n /**\n * Состояние инстанса сервиса. Приходит пропом при программном монтировании:\n * хост живёт вне дерева и своим `inject` до app-scoped состояния не дотянулся\n * бы. `inject` остаётся для случая, когда хост поставили в шаблон руками.\n */\n state?: DialogServiceState | null\n}\n\nconst props = defineProps<GrDialogServiceHostProps>()\n\nconst injected = inject(GRANULARITY_DIALOG_SERVICE_STATE, null)\n\nconst activeState = computed<DialogServiceState | null>(() => props.state ?? injected)\n\nconst visible = computed<DialogRequest[]>(() =>\n activeState.value ? visibleDialogRequests(activeState.value) : [],\n)\n\n// Окна берут состояние отсюда, а не пропом: они его мутируют (очередь, список\n// заявок в полёте), а мутировать проп — значит писать в чужое владение.\nprovide(GRANULARITY_DIALOG_SERVICE_STATE, activeState.value!)\n</script>\n\n<template>\n <GrDialogServiceItem\n v-for=\"request in visible\"\n :key=\"request.id\"\n :request=\"request\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceHost — внутренний компонент-оркестратор императивного\n * `useDialogService`. Не предназначен для прямого использования в шаблонах.\n *\n * Держит раскладку очереди и ничего больше: состояние каждой заявки — загрузка,\n * ошибки, введённое значение, мост конфига и i18n — живёт в\n * `GrDialogServiceItem`, потому что окон может быть несколько.\n *\n * Видимы голова очереди и её вложенные окна (`store.visibleDialogRequests`):\n * обычные вызовы по-прежнему показываются по одному, а диалог, открытый из\n * `onConfirm` другого диалога, ложится поверх родителя — иначе он ждал бы в\n * очереди того, кто ждёт его.\n */\nimport { computed, inject, provide } from 'vue'\n\nimport GrDialogServiceItem from './GrDialogServiceItem.vue'\nimport { visibleDialogRequests } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\n\nexport interface GrDialogServiceHostProps {\n /**\n * Состояние инстанса сервиса. Приходит пропом при программном монтировании:\n * хост живёт вне дерева и своим `inject` до app-scoped состояния не дотянулся\n * бы. `inject` остаётся для случая, когда хост поставили в шаблон руками.\n */\n state?: DialogServiceState | null\n}\n\nconst props = defineProps<GrDialogServiceHostProps>()\n\nconst injected = inject(GRANULARITY_DIALOG_SERVICE_STATE, null)\n\nconst activeState = computed<DialogServiceState | null>(() => props.state ?? injected)\n\nconst visible = computed<DialogRequest[]>(() =>\n activeState.value ? visibleDialogRequests(activeState.value) : [],\n)\n\n// Окна берут состояние отсюда, а не пропом: они его мутируют (очередь, список\n// заявок в полёте), а мутировать проп — значит писать в чужое владение.\nprovide(GRANULARITY_DIALOG_SERVICE_STATE, activeState.value!)\n</script>\n\n<template>\n <GrDialogServiceItem\n v-for=\"request in visible\"\n :key=\"request.id\"\n :request=\"request\"\n />\n</template>\n"],"mappings":";;;;;;;;;;AAsFA,SAAgB,2BAA+C;CAC7D,OAAO;EACL,OAAO,SAA0B,CAAC,CAAC;EACnC,SAAS;EACT,WAAW;EACX,YAAY;EACZ,qBAAqB;EACrB,mBAAmB;EACnB,UAAU,CAAC;CACb;AACF;AAEA,IAAI,UAAU;AAEd,SAAgB,eAAuB;CACrC,WAAW;CACX,OAAO,aAAa,KAAK,IAAI,EAAE,GAAG;AACpC;;;;;;AAOA,SAAgB,sBAAsB,OAA4C;CAChF,MAAM,UAA2B,CAAC;CAElC,KAAK,MAAM,WAAW,MAAM,OAC1B,IAAI,QAAQ,WAAW,KAAK,QAAQ,QAClC,QAAQ,KAAK,OAAO;MAEpB;CAGJ,OAAO;AACT;;;;;;;;;AAUA,SAAgB,qBAAqB,OAA2B,SAA8B;CAC5F,MAAM,SAAS,MAAM,SAAS,MAAM,SAAS,SAAS;CAEtD,IAAI,QAAQ;EACV,QAAQ,SAAS;EACjB,MAAM,cAAc,MAAM,MAAM,WAAU,SAAQ,KAAK,OAAO,OAAO,EAAE;EACvE,MAAM,MAAM,OAAO,cAAc,GAAG,GAAG,OAAO;EAC9C;CACF;CAEA,MAAM,eAAe,sBAAsB,KAAK,CAAC,CAAC;CAClD,MAAM,WAAW,MAAM,MAAM,WAC1B,MAAM,UAAU,SAAS,gBAAgB,KAAK,WAAW,QAAQ,QACpE;CAEA,IAAI,WAAW,GACb,MAAM,MAAM,KAAK,OAAO;MAExB,MAAM,MAAM,OAAO,UAAU,GAAG,OAAO;AAC3C;;;;;AAMA,SAAgB,oBAAoB,OAA2B,SAA8B;CAC3F,MAAM,SAAS,KAAK,OAAO;AAC7B;;AAGA,SAAgB,qBAAqB,OAA2B,SAA8B;CAC5F,MAAM,QAAQ,MAAM,SAAS,QAAQ,OAAO;CAC5C,IAAI,SAAS,GACX,MAAM,SAAS,OAAO,OAAO,CAAC;AAClC;;;;;;;;;AAUA,SAAgB,oBACd,OACA,SACA,QACA,EAAE,kBAAkB,SAAwC,CAAC,GACpD;CACT,IAAI,QAAQ,SACV,OAAO;CAET,QAAQ,UAAU;CAClB,QAAQ,QAAQ,MAAM;CAEtB,IAAI,iBACF,oBAAoB,OAAO,OAAO;CAEpC,OAAO;AACT;;AAGA,SAAgB,oBAAoB,OAA2B,SAA8B;CAC3F,MAAM,QAAQ,MAAM,MAAM,WAAU,SAAQ,KAAK,OAAO,QAAQ,EAAE;CAClE,IAAI,SAAS,GACX,MAAM,MAAM,OAAO,OAAO,CAAC;AAC/B;;;;;;;;;;;;;AChKA,IAAa,mCACT,OAAO,IAAI,2CAA2C;;;;;;;;AAS1D,IAAM,mCAAmB,IAAI,IAAwB;;;;;;;;;;;AAYrD,IAAa,iCAAiC,EAC5C,QAAQ,KAAU;CAChB,MAAM,QAAQ,yBAAyB;CAEvC,IAAI,QAAQ,kCAAkC,KAAK;CACnD,iBAAiB,IAAI,KAAK;CAE1B,IAAI,gBAAgB;EAClB,2BAA2B,KAAK;EAChC,iBAAiB,OAAO,KAAK;CAC/B,CAAC;AACH,EACF;AAGA,IAAI,cAAyC;AAE7C,SAAS,iBAAqC;CAC5C,IAAI,CAAC,aACH,cAAc,yBAAyB;CAEzC,OAAO;AACT;AAEA,IAAI,uBAAuB;AAE3B,SAAS,qBAA2B;CAClC,IAAI,sBAAsB;CAC1B,IAAI,EAAA,OAAA,YAAA,eAAA,QAAA,IAAA,aAAA,eAAa;CAEjB,uBAAuB;CACvB,QAAQ,KACN,6PAGF;AACF;;;;;AAMA,SAAS,4BAAgD;CAEvD,IAAI,mBAAmB,GAAG;EACxB,MAAM,WAAW,OAAO,kCAAkC,IAAI;EAC9D,IAAI,UAAU,OAAO;CACvB;CAEA,IAAI,iBAAiB,SAAS,GAC5B,OAAO,iBAAiB,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;CAE1C,IAAI,iBAAiB,OAAO,GAC1B,mBAAmB;CAErB,OAAO,eAAe;AACxB;AAEA,SAAS,cAAc,OAA2B,YAAsC;CACtF,IAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;CAEtE,IAAI,YAAY,MAAM,aAAa;CACnC,IAAI,MAAM,SAAS;CAEnB,MAAM,YAAY,SAAS,cAAc,KAAK;CAC9C,UAAU,aAAa,+BAA+B,EAAE;CACxD,UAAU,aAAa,wBAAwB,EAAE;CAGhD,CAAC,iBAAiB,KAAK,SAAS,KAAA,CAAM,YAAY,SAAS;CAC5D,MAAM,YAAY;CAKlB,MAAM,QAAQ,YAAY,6BAAqB,EAAE,OAAO,QAAQ,KAAK,EAAE,CAAC;CACxE,MAAM,aAAa,MAAM,cAAc;CACvC,OAAO,OAAO,SAAS;CACvB,MAAM,UAAU;AAClB;AAEA,SAAS,kBAAkB,OAA2B,SAA6C;CACjG,IAAI,MAAM,qBAAqB,SAAS,UAAU,SAAS,MAAM;CACjE,IAAI,EAAA,OAAA,YAAA,eAAA,QAAA,IAAA,aAAA,eAAa;CAEjB,MAAM,oBAAoB;CAC1B,QAAQ,KACN,sPAGF;AACF;;AAGA,SAAS,2BAA2B,OAAiC;CACnE,IAAI,MAAM,WAAW;EACnB,OAAO,MAAM,MAAM,SAAS;EAC5B,MAAM,UAAU,OAAO;EACvB,MAAM,YAAY;CACpB;CACA,MAAM,UAAU;CAIhB,MAAM,aAAa;CACnB,MAAM,sBAAsB;CAC5B,MAAM,oBAAoB;CAC1B,MAAM,MAAM,OAAO,GAAG,MAAM,MAAM,MAAM;CACxC,MAAM,SAAS,OAAO,GAAG,MAAM,SAAS,MAAM;AAChD;;;;;;AAOA,SAAgB,wBAA8B;CAC5C,2BAA2B,0BAA0B,CAAC;AACxD;AAEA,SAAS,QACP,OACA,MACA,SACA,SACA,WACA,UAC4D;CAI5D,IAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aACvD,MAAM,IAAI,MACR,gLAEF;CAGF,cAAc,OAAO,QAAQ,cAAc,MAAM,UAAU;CAE3D,MAAM,KAAK,aAAa;CACxB,IAAI;CACJ,MAAM,UAAU,IAAI,SAA4B,YAAY;EAC1D,YAAY;CACd,CAAC;CAKD,MAAM,UAAU,SAAS,UAAU,SAAS,OAAO,WAAW,MAAM;CACpE,kBAAkB,OAAO,OAAO;CAEhC,MAAM,UAAyB;EAC7B;EACA;EACA;EACA,SAAS;GAAE,GAAG;GAAS;EAAQ;EAC/B;EACA,UAAU,QAAQ,YAAY;EAC9B,QAAQ;EACR,SAAS;EACT,SAAS;EACT,QAAQ,SAAS,UAAU;EAC3B,MAAM,SAAS,QAAQ;CACzB;CAEA,qBAAqB,OAAO,OAAO;CAKnC,MAAM,cAAoB;EACxB,oBAAoB,OAAO,SAAS,EAAE,QAAQ,QAAQ,CAAC;CACzD;CAEA,OAAO;EAAE;EAAS;CAAM;AAC1B;AAEA,SAAS,UAAa,SAAqB,OAAqC;CAC9E,MAAM,SAAS;CACf,OAAO,QAAQ;CACf,OAAO;AACT;AAEA,SAAS,mBACP,UACA,SAC8D;CAC9D,OAAO;EACL,MAAM,SAAS;EACf,aAAa,SAAS;EACtB,gBAAgB,SAAS;EACzB,aAAa,SAAS;EACtB,YAAY,SAAS;EACrB,YAAY,SAAS;EACrB,iBAAiB,SAAS;EAC1B,YAAY,SAAS;EACrB,cAAc,SAAS;EACvB,YAAY,SAAS;EACrB,iBAAiB,SAAS;EAC1B,aAAa,SAAS;EACtB,GAAG;CACL;AACF;;;;;;;;AASA,SAAgB,iBAAiB,WAAkC,CAAC,GAAkB;CACpF,MAAM,QAAQ,0BAA0B;CAGxC,MAAM,WAAW,mBAAmB;CACpC,IAAI,UAAU,cAAc,CAAC,MAAM,YACjC,MAAM,aAAa,SAAS;CAe9B,MAAM,eAAe,WAAW,uBAAuB,IAAI;CAC3D,MAAM,WAAkC;EACtC,QAAQ,WAAW,QAAQ,YAAY,CAAC,IAAI;EAC5C,MAAM,eAAe,QAAQ,YAAY,IAAI;CAC/C;CAEA,IAAI,SAAS,UAAU,SAAS,MAC9B,MAAM,sBAAsB;CAE9B,SAAS,QAAQ,SAAiB,UAAgC,CAAC,GAA2B;EAC5F,MAAM,SAAS,mBAAmB,UAAU,OAAO;EACnD,MAAM,EAAE,SAAS,UAAU,QAAQ,OAAO,WAAW,SAAS,QAAQ,QAAQ,WAAW,QAAQ;EACjG,OAAO,UAAU,QAAQ,MAAK,MAAK,EAAE,WAAW,SAAS,GAAG,KAAK;CACnE;CAEA,SAAS,MAAM,SAAiB,UAA8B,CAAC,GAAwB;EACrF,MAAM,SAAS,mBAAmB,UAAU,OAAO;EACnD,MAAM,EAAE,SAAS,UAAU,QAAQ,OAAO,SAAS,SAAS,QAAQ,QAAQ,WAAW,QAAQ;EAC/F,OAAO,UAAU,QAAQ,WAAW,KAAA,CAAS,GAAG,KAAK;CACvD;CAEA,SAAS,OAAO,SAAiB,UAA+B,CAAC,GAAiC;EAChG,MAAM,SAAS,mBAAmB,UAAU,OAAO;EACnD,MAAM,EAAE,SAAS,UAAU,QAAQ,OAAO,UAAU,SAAS,QAAQ,QAAQ,WAAW,QAAQ;EAChG,OAAO,UACL,QAAQ,MAAK,MAAM,EAAE,WAAW,YAAc,EAAE,SAAoB,KAAM,IAAK,GAC/E,KACF;CACF;CAEA,SAAS,KACP,MACA,SACA,UAA6B,CAAC,GACE;EAChC,MAAM,SAAS,mBAAmB,UAAU,OAAO;EACnD,MAAM,EAAE,SAAS,UAAU,QACzB,OACA,MACA,SACA,QACC,QAAiC,WAClC,QACF;EACA,OAAO,UAAU,SAAqC,KAAK;CAC7D;CAIA,SAAS,WAAiB;EACxB,KAAK,MAAM,WAAW,CAAC,GAAG,MAAM,KAAK,GACnC,oBAAoB,OAAO,SAAS,EAAE,QAAQ,QAAQ,CAAC;CAC3D;CAEA,SAAS,cAAc,KAA8B;EACnD,MAAM,aAAa;CACrB;CAEA,OAAO;EAAE;EAAS;EAAO;EAAQ;EAAM;EAAU;CAAc;AACjE;;AAGA,IAAa,gBAA+B;CAC1C,UAAU,SAAS,YAAY,iBAAiB,CAAC,CAAC,QAAQ,SAAS,OAAO;CAC1E,QAAQ,SAAS,YAAY,iBAAiB,CAAC,CAAC,MAAM,SAAS,OAAO;CACtE,SAAS,SAAS,YAAY,iBAAiB,CAAC,CAAC,OAAO,SAAS,OAAO;CACxE,OAAO,MAAM,SAAS,YAAY,iBAAiB,CAAC,CAAC,KAAK,MAAM,SAAS,OAAO;CAChF,gBAAgB,iBAAiB,CAAC,CAAC,SAAS;CAC5C,gBAAe,QAAO,iBAAiB,CAAC,CAAC,cAAc,GAAG;AAC5D;;;;AC3QA,IAAM,eAAe;;;;;;;;;;;;;;;EAjErB,MAAM,QAAQ;EAOd,MAAM,QAAQ,OAAO,gCAAgC;EASrD,MAAM,iBAAsC,OAAO,OAAO,CAAC,CAAC;EAE5D,QAAyB,eAAe;GACtC,MAAM,eAAe,MAAM,QAAQ,QAAQ,KAAK,KAAK;GACrD,mBAAmB,eAAe,MAAM,QAAQ,QAAQ,kBAAkB,SAAS,cAAc;EACnG,CAAC;EAKD,MAAM,WAAW,uBAAuB;EAExC,QAAQ,sBAAsB;GAC5B,IAAI,KAAa,WAAqC;IAIpD,QAHgB,MAAM,QAAQ,QAAQ,SAAA,EAGtB,EAAE,KAAK,MAAM,KAAK;GACpC;GACA,IAAI,SAAS;IACX,QAAQ,MAAM,QAAQ,QAAQ,SAAA,EAAW;GAC3C;EACF,CAAC;EAMD,MAAM,mBAAmB,eAAe;GACtC,MAAM,EAAE,MAAM,2BAA2B,MAAM,QAAQ,QAAQ,QAAQ;GACvE,OAAO,EAAE,gBAAgB,IAAI;EAC/B,CAAC;EAED,MAAM,OAAO,IAAI,KAAK;EACtB,MAAM,UAAU,IAAI,KAAK;EACzB,MAAM,eAAe,WAAqC,IAAI;;;;;;EAM9D,MAAM,cAAc,IAA4B,CAAC,CAAC;EAClD,MAAM,cAAc,IAClB,MAAM,QAAQ,WAAW,WAAW,MAAM,QAAQ,UAC5C,MAAM,QAAQ,QAA+B,SAAS,KACxD,EACN;;EAMA,MAAM,mBAAmB,eAA8B;GACrD,MAAM,MAAM,YAAY,MAAM;GAC9B,IAAI,OAAO,MAAM,OAAO;GAExB,MAAM,UAAU,OAAO,OAAO,YAAY,KAAK;GAC/C,OAAO,QAAQ,WAAW,IAAI,QAAQ,KAAK;EAC7C,CAAC;EAED,MAAM,gBAAgB,eAAe,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,SAAS,CAAC;EAE9E,MAAM,aAAa,IAAI,gBAAgB;EACvC,IAAI,uBAA4C;EAEhD,SAAS,kBAAkB;GACzB,MAAM,OAAO,MAAM,QAAQ;GAK3B,OAAO,8BAA8B;IACnC,SALc,OAAO,KAAK,iBAAiB,aACzC,KAAK,aAAa,0BAA0B,IAC5C,KAAK,gBAAgB;IAIvB,OAAO,KAAK;IACZ,YAAY,KAAK;GACnB,CAAC;EACH;EAEA,gBAAgB;GAEd,MAAM,MAAM,MAAM,QAAQ,QAAQ;GAClC,IAAI,KAAK;IACP,IAAI,IAAI,SAAS;KACf,OAAO,OAAO;KACd;IACF;IACA,MAAM,gBAAgB,OAAO,OAAO;IACpC,IAAI,iBAAiB,SAAS,OAAO;IACrC,6BAA6B,IAAI,oBAAoB,SAAS,OAAO;GACvE;GAEA,eAAoB;IAClB,KAAK,QAAQ;GACf,CAAC;EACH,CAAC;EAED,sBAAsB;GAKpB,uBAAuB;GACvB,uBAAuB;GACvB,WAAW,MAAM;GAEjB,qBAAqB,OAAO,MAAM,OAAO;EAC3C,CAAC;EAED,SAAS,OAAO,QAA2B,OAAuB;GAGhE,IAAI,CAAC,oBAAoB,OAAO,MAAM,SAAS;IAAE;IAAQ;GAAM,GAAG,EAAE,iBAAiB,MAAM,CAAC,GAC1F;GAEF,KAAK,QAAQ;GACb,eAAoB,oBAAoB,OAAO,MAAM,OAAO,CAAC;EAC/D;EAEA,SAAS,aAAa,OAA0E;GAC9F,IAAI,WAAW;GAEf,MAAM,YAAY,YAAiC;IACjD,IAAI,WAAW,MAAM;KACnB,aAAa,QAAQ;KACrB;IACF;IACA,WAAW;IACX,aAAa,QAAQ;KAAE,MAAM;KAAW;KAAS,KAAK;IAAQ;GAChE;GAEA,MAAM,iBAAiB,OAAe,YAAiC;IACrE,MAAM,OAAO,EAAE,GAAG,YAAY,MAAM;IACpC,IAAI,WAAW,MAAM;KACnB,OAAO,KAAK;KACZ,YAAY,QAAQ;KACpB;IACF;IACA,WAAW;IACX,KAAK,SAAS;IACd,YAAY,QAAQ;GACtB;GAEA,MAAM,oBAA0B;IAC9B,aAAa,QAAQ;IACrB,YAAY,QAAQ,CAAC;GACvB;GAEA,MAAM,cAAc,OAAO,KAAc,SAAsE;IAC7G,MAAM,OAAO,MAAM,gBAAgB,CAAC,CAAC,KAAK,IAAI;IAC9C,IAAI,KAAK,aAAa,QAAQ;KAC5B,MAAM,OAAO,EAAE,GAAG,YAAY,MAAM;KACpC,KAAK,MAAM,SAAS,KAAK,aACvB,KAAK,MAAM,SAAS,MAAM,SAAS,KAAK,GAAG;KAC7C,YAAY,QAAQ;IACtB;IACA,aAAa,QAAQ;IACrB,WAAW;IACX,OAAO;GACT;GAEA,OAAO;IACL;IACA,QAAQ,WAAW;IACnB;IACA;IACA;IACA;IACA,aAAa,MAAe;KAAE,QAAQ,QAAQ;IAAE;IAChD,QAAQ,SAA4B,YAAY,OAAO,MAAM;IAC7D,iBAAiB;GACnB;EACF;EAEA,eAAe,gBAA+B;GAC5C,MAAM,MAAM,MAAM;GAClB,MAAM,QAAQ,IAAI,SAAS,WAAW,YAAY,QAAQ,KAAA;GAE1D,IAAI,CAAC,IAAI,WAAW;IAClB,OAAO,WAAW,KAAK;IACvB;GACF;GAEA,aAAa,QAAQ;GACrB,YAAY,QAAQ,CAAC;GACrB,QAAQ,QAAQ;GAEhB,MAAM,MAAM,aAAa,KAAK;GAK9B,oBAAoB,OAAO,GAAG;GAE9B,IAAI;IACF,MAAM,SAAS,MAAM,IAAI,UAAU,GAAG;IAGtC,IAAI,IAAI,SAAS;IACjB,QAAQ,QAAQ;IAChB,IAAI,WAAW,OAAO;IACtB,IAAI,aAAa,SAAS,cAAc,SAAS,IAAI,UAAU,GAAG;IAClE,OAAO,WAAW,KAAK;GACzB,SACO,OAAO;IACZ,IAAI,IAAI,SAAS;IACjB,QAAQ,QAAQ;IAChB,IAAI,CAAC,IAAI,UAAU,GAAG,MAAM,IAAI,YAAY,KAAK;GAEnD,UACQ;IACN,qBAAqB,OAAO,GAAG;GACjC;EACF;EAEA,SAAS,eAAqB;GAC5B,OAAO,QAAQ;EACjB;EAEA,SAAS,kBAAkB,OAAsB;GAC/C,IAAI,CAAC,OAAO,OAAO,OAAO;EAC5B;EAGA,MAAM,gBAAgB,eAAoC,MAAM,QAAQ,OAA8B;EACtG,MAAM,iBAAiB,eAAqC,MAAM,QAAQ,OAA+B;EAGzG,MAAM,cAAc,eAAe;GACjC,MAAM,IAAI,MAAM,QAAQ;GACxB,OAAO;IACL,OAAO,EAAE;IACT,MAAM,EAAE;IACR,iBAAiB,EAAE;IACnB,YAAY,EAAE;IAId,YAAY;IACZ,YAAY,EAAE;IACd,iBAAiB,EAAE;IACnB,cAAc,EAAE;IAChB,cAAc,EAAE;IAChB,YAAY,EAAE;IACd,YAAY,EAAE;IACd,YAAY,EAAE;IACd,aAAa,EAAE;IACf,gBAAgB,EAAE;IAClB,aAAa,EAAE;GACjB;EACF,CAAC;;UAMS,QAAA,QAAQ,SAAI,YAAA,UAAA,GADpB,YA0BE,wBA1BF,WA0BE,EAAA,KAAA,EAAA,GAxBQ,YAAA,OAAW;IACX,OAAO,YAAA;wEAAW,QAAA;IACzB,eAAa,KAAA;IACb,aAAa,QAAA,QAAQ,QAAQ,WAAW,QAAA,QAAQ,QAAQ;IACxD,OAAO,cAAA,MAAc;IACrB,aAAa,cAAA,MAAc;IAC3B,UAAU,cAAA,MAAc,YAAQ;IAChC,uBAAqB,cAAA,MAAc;IACnC,eAAa,cAAA,MAAc;IAC3B,cAAY,cAAA,MAAc;IAC1B,WAAW,cAAA,MAAc;IACzB,WAAW,cAAA,MAAc;IACzB,cAAY,cAAA,MAAc;IAC1B,WAAW,cAAA,MAAc;IACzB,MAAM,cAAA,MAAc;IACpB,UAAU,cAAA,MAAc;IACxB,OAAO,cAAA,MAAc;IACrB,OAAO,aAAA;IACP,eAAa,iBAAA;IACb,mBAAiB,QAAA;IACjB,oBAAkB;IAClB,uBAAoB;IACpB,WAAS;IACT,UAAQ;;;;;;;;;;;;;;;;;;;;;SAKE,QAAA,QAAQ,SAAI,WAAA,UAAA,GADzB,YAyBkB,yBAzBlB,WAyBkB;;IAvBf,eAAa,KAAA;MACN,YAAA,OAAW;IAClB,aAAa,QAAA,QAAQ,QAAQ,WAAW,QAAA,QAAQ,QAAQ;IACxD,OAAO,aAAA;IACP,mBAAiB,QAAA;IACjB,oBAAkB;IAClB,uBAAoB;IACpB,WAAS;;IAEC,QAAM,cAYT,CAXN,mBAWM,OAXN,YAWM,CAVJ,YASW,kBAAA;KART,eAAY;KACX,SAAS,YAAA,MAAY,kBAAc;KACnC,MAAM,YAAA,MAAY,eAAW;KAC7B,MAAM,YAAA,MAAY;KAClB,SAAS,QAAA;KACT,SAAO;;4BAEyC,CAAA,gBAAA,gBAA9C,YAAA,MAAY,eAAe,iBAAA,KAAgB,GAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;uBAOtD,YAYE,yBAZF,WAYE;;IAVC,eAAa,KAAA;MACN,YAAA,OAAW;IAClB,aAAa,QAAA,QAAQ,QAAQ,WAAW,QAAA,QAAQ,QAAQ;IACxD,eAAa,eAAA,MAAe;IAC5B,OAAO,aAAA;IACP,mBAAiB,QAAA;IACjB,oBAAkB;IAClB,uBAAoB;IACpB,WAAS;IACT,UAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EElVb,MAAM,QAAQ;EAEd,MAAM,WAAW,OAAO,kCAAkC,IAAI;EAE9D,MAAM,cAAc,eAA0C,MAAM,SAAS,QAAQ;EAErF,MAAM,UAAU,eACd,YAAY,QAAQ,sBAAsB,YAAY,KAAK,IAAI,CAAC,CAClE;EAIA,QAAQ,kCAAkC,YAAY,KAAM;;2BAI1D,mBAIE,UAAA,MAAA,WAHkB,QAAA,QAAX,YAAO;wBADhB,YAIE,6BAAA;KAFC,KAAK,QAAQ;KACJ"}
|
|
1
|
+
{"version":3,"file":"GrDialogService-6s2cQ-_6.js","names":[],"sources":["../../../../src/components/GrDialogService/store.ts","../../../../src/components/GrDialogService/useDialogService.ts","../../../../src/components/GrDialogService/GrDialogServiceItem.vue","../../../../src/components/GrDialogService/GrDialogServiceItem.vue","../../../../src/components/GrDialogService/GrDialogServiceHost.vue","../../../../src/components/GrDialogService/GrDialogServiceHost.vue"],"sourcesContent":["import { reactive } from 'vue'\nimport type { AppContext, Raw } from 'vue'\n\nimport type { GrConfigContext } from '../GrConfigProvider/context'\nimport type { GranularityI18nLike } from '../../internal/granularityI18n'\n\nimport type {\n DialogBaseOptions,\n DialogKind,\n DialogOnConfirm,\n DialogResult,\n} from './types'\n\n/**\n * Внутреннее описание одного запроса диалога в очереди. Создаётся методами\n * сервиса (`confirm`/`alert`/`prompt`/`open`) и потребляется хостом\n * (`GrDialogServiceHost`), который рендерит видимую часть очереди.\n */\nexport interface DialogRequest {\n id: string\n kind: DialogKind\n message: string\n options: DialogBaseOptions\n onConfirm?: DialogOnConfirm<any>\n /** Позиция среди ожидающих: больше — раньше. При равенстве порядок FIFO. */\n priority: number\n /**\n * Заявка создана из `onConfirm` уже открытого диалога и показывается **поверх**\n * него, а не за ним. Без этого вложенный вызов вставал бы в очередь за тем,\n * кто его ждёт: внешнее окно висело бы в загрузке, а промис не резолвился бы\n * никогда.\n */\n nested: boolean\n /**\n * Заявка уже завершена. Флаг живёт на самой заявке, а не на хосте: завершить\n * её могут трое (кнопка в окне, `close()` промиса, `closeAll()`), и общее\n * состояние на хосте означало бы, что второй и третий путь про первый не\n * знают.\n */\n settled: boolean\n /** Резолвит промис вызова детальным результатом `{ action, value }`. */\n resolve: (result: DialogResult<any>) => void\n /**\n * Конфиг и i18n, захваченные в месте вызова сервиса (в `setup`, где вызывающий\n * компонент ещё находится внутри дерева провайдера). Хост монтируется в\n * `document.body` вне дерева, поэтому сам их не увидит — см.\n * `SPEC-GrConfig-resolver.md`.\n *\n * Лежат в запросе, а не в состоянии сервиса: два поддерева с разными\n * провайдерами должны получать каждое свой конфиг.\n *\n * `Raw<…>` обязателен: очередь — `reactive`, а он разворачивает вложенные рефы.\n * Без `markRaw` контекст превратился бы в объект значений, `size.value` стало\n * бы `undefined`, и мост молча перестал бы работать.\n */\n config?: Raw<GrConfigContext> | null\n i18n?: Raw<GranularityI18nLike> | null\n}\n\n/** Контекст, захваченный вызовом `useDialogService()` внутри `setup`. */\nexport interface CapturedDialogContext {\n config: Raw<GrConfigContext> | null\n i18n: Raw<GranularityI18nLike> | null\n}\n\n/**\n * Состояние одного инстанса сервиса: очередь, смонтированный хост и захваченный\n * контекст приложения. Живёт либо в приложении (через\n * `granularityDialogServicePlugin`), либо в ленивом модульном фолбэке для\n * простых SPA — см. `useDialogService.ts`.\n */\nexport interface DialogServiceState {\n /** Очередь заявок. Видимую часть выбирает хост, остальные ждут. */\n queue: DialogRequest[]\n mounted: boolean\n container: HTMLElement | null\n appContext: AppContext | null\n lastCapturedContext: CapturedDialogContext | null\n contextlessWarned: boolean\n /**\n * Заявки, чей `onConfirm` сейчас в полёте, от внешней к самой глубокой.\n * По ним определяется вложенность нового вызова.\n */\n inFlight: DialogRequest[]\n}\n\nexport function createDialogServiceState(): DialogServiceState {\n return {\n queue: reactive<DialogRequest[]>([]),\n mounted: false,\n container: null,\n appContext: null,\n lastCapturedContext: null,\n contextlessWarned: false,\n inFlight: [],\n }\n}\n\nlet counter = 0\n\nexport function makeDialogId(): string {\n counter += 1\n return `gr-dialog-${Date.now()}-${counter}`\n}\n\n/**\n * Видимая часть очереди: голова и непрерывный хвост вложенных заявок за ней.\n * Всё остальное ждёт — обычные вызовы по-прежнему идут по одному, чтобы три\n * алерта из цикла не легли стопкой.\n */\nexport function visibleDialogRequests(state: DialogServiceState): DialogRequest[] {\n const visible: DialogRequest[] = []\n\n for (const request of state.queue) {\n if (visible.length === 0 || request.nested)\n visible.push(request)\n else\n break\n }\n\n return visible\n}\n\n/**\n * Ставит заявку в очередь. Единственное место, где решается её позиция:\n *\n * - вложенная (создана, пока `onConfirm` другой заявки в полёте) встаёт сразу\n * за своим родителем и показывается поверх него;\n * - обычная — перед всеми ожидающими с меньшим приоритетом; показанные окна не\n * трогаются, выдёргивать фокус-ловушку из-под пользователя нельзя.\n */\nexport function enqueueDialogRequest(state: DialogServiceState, request: DialogRequest): void {\n const parent = state.inFlight[state.inFlight.length - 1]\n\n if (parent) {\n request.nested = true\n const parentIndex = state.queue.findIndex(item => item.id === parent.id)\n state.queue.splice(parentIndex + 1, 0, request)\n return\n }\n\n const visibleCount = visibleDialogRequests(state).length\n const insertAt = state.queue.findIndex(\n (item, index) => index >= visibleCount && item.priority < request.priority,\n )\n\n if (insertAt < 0)\n state.queue.push(request)\n else\n state.queue.splice(insertAt, 0, request)\n}\n\n/**\n * Заявка входит в «полёт»: пока её `onConfirm` не вернулся, новые вызовы\n * считаются вложенными в неё.\n */\nexport function startDialogInFlight(state: DialogServiceState, request: DialogRequest): void {\n state.inFlight.push(request)\n}\n\n/** Заявка вышла из полёта (колбэк вернулся или окно размонтировано). */\nexport function finishDialogInFlight(state: DialogServiceState, request: DialogRequest): void {\n const index = state.inFlight.indexOf(request)\n if (index >= 0)\n state.inFlight.splice(index, 1)\n}\n\n/**\n * Единственный способ завершить заявку: идемпотентно резолвит промис и снимает\n * её из очереди.\n *\n * `removeFromQueue: false` нужен окну: оно гасит себя и даёт анимации закрытия\n * доиграть, прежде чем заявка исчезнет из очереди. Корректность от этой\n * отсрочки не зависит: повторное завершение отсекается флагом на заявке.\n */\nexport function settleDialogRequest(\n state: DialogServiceState,\n request: DialogRequest,\n result: DialogResult<any>,\n { removeFromQueue = true }: { removeFromQueue?: boolean } = {},\n): boolean {\n if (request.settled)\n return false\n\n request.settled = true\n request.resolve(result)\n\n if (removeFromQueue)\n removeDialogRequest(state, request)\n\n return true\n}\n\n/** Снимает заявку из очереди, если она ещё там. */\nexport function removeDialogRequest(state: DialogServiceState, request: DialogRequest): void {\n const index = state.queue.findIndex(item => item.id === request.id)\n if (index >= 0)\n state.queue.splice(index, 1)\n}\n","import { createVNode, getCurrentInstance, inject, markRaw, render } from 'vue'\nimport type { App, AppContext, InjectionKey } from 'vue'\n\nimport { ensurePortalRoot } from '../../composables/internal/portalRoot'\nimport GrDialogServiceHost from './GrDialogServiceHost.vue'\nimport { useGrConfig } from '../GrConfigProvider/context'\nimport { resolveGranularityI18n } from '../../internal/granularityI18n'\nimport {\n createDialogServiceState,\n enqueueDialogRequest,\n makeDialogId,\n settleDialogRequest,\n} from './store'\nimport type { CapturedDialogContext, DialogRequest, DialogServiceState } from './store'\nimport type {\n DialogAlertOptions,\n DialogBaseOptions,\n DialogConfirmOptions,\n DialogKind,\n DialogOnConfirm,\n DialogPromise,\n DialogPromptOptions,\n DialogResult,\n DialogService,\n DialogServiceDefaults,\n} from './types'\n\n/**\n * `useDialogService` — императивный сервис диалогов (в духе Element Plus\n * `ElMessageBox`). Позволяет вызывать `confirm`/`alert`/`prompt` из `<script>`\n * или `.ts` без вставки компонента в шаблон.\n *\n * Поверх существующих `GrConfirmDialog`/`GrPromptDialog`: лениво монтирует\n * единый хост (`GrDialogServiceHost`) в `document.body`, наследуя контекст\n * приложения (i18n / тема / `granular-provider`).\n */\n\n/** Ключ provide/inject для app-scoped состояния сервиса (устанавливает плагин ниже). */\nexport const GRANULARITY_DIALOG_SERVICE_STATE: InjectionKey<DialogServiceState>\n = Symbol.for('@feugene/granularity/dialog-service-state')\n\n/**\n * Инстансы, заведённые плагином. Реестр нужен готовому синглтону\n * `dialogService`: он создаётся на импорте модуля, где `inject` не работает\n * вовсе, и сам до app-scoped состояния не дотянется. Ровно один\n * зарегистрированный — ответ однозначен; несколько — выбирать за пользователя\n * нечего, и он получает предупреждение с модульным фолбэком.\n */\nconst registeredStates = new Set<DialogServiceState>()\n\n/**\n * Vue-плагин: даёт каждому приложению собственное состояние сервиса и снимает\n * его хост вместе с приложением. Обязателен для нескольких Vue-приложений на\n * одной странице (микрофронтенды — иначе они делят одну очередь и один хост) и\n * для HMR, где контейнер прошлого приложения иначе остаётся в `document.body`.\n *\n * ```ts\n * app.use(granularityDialogServicePlugin)\n * ```\n */\nexport const granularityDialogServicePlugin = {\n install(app: App) {\n const state = createDialogServiceState()\n\n app.provide(GRANULARITY_DIALOG_SERVICE_STATE, state)\n registeredStates.add(state)\n\n app.onUnmount(() => {\n teardownDialogServiceState(state)\n registeredStates.delete(state)\n })\n },\n}\n\n// Ленивый модульный фолбэк — канонический вариант для простого SPA без плагина.\nlet moduleState: DialogServiceState | null = null\n\nfunction getModuleState(): DialogServiceState {\n if (!moduleState)\n moduleState = createDialogServiceState()\n\n return moduleState\n}\n\nlet ambiguousStateWarned = false\n\nfunction warnAmbiguousState(): void {\n if (ambiguousStateWarned) return\n if (!__GR_DEV__) return\n\n ambiguousStateWarned = true\n console.warn(\n '[granularity] useDialogService: плагин установлен в нескольких приложениях, '\n + 'а сервис запрошен вне `setup` — какое из них имелось в виду, знать неоткуда. '\n + 'Используется модульное состояние. Вызовите `useDialogService()` внутри `setup` нужного приложения.',\n )\n}\n\n/**\n * Состояние текущего вызова: app-scoped (плагин) → единственный\n * зарегистрированный инстанс → модульный фолбэк.\n */\nfunction resolveDialogServiceState(): DialogServiceState {\n // `inject` работает только в `setup` — там ответ точный.\n if (getCurrentInstance()) {\n const provided = inject(GRANULARITY_DIALOG_SERVICE_STATE, null)\n if (provided) return provided\n }\n\n if (registeredStates.size === 1)\n return registeredStates.values().next().value!\n\n if (registeredStates.size > 1)\n warnAmbiguousState()\n\n return getModuleState()\n}\n\nfunction ensureMounted(state: DialogServiceState, appContext?: AppContext | null): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return\n\n if (appContext) state.appContext = appContext\n if (state.mounted) return\n\n const container = document.createElement('div')\n container.setAttribute('data-gr-dialog-service-host', '')\n container.setAttribute('data-gr-overlay-root', '')\n // Хост уезжает в общий портал оверлеев: он должен лежать в той же ветке, что\n // и остальные слои, иначе правило `inert` пометит его вместе со страницей.\n ;(ensurePortalRoot() ?? document.body).appendChild(container)\n state.container = container\n\n // Состояние отдаём пропом: хост монтируется вне дерева и своим `inject` до\n // app-scoped состояния не дотянулся бы. `markRaw` — чтобы Vue не пытался\n // сделать реактивной саму обёртку: реактивна очередь внутри неё.\n const vnode = createVNode(GrDialogServiceHost, { state: markRaw(state) })\n vnode.appContext = state.appContext ?? null\n render(vnode, container)\n state.mounted = true\n}\n\nfunction warnIfContextless(state: DialogServiceState, context: CapturedDialogContext | null): void {\n if (state.contextlessWarned || context?.config || context?.i18n) return\n if (!__GR_DEV__) return\n\n state.contextlessWarned = true\n console.warn(\n '[granularity] useDialogService: диалог открыт без контекста приложения — '\n + 'ни `GrConfigProvider`, ни адаптер i18n не найдены, будут использованы дефолты. '\n + 'Вызовите `useDialogService()` внутри `setup` хотя бы один раз или передайте `setAppContext`.',\n )\n}\n\n/** Снимает смонтированный хост и сбрасывает захваченный контекст одного инстанса. */\nfunction teardownDialogServiceState(state: DialogServiceState): void {\n if (state.container) {\n render(null, state.container)\n state.container.remove()\n state.container = null\n }\n state.mounted = false\n // Сбрасываем и кэш контекста приложения: без этого он переживает teardown и\n // протекает в следующее приложение (в тестах — в следующий тест), из-за чего\n // диалог может подхватить чужие provides и проверка соврёт.\n state.appContext = null\n state.lastCapturedContext = null\n state.contextlessWarned = false\n state.queue.splice(0, state.queue.length)\n state.inFlight.splice(0, state.inFlight.length)\n}\n\n/**\n * Тестовая/служебная очистка смонтированного хоста. Работает над тем же\n * состоянием, что и сам сервис: app-scoped при вызове из `setup` или при\n * единственном зарегистрированном приложении, иначе — модульным.\n */\nexport function teardownDialogService(): void {\n teardownDialogServiceState(resolveDialogServiceState())\n}\n\nfunction enqueue(\n state: DialogServiceState,\n kind: DialogKind,\n message: string,\n options: DialogBaseOptions,\n onConfirm: DialogOnConfirm<any> | undefined,\n captured: CapturedDialogContext,\n): { promise: Promise<DialogResult<any>>, close: () => void } {\n // Императивный сервис клиент-only: монтирует хост в `document.body`. В SSR\n // выполнять его нельзя — очередь мутировалась бы на сервере и текла между\n // запросами. Явно запрещаем вместо тихого no-op.\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n throw new Error(\n '[granularity] useDialogService is client-only and cannot be called during SSR '\n + '(it mounts a host into document.body). Guard imperative dialog calls behind a client-side check.',\n )\n }\n\n ensureMounted(state, options.appContext ?? state.appContext)\n\n const id = makeDialogId()\n let resolveFn!: (result: DialogResult<any>) => void\n const promise = new Promise<DialogResult<any>>((resolve) => {\n resolveFn = resolve\n })\n\n // Синглтон `dialogService` создаётся на импорте модуля, вне `setup`, и своего\n // контекста не имеет в принципе. Берём последний захваченный — иначе такие\n // вызовы навсегда остаются без i18n и без `GrConfigProvider`.\n const context = captured.config || captured.i18n ? captured : state.lastCapturedContext\n warnIfContextless(state, context)\n\n const request: DialogRequest = {\n id,\n kind,\n message,\n options: { ...options, message },\n onConfirm,\n priority: options.priority ?? 0,\n nested: false,\n settled: false,\n resolve: resolveFn,\n config: context?.config ?? null,\n i18n: context?.i18n ?? null,\n }\n\n enqueueDialogRequest(state, request)\n\n // Закрытие через промис идёт тем же путём, что и кнопка в окне: заявка сама\n // знает, завершена ли она, а окно по её исчезновению свернёт всё, что под неё\n // заведено (в т.ч. оборвёт in-flight `onConfirm` через `abort()`).\n const close = (): void => {\n settleDialogRequest(state, request, { action: 'close' })\n }\n\n return { promise, close }\n}\n\nfunction withClose<T>(promise: Promise<T>, close: () => void): DialogPromise<T> {\n const result = promise as DialogPromise<T>\n result.close = close\n return result\n}\n\nfunction mergeErrorDefaults(\n defaults: DialogServiceDefaults,\n options: DialogBaseOptions,\n): DialogBaseOptions & Pick<DialogConfirmOptions, 'cancelText'> {\n return {\n size: defaults.size,\n confirmText: defaults.confirmText,\n confirmVariant: defaults.confirmVariant,\n confirmTone: defaults.confirmTone,\n cancelText: defaults.cancelText,\n buttonSize: defaults.buttonSize,\n closeOnBackdrop: defaults.closeOnBackdrop,\n closeOnEsc: defaults.closeOnEsc,\n errorParsers: defaults.errorParsers,\n errorTexts: defaults.errorTexts,\n errorMessageKey: defaults.errorMessageKey,\n fieldLabels: defaults.fieldLabels,\n ...options,\n }\n}\n\n/**\n * Создаёт сервис диалогов. Опциональные `defaults` применяются ко всем\n * вызовам (можно переопределить в каждом конкретном вызове).\n *\n * Контекст приложения кэшируется автоматически при первом вызове внутри\n * `setup`. Для вызовов вне компонента используйте `setAppContext`.\n */\nexport function useDialogService(defaults: DialogServiceDefaults = {}): DialogService {\n const state = resolveDialogServiceState()\n\n // Авто-кэш appContext из текущего компонента (если вызвано в setup).\n const instance = getCurrentInstance()\n if (instance?.appContext && !state.appContext) {\n state.appContext = instance.appContext\n }\n\n // Конфиг и i18n захватываем здесь и только здесь: `inject` работает лишь в\n // `setup`, а хост монтируется вне дерева и сам до провайдера не дотянется.\n // Храним в замыкании сервиса, а не в состоянии, — иначе два поддерева с\n // разными провайдерами делили бы конфиг первого.\n // i18n захватываем не «на всякий случай»: дочерний `GrConfirmDialog` сам зовёт\n // `useGranularityTranslations()`, но его `inject` из хоста уходит в\n // `appContext.provides` — то есть видит только установку через `app.use()`.\n // Адаптер, переданный пропом `<GrConfigProvider i18n>`, живёт в дереве, и без\n // захвата диалог откатился бы на встроенные английские строки.\n //\n // `markRaw` — потому что дальше контекст ложится в `reactive`-очередь, а она\n // разворачивает вложенные рефы (см. комментарий в `store.ts`).\n const capturedI18n = instance ? resolveGranularityI18n() : null\n const captured: CapturedDialogContext = {\n config: instance ? markRaw(useGrConfig()) : null,\n i18n: capturedI18n ? markRaw(capturedI18n) : null,\n }\n\n if (captured.config || captured.i18n)\n state.lastCapturedContext = captured\n\n function confirm(message: string, options: DialogConfirmOptions = {}): DialogPromise<boolean> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(state, 'confirm', message, merged, options.onConfirm, captured)\n return withClose(promise.then(r => r.action === 'confirm'), close)\n }\n\n function alert(message: string, options: DialogAlertOptions = {}): DialogPromise<void> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(state, 'alert', message, merged, options.onConfirm, captured)\n return withClose(promise.then(() => undefined), close)\n }\n\n function prompt(message: string, options: DialogPromptOptions = {}): DialogPromise<string | null> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(state, 'prompt', message, merged, options.onConfirm, captured)\n return withClose(\n promise.then(r => (r.action === 'confirm' ? ((r.value as string) ?? '') : null)),\n close,\n )\n }\n\n function open<V = unknown>(\n kind: DialogKind,\n message: string,\n options: DialogBaseOptions = {},\n ): DialogPromise<DialogResult<V>> {\n const merged = mergeErrorDefaults(defaults, options)\n const { promise, close } = enqueue(\n state,\n kind,\n message,\n merged,\n (options as DialogConfirmOptions).onConfirm,\n captured,\n )\n return withClose(promise as Promise<DialogResult<V>>, close)\n }\n\n // Порядок разбора — FIFO, как и порядок показа: `pop()` резолвил промисы\n // задом наперёд, и вызывающий получал результаты в обратном порядке.\n function closeAll(): void {\n for (const request of [...state.queue])\n settleDialogRequest(state, request, { action: 'close' })\n }\n\n function setAppContext(ctx: AppContext | null): void {\n state.appContext = ctx\n }\n\n return { confirm, alert, prompt, open, closeAll, setAppContext }\n}\n\n/** Готовый синглтон-сервис с дефолтными настройками. */\nexport const dialogService: DialogService = {\n confirm: (message, options) => useDialogService().confirm(message, options),\n alert: (message, options) => useDialogService().alert(message, options),\n prompt: (message, options) => useDialogService().prompt(message, options),\n open: (kind, message, options) => useDialogService().open(kind, message, options),\n closeAll: () => useDialogService().closeAll(),\n setAppContext: ctx => useDialogService().setAppContext(ctx),\n}\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceItem — одно окно императивного сервиса. Внутренний компонент,\n * в шаблонах напрямую не используется.\n *\n * Всё состояние живёт здесь, а не на хосте, ровно потому, что окон может быть\n * несколько: вложенный вызов из `onConfirm` показывается поверх родителя, и\n * общий `loading` с общей ошибкой означали бы, что верхнее окно пишет в нижнее.\n */\nimport { computed, inject, nextTick, onBeforeUnmount, onMounted, provide, ref, shallowRef } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrConfirmDialog from '../GrConfirmDialog/GrConfirmDialog.vue'\nimport GrPromptDialog from '../GrPromptDialog/GrPromptDialog.vue'\nimport {\n coreResponseErrorParsers,\n createResponseErrorClassifier,\n responseErrorParserPresets,\n} from '../GrResponseErrorBanner/parsers'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nimport { GR_CONFIG_KEY, type GrComponentDefaults, type GrConfigContext } from '../GrConfigProvider/context'\nimport { GRANULARITY_I18N_KEY } from '../../i18n/adapter'\nimport { resolveGranularityI18n, useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport { finishDialogInFlight, removeDialogRequest, settleDialogRequest, startDialogInFlight } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\nimport type { DialogCloseAction, DialogConfirmContext, DialogConfirmOptions, DialogPromptOptions } from './types'\n\nconst props = defineProps<{\n request: DialogRequest\n}>()\n\n// Состояние инстанса приходит через `provide` хоста, а не пропом: окно его\n// мутирует (очередь, список заявок в полёте), а мутировать проп — значит писать\n// в чужое владение.\nconst state = inject(GRANULARITY_DIALOG_SERVICE_STATE) as DialogServiceState\n\n// ————— Мост конфига и i18n.\n//\n// Окно рендерится через `render(vnode, container)` в `document.body`, то есть вне\n// дерева компонентов. Vue в этом случае берёт `provides` только из `appContext`,\n// куда попадает лишь `app.provide()` — значения от `<GrConfigProvider>` до сюда\n// не доходят. Поэтому вызов сервиса захватывает их в `setup` и кладёт в заявку,\n// а окно раздаёт вниз конфиг **своей** заявки.\nconst EMPTY_DEFAULTS: GrComponentDefaults = Object.freeze({})\n\nprovide<GrConfigContext>(GR_CONFIG_KEY, {\n size: computed(() => props.request.config?.size.value),\n componentDefaults: computed(() => props.request.config?.componentDefaults.value ?? EMPTY_DEFAULTS),\n})\n\n// i18n: тот же приём. Адаптер хоста (из `appContext`) — запасной вариант для\n// вызова без захваченного: без него установка через `app.use()` осталась бы\n// незамеченной.\nconst hostI18n = resolveGranularityI18n()\n\nprovide(GRANULARITY_I18N_KEY, {\n t: (key: string, params?: Record<string, unknown>) => {\n const adapter = props.request.i18n ?? hostI18n\n // Вернуть сам ключ — принятый в пакете сигнал «перевода нет»: потребитель\n // подставит свой fallback-текст (см. `useGranularityTranslations`).\n return adapter?.t(key, params) ?? key\n },\n get locale() {\n return (props.request.i18n ?? hostI18n)?.locale\n },\n})\n\n// Подвал alert-ветки рисуется здесь, поэтому и перевод кнопки достаётся здесь.\n// Адаптер берём тот же, что раздан вниз: собственный `provide` компонента его\n// же `inject` не видит, и `useGranularityTranslations()` без аргумента ушёл бы\n// мимо захваченного вызовом адаптера — в фолбэк из `appContext`.\nconst alertConfirmText = computed(() => {\n const { t } = useGranularityTranslations(props.request.i18n ?? hostI18n)\n return t('gr.dialog.ok', 'OK')\n})\n\nconst open = ref(false)\nconst loading = ref(false)\nconst currentError = shallowRef<ResponseErrorInfo | null>(null)\n/**\n * Ошибки по именам полей. Карта, а не одна строка: `setFieldError(field, …)`\n * обещает адресность, и обещание должно быть правдой ещё до того, как в\n * диалоге появится второе поле.\n */\nconst fieldErrors = ref<Record<string, string>>({})\nconst promptValue = ref(\n props.request.options && 'value' in props.request.options\n ? ((props.request.options as { value?: string }).value ?? '')\n : '',\n)\n\n/** Единственное поле `GrPromptDialog` называется `value`. */\nconst PROMPT_FIELD = 'value'\n\n// Одна запись — её и показываем: адрес не важен, когда адресат один.\nconst promptFieldError = computed<string | null>(() => {\n const own = fieldErrors.value[PROMPT_FIELD]\n if (own != null) return own\n\n const entries = Object.values(fieldErrors.value)\n return entries.length === 1 ? entries[0] : null\n})\n\nconst hasFieldError = computed(() => Object.keys(fieldErrors.value).length > 0)\n\nconst controller = new AbortController()\nlet externalAbortCleanup: (() => void) | null = null\n\nfunction buildClassifier() {\n const opts = props.request.options\n const parsers = typeof opts.errorParsers === 'function'\n ? opts.errorParsers(responseErrorParserPresets)\n : opts.errorParsers ?? coreResponseErrorParsers\n\n return createResponseErrorClassifier({\n parsers,\n texts: opts.errorTexts,\n messageKey: opts.errorMessageKey,\n })\n}\n\nonMounted(() => {\n // Внешний AbortSignal закрывает диалог как `close`.\n const sig = props.request.options.signal\n if (sig) {\n if (sig.aborted) {\n finish('close')\n return\n }\n const onAbort = () => finish('close')\n sig.addEventListener('abort', onAbort)\n externalAbortCleanup = () => sig.removeEventListener('abort', onAbort)\n }\n\n void nextTick(() => {\n open.value = true\n })\n})\n\nonBeforeUnmount(() => {\n // Снимаем всё, что заведено под заявку: подписку на внешний сигнал и свой\n // контроллер. Размонтирование — единственная точка, которую видят все три\n // пути завершения (кнопка, `close()` промиса, `closeAll()`), поэтому обрыв\n // in-flight `onConfirm` живёт именно здесь.\n externalAbortCleanup?.()\n externalAbortCleanup = null\n controller.abort()\n\n finishDialogInFlight(state, props.request)\n})\n\nfunction finish(action: DialogCloseAction, value?: unknown): void {\n // Снятие заявки из очереди откладываем на тик: окно должно успеть закрыться\n // до того, как исчезнет из разметки.\n if (!settleDialogRequest(state, props.request, { action, value }, { removeFromQueue: false }))\n return\n\n open.value = false\n void nextTick(() => removeDialogRequest(state, props.request))\n}\n\nfunction buildContext(value: unknown): DialogConfirmContext<any> & { _errorSet: () => boolean } {\n let errorSet = false\n\n const setError = (message: string | null): void => {\n if (message == null) {\n currentError.value = null\n return\n }\n errorSet = true\n currentError.value = { kind: 'unknown', message, raw: message }\n }\n\n const setFieldError = (field: string, message: string | null): void => {\n const next = { ...fieldErrors.value }\n if (message == null) {\n delete next[field]\n fieldErrors.value = next\n return\n }\n errorSet = true\n next[field] = message\n fieldErrors.value = next\n }\n\n const clearErrors = (): void => {\n currentError.value = null\n fieldErrors.value = {}\n }\n\n const setRawError = async (raw: unknown, meta?: Record<string, unknown>): Promise<ResponseErrorInfo | null> => {\n const info = await buildClassifier()(raw, meta)\n if (info.fieldErrors?.length) {\n const next = { ...fieldErrors.value }\n for (const entry of info.fieldErrors)\n next[entry.field] = entry.messages.join(' ')\n fieldErrors.value = next\n }\n currentError.value = info\n errorSet = true\n return info\n }\n\n return {\n value,\n signal: controller.signal,\n setError,\n setRawError,\n setFieldError,\n clearErrors,\n setLoading: (v: boolean) => { loading.value = v },\n close: (action: DialogCloseAction = 'close') => finish(action),\n _errorSet: () => errorSet,\n }\n}\n\nasync function handleConfirm(): Promise<void> {\n const req = props.request\n const value = req.kind === 'prompt' ? promptValue.value : undefined\n\n if (!req.onConfirm) {\n finish('confirm', value)\n return\n }\n\n currentError.value = null\n fieldErrors.value = {}\n loading.value = true\n\n const ctx = buildContext(value)\n\n // Пока колбэк в полёте, заявка считается родителем для новых вызовов: диалог,\n // открытый изнутри `onConfirm`, показывается поверх, а не встаёт в очередь за\n // тем, кто его ждёт.\n startDialogInFlight(state, req)\n\n try {\n const result = await req.onConfirm(ctx)\n // Сначала проверка «диалог ещё жив», потом запись состояния: за время\n // `await` заявку мог завершить `ctx.close()` или `closeAll()`.\n if (req.settled) return\n loading.value = false\n if (result === false) return // оставить открытым без ошибки\n if (currentError.value || hasFieldError.value || ctx._errorSet()) return // есть ошибка — не закрывать\n finish('confirm', value)\n }\n catch (error) {\n if (req.settled) return\n loading.value = false\n if (!ctx._errorSet()) await ctx.setRawError(error)\n // оставить диалог открытым с показанной ошибкой\n }\n finally {\n finishDialogInFlight(state, req)\n }\n}\n\nfunction handleCancel(): void {\n finish('cancel')\n}\n\nfunction handleModelUpdate(value: boolean): void {\n if (!value) finish('close')\n}\n\n// Опции веток читаются типизированно, без `as any` в шаблоне.\nconst promptOptions = computed<DialogPromptOptions>(() => props.request.options as DialogPromptOptions)\nconst confirmOptions = computed<DialogConfirmOptions>(() => props.request.options as DialogConfirmOptions)\n\n// Прокидываемые в дочерний диалог общие пропы.\nconst sharedProps = computed(() => {\n const o = props.request.options\n return {\n title: o.title,\n size: o.size,\n closeOnBackdrop: o.closeOnBackdrop,\n closeOnEsc: o.closeOnEsc,\n // Подавление «мягкого» закрытия на время `onConfirm` считает сам диалог по\n // `confirmLoading` — здесь только включаем режим, чтобы механизм жил в\n // одном месте, а не дублировался сервисом.\n persistent: true,\n showHeader: o.showHeader,\n showCloseButton: o.showCloseButton,\n headerConfig: o.headerConfig,\n footerConfig: o.footerConfig,\n bodyConfig: o.bodyConfig,\n closeLabel: o.closeLabel,\n buttonSize: o.buttonSize,\n confirmText: o.confirmText,\n confirmVariant: o.confirmVariant,\n confirmTone: o.confirmTone,\n }\n})\n</script>\n\n<template>\n <!-- prompt -->\n <GrPromptDialog\n v-if=\"request.kind === 'prompt'\"\n v-bind=\"sharedProps\"\n v-model:value=\"promptValue\"\n :model-value=\"open\"\n :description=\"request.options.message ?? request.options.description\"\n :label=\"promptOptions.label\"\n :placeholder=\"promptOptions.placeholder\"\n :required=\"promptOptions.required ?? false\"\n :required-error-text=\"promptOptions.requiredErrorText\"\n :cancel-text=\"promptOptions.cancelText\"\n :input-type=\"promptOptions.inputType\"\n :inputmode=\"promptOptions.inputmode\"\n :maxlength=\"promptOptions.maxlength\"\n :show-count=\"promptOptions.showCount\"\n :multiline=\"promptOptions.multiline\"\n :rows=\"promptOptions.rows\"\n :autosize=\"promptOptions.autosize\"\n :rules=\"promptOptions.rules\"\n :error=\"currentError\"\n :field-error=\"promptFieldError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n\n <!-- alert: confirm-диалог без кнопки Cancel -->\n <GrConfirmDialog\n v-else-if=\"request.kind === 'alert'\"\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n >\n <template #footer>\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton\n data-testid=\"gr-alert-confirm\"\n :variant=\"sharedProps.confirmVariant ?? 'primary'\"\n :tone=\"sharedProps.confirmTone ?? 'primary'\"\n :size=\"sharedProps.buttonSize\"\n :loading=\"loading\"\n @click=\"handleConfirm\"\n >\n {{ sharedProps.confirmText ?? alertConfirmText }}\n </GrButton>\n </div>\n </template>\n </GrConfirmDialog>\n\n <!-- confirm -->\n <GrConfirmDialog\n v-else\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :cancel-text=\"confirmOptions.cancelText\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceItem — одно окно императивного сервиса. Внутренний компонент,\n * в шаблонах напрямую не используется.\n *\n * Всё состояние живёт здесь, а не на хосте, ровно потому, что окон может быть\n * несколько: вложенный вызов из `onConfirm` показывается поверх родителя, и\n * общий `loading` с общей ошибкой означали бы, что верхнее окно пишет в нижнее.\n */\nimport { computed, inject, nextTick, onBeforeUnmount, onMounted, provide, ref, shallowRef } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrConfirmDialog from '../GrConfirmDialog/GrConfirmDialog.vue'\nimport GrPromptDialog from '../GrPromptDialog/GrPromptDialog.vue'\nimport {\n coreResponseErrorParsers,\n createResponseErrorClassifier,\n responseErrorParserPresets,\n} from '../GrResponseErrorBanner/parsers'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nimport { GR_CONFIG_KEY, type GrComponentDefaults, type GrConfigContext } from '../GrConfigProvider/context'\nimport { GRANULARITY_I18N_KEY } from '../../i18n/adapter'\nimport { resolveGranularityI18n, useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport { finishDialogInFlight, removeDialogRequest, settleDialogRequest, startDialogInFlight } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\nimport type { DialogCloseAction, DialogConfirmContext, DialogConfirmOptions, DialogPromptOptions } from './types'\n\nconst props = defineProps<{\n request: DialogRequest\n}>()\n\n// Состояние инстанса приходит через `provide` хоста, а не пропом: окно его\n// мутирует (очередь, список заявок в полёте), а мутировать проп — значит писать\n// в чужое владение.\nconst state = inject(GRANULARITY_DIALOG_SERVICE_STATE) as DialogServiceState\n\n// ————— Мост конфига и i18n.\n//\n// Окно рендерится через `render(vnode, container)` в `document.body`, то есть вне\n// дерева компонентов. Vue в этом случае берёт `provides` только из `appContext`,\n// куда попадает лишь `app.provide()` — значения от `<GrConfigProvider>` до сюда\n// не доходят. Поэтому вызов сервиса захватывает их в `setup` и кладёт в заявку,\n// а окно раздаёт вниз конфиг **своей** заявки.\nconst EMPTY_DEFAULTS: GrComponentDefaults = Object.freeze({})\n\nprovide<GrConfigContext>(GR_CONFIG_KEY, {\n size: computed(() => props.request.config?.size.value),\n componentDefaults: computed(() => props.request.config?.componentDefaults.value ?? EMPTY_DEFAULTS),\n})\n\n// i18n: тот же приём. Адаптер хоста (из `appContext`) — запасной вариант для\n// вызова без захваченного: без него установка через `app.use()` осталась бы\n// незамеченной.\nconst hostI18n = resolveGranularityI18n()\n\nprovide(GRANULARITY_I18N_KEY, {\n t: (key: string, params?: Record<string, unknown>) => {\n const adapter = props.request.i18n ?? hostI18n\n // Вернуть сам ключ — принятый в пакете сигнал «перевода нет»: потребитель\n // подставит свой fallback-текст (см. `useGranularityTranslations`).\n return adapter?.t(key, params) ?? key\n },\n get locale() {\n return (props.request.i18n ?? hostI18n)?.locale\n },\n})\n\n// Подвал alert-ветки рисуется здесь, поэтому и перевод кнопки достаётся здесь.\n// Адаптер берём тот же, что раздан вниз: собственный `provide` компонента его\n// же `inject` не видит, и `useGranularityTranslations()` без аргумента ушёл бы\n// мимо захваченного вызовом адаптера — в фолбэк из `appContext`.\nconst alertConfirmText = computed(() => {\n const { t } = useGranularityTranslations(props.request.i18n ?? hostI18n)\n return t('gr.dialog.ok', 'OK')\n})\n\nconst open = ref(false)\nconst loading = ref(false)\nconst currentError = shallowRef<ResponseErrorInfo | null>(null)\n/**\n * Ошибки по именам полей. Карта, а не одна строка: `setFieldError(field, …)`\n * обещает адресность, и обещание должно быть правдой ещё до того, как в\n * диалоге появится второе поле.\n */\nconst fieldErrors = ref<Record<string, string>>({})\nconst promptValue = ref(\n props.request.options && 'value' in props.request.options\n ? ((props.request.options as { value?: string }).value ?? '')\n : '',\n)\n\n/** Единственное поле `GrPromptDialog` называется `value`. */\nconst PROMPT_FIELD = 'value'\n\n// Одна запись — её и показываем: адрес не важен, когда адресат один.\nconst promptFieldError = computed<string | null>(() => {\n const own = fieldErrors.value[PROMPT_FIELD]\n if (own != null) return own\n\n const entries = Object.values(fieldErrors.value)\n return entries.length === 1 ? entries[0] : null\n})\n\nconst hasFieldError = computed(() => Object.keys(fieldErrors.value).length > 0)\n\nconst controller = new AbortController()\nlet externalAbortCleanup: (() => void) | null = null\n\nfunction buildClassifier() {\n const opts = props.request.options\n const parsers = typeof opts.errorParsers === 'function'\n ? opts.errorParsers(responseErrorParserPresets)\n : opts.errorParsers ?? coreResponseErrorParsers\n\n return createResponseErrorClassifier({\n parsers,\n texts: opts.errorTexts,\n messageKey: opts.errorMessageKey,\n })\n}\n\nonMounted(() => {\n // Внешний AbortSignal закрывает диалог как `close`.\n const sig = props.request.options.signal\n if (sig) {\n if (sig.aborted) {\n finish('close')\n return\n }\n const onAbort = () => finish('close')\n sig.addEventListener('abort', onAbort)\n externalAbortCleanup = () => sig.removeEventListener('abort', onAbort)\n }\n\n void nextTick(() => {\n open.value = true\n })\n})\n\nonBeforeUnmount(() => {\n // Снимаем всё, что заведено под заявку: подписку на внешний сигнал и свой\n // контроллер. Размонтирование — единственная точка, которую видят все три\n // пути завершения (кнопка, `close()` промиса, `closeAll()`), поэтому обрыв\n // in-flight `onConfirm` живёт именно здесь.\n externalAbortCleanup?.()\n externalAbortCleanup = null\n controller.abort()\n\n finishDialogInFlight(state, props.request)\n})\n\nfunction finish(action: DialogCloseAction, value?: unknown): void {\n // Снятие заявки из очереди откладываем на тик: окно должно успеть закрыться\n // до того, как исчезнет из разметки.\n if (!settleDialogRequest(state, props.request, { action, value }, { removeFromQueue: false }))\n return\n\n open.value = false\n void nextTick(() => removeDialogRequest(state, props.request))\n}\n\nfunction buildContext(value: unknown): DialogConfirmContext<any> & { _errorSet: () => boolean } {\n let errorSet = false\n\n const setError = (message: string | null): void => {\n if (message == null) {\n currentError.value = null\n return\n }\n errorSet = true\n currentError.value = { kind: 'unknown', message, raw: message }\n }\n\n const setFieldError = (field: string, message: string | null): void => {\n const next = { ...fieldErrors.value }\n if (message == null) {\n delete next[field]\n fieldErrors.value = next\n return\n }\n errorSet = true\n next[field] = message\n fieldErrors.value = next\n }\n\n const clearErrors = (): void => {\n currentError.value = null\n fieldErrors.value = {}\n }\n\n const setRawError = async (raw: unknown, meta?: Record<string, unknown>): Promise<ResponseErrorInfo | null> => {\n const info = await buildClassifier()(raw, meta)\n if (info.fieldErrors?.length) {\n const next = { ...fieldErrors.value }\n for (const entry of info.fieldErrors)\n next[entry.field] = entry.messages.join(' ')\n fieldErrors.value = next\n }\n currentError.value = info\n errorSet = true\n return info\n }\n\n return {\n value,\n signal: controller.signal,\n setError,\n setRawError,\n setFieldError,\n clearErrors,\n setLoading: (v: boolean) => { loading.value = v },\n close: (action: DialogCloseAction = 'close') => finish(action),\n _errorSet: () => errorSet,\n }\n}\n\nasync function handleConfirm(): Promise<void> {\n const req = props.request\n const value = req.kind === 'prompt' ? promptValue.value : undefined\n\n if (!req.onConfirm) {\n finish('confirm', value)\n return\n }\n\n currentError.value = null\n fieldErrors.value = {}\n loading.value = true\n\n const ctx = buildContext(value)\n\n // Пока колбэк в полёте, заявка считается родителем для новых вызовов: диалог,\n // открытый изнутри `onConfirm`, показывается поверх, а не встаёт в очередь за\n // тем, кто его ждёт.\n startDialogInFlight(state, req)\n\n try {\n const result = await req.onConfirm(ctx)\n // Сначала проверка «диалог ещё жив», потом запись состояния: за время\n // `await` заявку мог завершить `ctx.close()` или `closeAll()`.\n if (req.settled) return\n loading.value = false\n if (result === false) return // оставить открытым без ошибки\n if (currentError.value || hasFieldError.value || ctx._errorSet()) return // есть ошибка — не закрывать\n finish('confirm', value)\n }\n catch (error) {\n if (req.settled) return\n loading.value = false\n if (!ctx._errorSet()) await ctx.setRawError(error)\n // оставить диалог открытым с показанной ошибкой\n }\n finally {\n finishDialogInFlight(state, req)\n }\n}\n\nfunction handleCancel(): void {\n finish('cancel')\n}\n\nfunction handleModelUpdate(value: boolean): void {\n if (!value) finish('close')\n}\n\n// Опции веток читаются типизированно, без `as any` в шаблоне.\nconst promptOptions = computed<DialogPromptOptions>(() => props.request.options as DialogPromptOptions)\nconst confirmOptions = computed<DialogConfirmOptions>(() => props.request.options as DialogConfirmOptions)\n\n// Прокидываемые в дочерний диалог общие пропы.\nconst sharedProps = computed(() => {\n const o = props.request.options\n return {\n title: o.title,\n size: o.size,\n closeOnBackdrop: o.closeOnBackdrop,\n closeOnEsc: o.closeOnEsc,\n // Подавление «мягкого» закрытия на время `onConfirm` считает сам диалог по\n // `confirmLoading` — здесь только включаем режим, чтобы механизм жил в\n // одном месте, а не дублировался сервисом.\n persistent: true,\n showHeader: o.showHeader,\n showCloseButton: o.showCloseButton,\n headerConfig: o.headerConfig,\n footerConfig: o.footerConfig,\n bodyConfig: o.bodyConfig,\n closeLabel: o.closeLabel,\n buttonSize: o.buttonSize,\n confirmText: o.confirmText,\n confirmVariant: o.confirmVariant,\n confirmTone: o.confirmTone,\n }\n})\n</script>\n\n<template>\n <!-- prompt -->\n <GrPromptDialog\n v-if=\"request.kind === 'prompt'\"\n v-bind=\"sharedProps\"\n v-model:value=\"promptValue\"\n :model-value=\"open\"\n :description=\"request.options.message ?? request.options.description\"\n :label=\"promptOptions.label\"\n :placeholder=\"promptOptions.placeholder\"\n :required=\"promptOptions.required ?? false\"\n :required-error-text=\"promptOptions.requiredErrorText\"\n :cancel-text=\"promptOptions.cancelText\"\n :input-type=\"promptOptions.inputType\"\n :inputmode=\"promptOptions.inputmode\"\n :maxlength=\"promptOptions.maxlength\"\n :show-count=\"promptOptions.showCount\"\n :multiline=\"promptOptions.multiline\"\n :rows=\"promptOptions.rows\"\n :autosize=\"promptOptions.autosize\"\n :rules=\"promptOptions.rules\"\n :error=\"currentError\"\n :field-error=\"promptFieldError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n\n <!-- alert: confirm-диалог без кнопки Cancel -->\n <GrConfirmDialog\n v-else-if=\"request.kind === 'alert'\"\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n >\n <template #footer>\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton\n data-testid=\"gr-alert-confirm\"\n :variant=\"sharedProps.confirmVariant ?? 'primary'\"\n :tone=\"sharedProps.confirmTone ?? 'primary'\"\n :size=\"sharedProps.buttonSize\"\n :loading=\"loading\"\n @click=\"handleConfirm\"\n >\n {{ sharedProps.confirmText ?? alertConfirmText }}\n </GrButton>\n </div>\n </template>\n </GrConfirmDialog>\n\n <!-- confirm -->\n <GrConfirmDialog\n v-else\n :model-value=\"open\"\n v-bind=\"sharedProps\"\n :description=\"request.options.message ?? request.options.description\"\n :cancel-text=\"confirmOptions.cancelText\"\n :error=\"currentError\"\n :confirm-loading=\"loading\"\n :close-on-confirm=\"false\"\n @update:model-value=\"handleModelUpdate\"\n @confirm=\"handleConfirm\"\n @cancel=\"handleCancel\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceHost — внутренний компонент-оркестратор императивного\n * `useDialogService`. Не предназначен для прямого использования в шаблонах.\n *\n * Держит раскладку очереди и ничего больше: состояние каждой заявки — загрузка,\n * ошибки, введённое значение, мост конфига и i18n — живёт в\n * `GrDialogServiceItem`, потому что окон может быть несколько.\n *\n * Видимы голова очереди и её вложенные окна (`store.visibleDialogRequests`):\n * обычные вызовы по-прежнему показываются по одному, а диалог, открытый из\n * `onConfirm` другого диалога, ложится поверх родителя — иначе он ждал бы в\n * очереди того, кто ждёт его.\n */\nimport { computed, inject, provide } from 'vue'\n\nimport GrDialogServiceItem from './GrDialogServiceItem.vue'\nimport { visibleDialogRequests } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\n\nexport interface GrDialogServiceHostProps {\n /**\n * Состояние инстанса сервиса. Приходит пропом при программном монтировании:\n * хост живёт вне дерева и своим `inject` до app-scoped состояния не дотянулся\n * бы. `inject` остаётся для случая, когда хост поставили в шаблон руками.\n */\n state?: DialogServiceState | null\n}\n\nconst props = defineProps<GrDialogServiceHostProps>()\n\nconst injected = inject(GRANULARITY_DIALOG_SERVICE_STATE, null)\n\nconst activeState = computed<DialogServiceState | null>(() => props.state ?? injected)\n\nconst visible = computed<DialogRequest[]>(() =>\n activeState.value ? visibleDialogRequests(activeState.value) : [],\n)\n\n// Окна берут состояние отсюда, а не пропом: они его мутируют (очередь, список\n// заявок в полёте), а мутировать проп — значит писать в чужое владение.\nprovide(GRANULARITY_DIALOG_SERVICE_STATE, activeState.value!)\n</script>\n\n<template>\n <GrDialogServiceItem\n v-for=\"request in visible\"\n :key=\"request.id\"\n :request=\"request\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrDialogServiceHost — внутренний компонент-оркестратор императивного\n * `useDialogService`. Не предназначен для прямого использования в шаблонах.\n *\n * Держит раскладку очереди и ничего больше: состояние каждой заявки — загрузка,\n * ошибки, введённое значение, мост конфига и i18n — живёт в\n * `GrDialogServiceItem`, потому что окон может быть несколько.\n *\n * Видимы голова очереди и её вложенные окна (`store.visibleDialogRequests`):\n * обычные вызовы по-прежнему показываются по одному, а диалог, открытый из\n * `onConfirm` другого диалога, ложится поверх родителя — иначе он ждал бы в\n * очереди того, кто ждёт его.\n */\nimport { computed, inject, provide } from 'vue'\n\nimport GrDialogServiceItem from './GrDialogServiceItem.vue'\nimport { visibleDialogRequests } from './store'\nimport type { DialogRequest, DialogServiceState } from './store'\nimport { GRANULARITY_DIALOG_SERVICE_STATE } from './useDialogService'\n\nexport interface GrDialogServiceHostProps {\n /**\n * Состояние инстанса сервиса. Приходит пропом при программном монтировании:\n * хост живёт вне дерева и своим `inject` до app-scoped состояния не дотянулся\n * бы. `inject` остаётся для случая, когда хост поставили в шаблон руками.\n */\n state?: DialogServiceState | null\n}\n\nconst props = defineProps<GrDialogServiceHostProps>()\n\nconst injected = inject(GRANULARITY_DIALOG_SERVICE_STATE, null)\n\nconst activeState = computed<DialogServiceState | null>(() => props.state ?? injected)\n\nconst visible = computed<DialogRequest[]>(() =>\n activeState.value ? visibleDialogRequests(activeState.value) : [],\n)\n\n// Окна берут состояние отсюда, а не пропом: они его мутируют (очередь, список\n// заявок в полёте), а мутировать проп — значит писать в чужое владение.\nprovide(GRANULARITY_DIALOG_SERVICE_STATE, activeState.value!)\n</script>\n\n<template>\n <GrDialogServiceItem\n v-for=\"request in visible\"\n :key=\"request.id\"\n :request=\"request\"\n />\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as useDialogService, i as teardownDialogService, n as dialogService, r as granularityDialogServicePlugin, t as GrDialogServiceHost_default } from "./chunks/GrDialogService-
|
|
1
|
+
import { a as useDialogService, i as teardownDialogService, n as dialogService, r as granularityDialogServicePlugin, t as GrDialogServiceHost_default } from "./chunks/GrDialogService-6s2cQ-_6.js";
|
|
2
2
|
import { t as grDialogServiceConfig } from "../../chunks/config-CjXjhphs.js";
|
|
3
3
|
export { GrDialogServiceHost_default as GrDialogServiceHost, dialogService, grDialogServiceConfig, granularityDialogServicePlugin, teardownDialogService, useDialogService };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as colsClass, o as columnBaseClass, s as columnsBaseClass, t as alignClass } from "../../../chunks/grDropdownMenuStyles-Cf8OdR8S.js";
|
|
2
|
-
import { n as GrDropdownMenuEntries_default, t as GrDropdownMenuList_default } from "./GrDropdownMenuList-
|
|
2
|
+
import { n as GrDropdownMenuEntries_default, t as GrDropdownMenuList_default } from "./GrDropdownMenuList-B33ZzwfB.js";
|
|
3
3
|
import { t as GrDropdown_default } from "../../GrDropdown/chunks/GrDropdown-13Wt9eB_.js";
|
|
4
4
|
import { createBlock, createElementBlock, createVNode, defineComponent, guardReactiveProps, normalizeClass, normalizeProps, openBlock, renderSlot, unref, withCtx } from "vue";
|
|
5
5
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrDropdownMenu-Cg7qHV9J.js","names":[],"sources":["../../../../src/components/GrDropdownMenu/GrDropdownMenu.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenu.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumn.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumn.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumns.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumns.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport GrDropdown from '../GrDropdown/GrDropdown.vue'\n// Типы размещения и ширины — из владельца API, а не переобъявлением: расхождение\n// обнаружилось бы только рантаймом.\nimport type { GrDropdownTrigger } from '../GrDropdown/GrDropdown.vue'\nimport type { GrDropdownWidth } from '../GrDropdown/grDropdownStyles'\nimport type { UseFloatingPlacement } from '../../composables/useFloating'\n\nimport GrDropdownMenuEntries from './GrDropdownMenuEntries.vue'\nimport GrDropdownMenuList from './GrDropdownMenuList.vue'\nimport type { GrDropdownMenuAction, GrDropdownMenuEntry } from './menuModel'\n\nexport interface GrDropdownMenuProps {\n /** Размещение панели относительно триггера; переворот при нехватке места остаётся. */\n placement?: UseFloatingPlacement\n /** Зазор между триггером и панелью, px. */\n offset?: number\n /** Ширина панели: число — пиксели, строка — CSS-длина, `auto` — по контенту. */\n width?: GrDropdownWidth\n /** Чем открывается панель. В любом режиме работают клик и клавиатура. */\n trigger?: GrDropdownTrigger\n /** Задержка открытия по наведению, мс. */\n openDelay?: number\n /** Задержка закрытия после ухода курсора, мс. */\n closeDelay?: number\n /** Меню не открывается ничем; триггер остаётся фокусируемым. */\n disabled?: boolean\n /** Куда телепортировать панель (`body` по умолчанию). */\n /** Точечное переопределение точки монтирования; по умолчанию — общий портал. */\n teleportTo?: string | HTMLElement\n /** Закрывать по клику внутри content. */\n closeOnContentClick?: boolean\n /** Дополнительные классы content-контейнера. */\n contentClass?: string\n /**\n * Декларативное меню: пункты, группы и разделители массивом. Слот по\n * умолчанию сильнее — он для меню, которое из модели не собирается.\n */\n items?: GrDropdownMenuEntry[]\n\n // list wrapper\n /** Разделители между пунктами. */\n dividers?: boolean\n /** Верхний бордер контейнера списка. */\n borderTop?: boolean\n /** Нижний бордер контейнера списка. */\n borderBottom?: boolean\n /** Дополнительные классы для wrapper'а списка. */\n listClass?: string\n /**\n * Контролируемое состояние панели (`v-model:open`) — прокидывается в\n * `GrDropdown`. Без пропа меню ведёт себя само (uncontrolled).\n */\n open?: boolean\n}\n\nexport interface GrDropdownMenuEmits {\n /** Выбран пункт декларативного меню. */\n (e: 'select', item: GrDropdownMenuAction): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n}\n\nwithDefaults(defineProps<GrDropdownMenuProps>(), {\n placement: 'bottom-end',\n offset: 8,\n width: '12rem',\n trigger: 'click',\n openDelay: 120,\n closeDelay: 160,\n disabled: false,\n teleportTo: undefined,\n closeOnContentClick: true,\n // Поле панели принадлежит панели. Гасить его отсюда своим `p-0` нельзя: оба\n // класса попадают в один атрибут с равной специфичностью, и победителя\n // выбирает порядок правил в сгенерированном CSS, а не разметка. Пункт вписан\n // в это поле собственным радиусом — см. `itemBaseClass`.\n contentClass: '',\n items: undefined,\n dividers: false,\n borderTop: false,\n borderBottom: false,\n listClass: '',\n open: undefined,\n})\n\nconst emit = defineEmits<GrDropdownMenuEmits>()\n\ndefineSlots<{\n /** Пункты меню. Слот-пропы прокидываются от `GrDropdown` как есть. */\n default?: (props: { close: () => void }) => any\n /**\n * Триггер панели. `triggerProps` обязаны попасть на сам интерактивный\n * элемент, а не на обёртку вокруг него: `aria-expanded` и `aria-controls`\n * читаются с того узла, который получает фокус.\n */\n trigger?: (props: {\n open: boolean\n toggle: () => void\n close: () => void\n triggerProps: Record<string, unknown>\n }) => any\n}>()\n\n</script>\n\n<template>\n <GrDropdown\n data-gr-dropdown-menu\n :placement=\"placement\"\n :offset=\"offset\"\n :width=\"width\"\n :trigger=\"trigger\"\n :open-delay=\"openDelay\"\n :close-delay=\"closeDelay\"\n :disabled=\"disabled\"\n :teleport-to=\"teleportTo\"\n :close-on-content-click=\"closeOnContentClick\"\n :content-class=\"contentClass\"\n :open=\"open\"\n @update:open=\"emit('update:open', $event)\"\n >\n <template #trigger=\"slotProps\">\n <slot name=\"trigger\" v-bind=\"slotProps\" />\n </template>\n\n <template #content=\"slotProps\">\n <GrDropdownMenuList\n :dividers=\"dividers\"\n :border-top=\"borderTop\"\n :border-bottom=\"borderBottom\"\n :class=\"listClass\"\n >\n <slot v-bind=\"slotProps\">\n <GrDropdownMenuEntries :items=\"items\" @select=\"emit('select', $event)\" />\n </slot>\n </GrDropdownMenuList>\n </template>\n </GrDropdown>\n</template>\n","<script setup lang=\"ts\">\nimport GrDropdown from '../GrDropdown/GrDropdown.vue'\n// Типы размещения и ширины — из владельца API, а не переобъявлением: расхождение\n// обнаружилось бы только рантаймом.\nimport type { GrDropdownTrigger } from '../GrDropdown/GrDropdown.vue'\nimport type { GrDropdownWidth } from '../GrDropdown/grDropdownStyles'\nimport type { UseFloatingPlacement } from '../../composables/useFloating'\n\nimport GrDropdownMenuEntries from './GrDropdownMenuEntries.vue'\nimport GrDropdownMenuList from './GrDropdownMenuList.vue'\nimport type { GrDropdownMenuAction, GrDropdownMenuEntry } from './menuModel'\n\nexport interface GrDropdownMenuProps {\n /** Размещение панели относительно триггера; переворот при нехватке места остаётся. */\n placement?: UseFloatingPlacement\n /** Зазор между триггером и панелью, px. */\n offset?: number\n /** Ширина панели: число — пиксели, строка — CSS-длина, `auto` — по контенту. */\n width?: GrDropdownWidth\n /** Чем открывается панель. В любом режиме работают клик и клавиатура. */\n trigger?: GrDropdownTrigger\n /** Задержка открытия по наведению, мс. */\n openDelay?: number\n /** Задержка закрытия после ухода курсора, мс. */\n closeDelay?: number\n /** Меню не открывается ничем; триггер остаётся фокусируемым. */\n disabled?: boolean\n /** Куда телепортировать панель (`body` по умолчанию). */\n /** Точечное переопределение точки монтирования; по умолчанию — общий портал. */\n teleportTo?: string | HTMLElement\n /** Закрывать по клику внутри content. */\n closeOnContentClick?: boolean\n /** Дополнительные классы content-контейнера. */\n contentClass?: string\n /**\n * Декларативное меню: пункты, группы и разделители массивом. Слот по\n * умолчанию сильнее — он для меню, которое из модели не собирается.\n */\n items?: GrDropdownMenuEntry[]\n\n // list wrapper\n /** Разделители между пунктами. */\n dividers?: boolean\n /** Верхний бордер контейнера списка. */\n borderTop?: boolean\n /** Нижний бордер контейнера списка. */\n borderBottom?: boolean\n /** Дополнительные классы для wrapper'а списка. */\n listClass?: string\n /**\n * Контролируемое состояние панели (`v-model:open`) — прокидывается в\n * `GrDropdown`. Без пропа меню ведёт себя само (uncontrolled).\n */\n open?: boolean\n}\n\nexport interface GrDropdownMenuEmits {\n /** Выбран пункт декларативного меню. */\n (e: 'select', item: GrDropdownMenuAction): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n}\n\nwithDefaults(defineProps<GrDropdownMenuProps>(), {\n placement: 'bottom-end',\n offset: 8,\n width: '12rem',\n trigger: 'click',\n openDelay: 120,\n closeDelay: 160,\n disabled: false,\n teleportTo: undefined,\n closeOnContentClick: true,\n // Поле панели принадлежит панели. Гасить его отсюда своим `p-0` нельзя: оба\n // класса попадают в один атрибут с равной специфичностью, и победителя\n // выбирает порядок правил в сгенерированном CSS, а не разметка. Пункт вписан\n // в это поле собственным радиусом — см. `itemBaseClass`.\n contentClass: '',\n items: undefined,\n dividers: false,\n borderTop: false,\n borderBottom: false,\n listClass: '',\n open: undefined,\n})\n\nconst emit = defineEmits<GrDropdownMenuEmits>()\n\ndefineSlots<{\n /** Пункты меню. Слот-пропы прокидываются от `GrDropdown` как есть. */\n default?: (props: { close: () => void }) => any\n /**\n * Триггер панели. `triggerProps` обязаны попасть на сам интерактивный\n * элемент, а не на обёртку вокруг него: `aria-expanded` и `aria-controls`\n * читаются с того узла, который получает фокус.\n */\n trigger?: (props: {\n open: boolean\n toggle: () => void\n close: () => void\n triggerProps: Record<string, unknown>\n }) => any\n}>()\n\n</script>\n\n<template>\n <GrDropdown\n data-gr-dropdown-menu\n :placement=\"placement\"\n :offset=\"offset\"\n :width=\"width\"\n :trigger=\"trigger\"\n :open-delay=\"openDelay\"\n :close-delay=\"closeDelay\"\n :disabled=\"disabled\"\n :teleport-to=\"teleportTo\"\n :close-on-content-click=\"closeOnContentClick\"\n :content-class=\"contentClass\"\n :open=\"open\"\n @update:open=\"emit('update:open', $event)\"\n >\n <template #trigger=\"slotProps\">\n <slot name=\"trigger\" v-bind=\"slotProps\" />\n </template>\n\n <template #content=\"slotProps\">\n <GrDropdownMenuList\n :dividers=\"dividers\"\n :border-top=\"borderTop\"\n :border-bottom=\"borderBottom\"\n :class=\"listClass\"\n >\n <slot v-bind=\"slotProps\">\n <GrDropdownMenuEntries :items=\"items\" @select=\"emit('select', $event)\" />\n </slot>\n </GrDropdownMenuList>\n </template>\n </GrDropdown>\n</template>\n","<script setup lang=\"ts\">\nimport { alignClass, columnBaseClass, type GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport type { GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnProps {\n align?: GrDropdownMenuColumnAlign\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnProps>(), {\n align: 'center',\n})\n\ndefineSlots<{\n /** Содержимое колонки меню. */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-column\n role=\"none\"\n :class=\"[columnBaseClass, alignClass[align]]\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { alignClass, columnBaseClass, type GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport type { GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnProps {\n align?: GrDropdownMenuColumnAlign\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnProps>(), {\n align: 'center',\n})\n\ndefineSlots<{\n /** Содержимое колонки меню. */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-column\n role=\"none\"\n :class=\"[columnBaseClass, alignClass[align]]\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { colsClass, columnsBaseClass, type GrDropdownMenuColumnsCount } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnsProps {\n cols?: GrDropdownMenuColumnsCount\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnsProps>(), {\n cols: 2,\n})\n\ndefineSlots<{\n /** Колонки меню (`GrDropdownMenuColumn`). */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-columns\n role=\"none\"\n :class=\"[columnsBaseClass, colsClass[cols]]\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { colsClass, columnsBaseClass, type GrDropdownMenuColumnsCount } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnsProps {\n cols?: GrDropdownMenuColumnsCount\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnsProps>(), {\n cols: 2,\n})\n\ndefineSlots<{\n /** Колонки меню (`GrDropdownMenuColumn`). */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-columns\n role=\"none\"\n :class=\"[columnsBaseClass, colsClass[cols]]\"\n >\n <slot />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsFA,MAAM,OAAO;;uBAqBX,YA+Ba,oBAAA;IA9BX,yBAAA;IACC,WAAW,QAAA;IACX,QAAQ,QAAA;IACR,OAAO,QAAA;IACP,SAAS,QAAA;IACT,cAAY,QAAA;IACZ,eAAa,QAAA;IACb,UAAU,QAAA;IACV,eAAa,QAAA;IACb,0BAAwB,QAAA;IACxB,iBAAe,QAAA;IACf,MAAM,QAAA;IACN,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,eAAgB,MAAM;;IAE7B,SAAO,SAAE,cAAS,CAC3B,WAA0C,KAAA,QAAA,WAAA,eAAA,mBAAb,SAAS,CAAA,CAAA,CAAA,CAAA;IAG7B,SAAO,SAAE,cAAS,CAC3B,YASqB,4BAAA;KARlB,UAAU,QAAA;KACV,cAAY,QAAA;KACZ,iBAAe,QAAA;KACf,OAAK,eAAE,QAAA,SAAS;;4BAIV,CAFP,WAEO,KAAA,QAAA,WAAA,eAAA,mBAFO,SAAS,CAAA,SAEhB,CADL,YAAyE,+BAAA;MAAjD,OAAO,QAAA;MAAQ,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,UAAW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBEjH5E,mBAMM,OAAA;IALJ,gCAAA;IACA,MAAK;IACJ,OAAK,eAAA,CAAG,MAAA,eAAA,GAAiB,MAAA,UAAA,CAAU,CAAC,QAAA,MAAK,CAAA;OAE1C,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA;;;;;;;;;;;uBEPV,mBAMM,OAAA;IALJ,iCAAA;IACA,MAAK;IACJ,OAAK,eAAA,CAAG,MAAA,gBAAA,GAAkB,MAAA,SAAA,CAAS,CAAC,QAAA,KAAI,CAAA;OAEzC,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"GrDropdownMenu-GOzuAXsi.js","names":[],"sources":["../../../../src/components/GrDropdownMenu/GrDropdownMenu.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenu.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumn.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumn.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumns.vue","../../../../src/components/GrDropdownMenu/GrDropdownMenuColumns.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport GrDropdown from '../GrDropdown/GrDropdown.vue'\n// Типы размещения и ширины — из владельца API, а не переобъявлением: расхождение\n// обнаружилось бы только рантаймом.\nimport type { GrDropdownTrigger } from '../GrDropdown/GrDropdown.vue'\nimport type { GrDropdownWidth } from '../GrDropdown/grDropdownStyles'\nimport type { UseFloatingPlacement } from '../../composables/useFloating'\n\nimport GrDropdownMenuEntries from './GrDropdownMenuEntries.vue'\nimport GrDropdownMenuList from './GrDropdownMenuList.vue'\nimport type { GrDropdownMenuAction, GrDropdownMenuEntry } from './menuModel'\n\nexport interface GrDropdownMenuProps {\n /** Размещение панели относительно триггера; переворот при нехватке места остаётся. */\n placement?: UseFloatingPlacement\n /** Зазор между триггером и панелью, px. */\n offset?: number\n /** Ширина панели: число — пиксели, строка — CSS-длина, `auto` — по контенту. */\n width?: GrDropdownWidth\n /** Чем открывается панель. В любом режиме работают клик и клавиатура. */\n trigger?: GrDropdownTrigger\n /** Задержка открытия по наведению, мс. */\n openDelay?: number\n /** Задержка закрытия после ухода курсора, мс. */\n closeDelay?: number\n /** Меню не открывается ничем; триггер остаётся фокусируемым. */\n disabled?: boolean\n /** Куда телепортировать панель (`body` по умолчанию). */\n /** Точечное переопределение точки монтирования; по умолчанию — общий портал. */\n teleportTo?: string | HTMLElement\n /** Закрывать по клику внутри content. */\n closeOnContentClick?: boolean\n /** Дополнительные классы content-контейнера. */\n contentClass?: string\n /**\n * Декларативное меню: пункты, группы и разделители массивом. Слот по\n * умолчанию сильнее — он для меню, которое из модели не собирается.\n */\n items?: GrDropdownMenuEntry[]\n\n // list wrapper\n /** Разделители между пунктами. */\n dividers?: boolean\n /** Верхний бордер контейнера списка. */\n borderTop?: boolean\n /** Нижний бордер контейнера списка. */\n borderBottom?: boolean\n /** Дополнительные классы для wrapper'а списка. */\n listClass?: string\n /**\n * Контролируемое состояние панели (`v-model:open`) — прокидывается в\n * `GrDropdown`. Без пропа меню ведёт себя само (uncontrolled).\n */\n open?: boolean\n}\n\nexport interface GrDropdownMenuEmits {\n /** Выбран пункт декларативного меню. */\n (e: 'select', item: GrDropdownMenuAction): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n}\n\nwithDefaults(defineProps<GrDropdownMenuProps>(), {\n placement: 'bottom-end',\n offset: 8,\n width: '12rem',\n trigger: 'click',\n openDelay: 120,\n closeDelay: 160,\n disabled: false,\n teleportTo: undefined,\n closeOnContentClick: true,\n // Поле панели принадлежит панели. Гасить его отсюда своим `p-0` нельзя: оба\n // класса попадают в один атрибут с равной специфичностью, и победителя\n // выбирает порядок правил в сгенерированном CSS, а не разметка. Пункт вписан\n // в это поле собственным радиусом — см. `itemBaseClass`.\n contentClass: '',\n items: undefined,\n dividers: false,\n borderTop: false,\n borderBottom: false,\n listClass: '',\n open: undefined,\n})\n\nconst emit = defineEmits<GrDropdownMenuEmits>()\n\ndefineSlots<{\n /** Пункты меню. Слот-пропы прокидываются от `GrDropdown` как есть. */\n default?: (props: { close: () => void }) => any\n /**\n * Триггер панели. `triggerProps` обязаны попасть на сам интерактивный\n * элемент, а не на обёртку вокруг него: `aria-expanded` и `aria-controls`\n * читаются с того узла, который получает фокус.\n */\n trigger?: (props: {\n open: boolean\n toggle: () => void\n close: () => void\n triggerProps: Record<string, unknown>\n }) => any\n}>()\n\n</script>\n\n<template>\n <GrDropdown\n data-gr-dropdown-menu\n :placement=\"placement\"\n :offset=\"offset\"\n :width=\"width\"\n :trigger=\"trigger\"\n :open-delay=\"openDelay\"\n :close-delay=\"closeDelay\"\n :disabled=\"disabled\"\n :teleport-to=\"teleportTo\"\n :close-on-content-click=\"closeOnContentClick\"\n :content-class=\"contentClass\"\n :open=\"open\"\n @update:open=\"emit('update:open', $event)\"\n >\n <template #trigger=\"slotProps\">\n <slot name=\"trigger\" v-bind=\"slotProps\" />\n </template>\n\n <template #content=\"slotProps\">\n <GrDropdownMenuList\n :dividers=\"dividers\"\n :border-top=\"borderTop\"\n :border-bottom=\"borderBottom\"\n :class=\"listClass\"\n >\n <slot v-bind=\"slotProps\">\n <GrDropdownMenuEntries :items=\"items\" @select=\"emit('select', $event)\" />\n </slot>\n </GrDropdownMenuList>\n </template>\n </GrDropdown>\n</template>\n","<script setup lang=\"ts\">\nimport GrDropdown from '../GrDropdown/GrDropdown.vue'\n// Типы размещения и ширины — из владельца API, а не переобъявлением: расхождение\n// обнаружилось бы только рантаймом.\nimport type { GrDropdownTrigger } from '../GrDropdown/GrDropdown.vue'\nimport type { GrDropdownWidth } from '../GrDropdown/grDropdownStyles'\nimport type { UseFloatingPlacement } from '../../composables/useFloating'\n\nimport GrDropdownMenuEntries from './GrDropdownMenuEntries.vue'\nimport GrDropdownMenuList from './GrDropdownMenuList.vue'\nimport type { GrDropdownMenuAction, GrDropdownMenuEntry } from './menuModel'\n\nexport interface GrDropdownMenuProps {\n /** Размещение панели относительно триггера; переворот при нехватке места остаётся. */\n placement?: UseFloatingPlacement\n /** Зазор между триггером и панелью, px. */\n offset?: number\n /** Ширина панели: число — пиксели, строка — CSS-длина, `auto` — по контенту. */\n width?: GrDropdownWidth\n /** Чем открывается панель. В любом режиме работают клик и клавиатура. */\n trigger?: GrDropdownTrigger\n /** Задержка открытия по наведению, мс. */\n openDelay?: number\n /** Задержка закрытия после ухода курсора, мс. */\n closeDelay?: number\n /** Меню не открывается ничем; триггер остаётся фокусируемым. */\n disabled?: boolean\n /** Куда телепортировать панель (`body` по умолчанию). */\n /** Точечное переопределение точки монтирования; по умолчанию — общий портал. */\n teleportTo?: string | HTMLElement\n /** Закрывать по клику внутри content. */\n closeOnContentClick?: boolean\n /** Дополнительные классы content-контейнера. */\n contentClass?: string\n /**\n * Декларативное меню: пункты, группы и разделители массивом. Слот по\n * умолчанию сильнее — он для меню, которое из модели не собирается.\n */\n items?: GrDropdownMenuEntry[]\n\n // list wrapper\n /** Разделители между пунктами. */\n dividers?: boolean\n /** Верхний бордер контейнера списка. */\n borderTop?: boolean\n /** Нижний бордер контейнера списка. */\n borderBottom?: boolean\n /** Дополнительные классы для wrapper'а списка. */\n listClass?: string\n /**\n * Контролируемое состояние панели (`v-model:open`) — прокидывается в\n * `GrDropdown`. Без пропа меню ведёт себя само (uncontrolled).\n */\n open?: boolean\n}\n\nexport interface GrDropdownMenuEmits {\n /** Выбран пункт декларативного меню. */\n (e: 'select', item: GrDropdownMenuAction): void\n /** Панель открылась/закрылась (`v-model:open`). */\n (e: 'update:open', value: boolean): void\n}\n\nwithDefaults(defineProps<GrDropdownMenuProps>(), {\n placement: 'bottom-end',\n offset: 8,\n width: '12rem',\n trigger: 'click',\n openDelay: 120,\n closeDelay: 160,\n disabled: false,\n teleportTo: undefined,\n closeOnContentClick: true,\n // Поле панели принадлежит панели. Гасить его отсюда своим `p-0` нельзя: оба\n // класса попадают в один атрибут с равной специфичностью, и победителя\n // выбирает порядок правил в сгенерированном CSS, а не разметка. Пункт вписан\n // в это поле собственным радиусом — см. `itemBaseClass`.\n contentClass: '',\n items: undefined,\n dividers: false,\n borderTop: false,\n borderBottom: false,\n listClass: '',\n open: undefined,\n})\n\nconst emit = defineEmits<GrDropdownMenuEmits>()\n\ndefineSlots<{\n /** Пункты меню. Слот-пропы прокидываются от `GrDropdown` как есть. */\n default?: (props: { close: () => void }) => any\n /**\n * Триггер панели. `triggerProps` обязаны попасть на сам интерактивный\n * элемент, а не на обёртку вокруг него: `aria-expanded` и `aria-controls`\n * читаются с того узла, который получает фокус.\n */\n trigger?: (props: {\n open: boolean\n toggle: () => void\n close: () => void\n triggerProps: Record<string, unknown>\n }) => any\n}>()\n\n</script>\n\n<template>\n <GrDropdown\n data-gr-dropdown-menu\n :placement=\"placement\"\n :offset=\"offset\"\n :width=\"width\"\n :trigger=\"trigger\"\n :open-delay=\"openDelay\"\n :close-delay=\"closeDelay\"\n :disabled=\"disabled\"\n :teleport-to=\"teleportTo\"\n :close-on-content-click=\"closeOnContentClick\"\n :content-class=\"contentClass\"\n :open=\"open\"\n @update:open=\"emit('update:open', $event)\"\n >\n <template #trigger=\"slotProps\">\n <slot name=\"trigger\" v-bind=\"slotProps\" />\n </template>\n\n <template #content=\"slotProps\">\n <GrDropdownMenuList\n :dividers=\"dividers\"\n :border-top=\"borderTop\"\n :border-bottom=\"borderBottom\"\n :class=\"listClass\"\n >\n <slot v-bind=\"slotProps\">\n <GrDropdownMenuEntries :items=\"items\" @select=\"emit('select', $event)\" />\n </slot>\n </GrDropdownMenuList>\n </template>\n </GrDropdown>\n</template>\n","<script setup lang=\"ts\">\nimport { alignClass, columnBaseClass, type GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport type { GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnProps {\n align?: GrDropdownMenuColumnAlign\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnProps>(), {\n align: 'center',\n})\n\ndefineSlots<{\n /** Содержимое колонки меню. */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-column\n role=\"none\"\n :class=\"[columnBaseClass, alignClass[align]]\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { alignClass, columnBaseClass, type GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport type { GrDropdownMenuColumnAlign } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnProps {\n align?: GrDropdownMenuColumnAlign\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnProps>(), {\n align: 'center',\n})\n\ndefineSlots<{\n /** Содержимое колонки меню. */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-column\n role=\"none\"\n :class=\"[columnBaseClass, alignClass[align]]\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { colsClass, columnsBaseClass, type GrDropdownMenuColumnsCount } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnsProps {\n cols?: GrDropdownMenuColumnsCount\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnsProps>(), {\n cols: 2,\n})\n\ndefineSlots<{\n /** Колонки меню (`GrDropdownMenuColumn`). */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-columns\n role=\"none\"\n :class=\"[columnsBaseClass, colsClass[cols]]\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { colsClass, columnsBaseClass, type GrDropdownMenuColumnsCount } from './grDropdownMenuStyles'\n\nexport interface GrDropdownMenuColumnsProps {\n cols?: GrDropdownMenuColumnsCount\n}\n\nwithDefaults(defineProps<GrDropdownMenuColumnsProps>(), {\n cols: 2,\n})\n\ndefineSlots<{\n /** Колонки меню (`GrDropdownMenuColumn`). */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n data-gr-dropdown-menu-columns\n role=\"none\"\n :class=\"[columnsBaseClass, colsClass[cols]]\"\n >\n <slot />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsFA,MAAM,OAAO;;uBAqBX,YA+Ba,oBAAA;IA9BX,yBAAA;IACC,WAAW,QAAA;IACX,QAAQ,QAAA;IACR,OAAO,QAAA;IACP,SAAS,QAAA;IACT,cAAY,QAAA;IACZ,eAAa,QAAA;IACb,UAAU,QAAA;IACV,eAAa,QAAA;IACb,0BAAwB,QAAA;IACxB,iBAAe,QAAA;IACf,MAAM,QAAA;IACN,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,eAAgB,MAAM;;IAE7B,SAAO,SAAE,cAAS,CAC3B,WAA0C,KAAA,QAAA,WAAA,eAAA,mBAAb,SAAS,CAAA,CAAA,CAAA,CAAA;IAG7B,SAAO,SAAE,cAAS,CAC3B,YASqB,4BAAA;KARlB,UAAU,QAAA;KACV,cAAY,QAAA;KACZ,iBAAe,QAAA;KACf,OAAK,eAAE,QAAA,SAAS;;4BAIV,CAFP,WAEO,KAAA,QAAA,WAAA,eAAA,mBAFO,SAAS,CAAA,SAEhB,CADL,YAAyE,+BAAA;MAAjD,OAAO,QAAA;MAAQ,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,UAAW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBEjH5E,mBAMM,OAAA;IALJ,gCAAA;IACA,MAAK;IACJ,OAAK,eAAA,CAAG,MAAA,eAAA,GAAiB,MAAA,UAAA,CAAU,CAAC,QAAA,MAAK,CAAA;OAE1C,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA;;;;;;;;;;;uBEPV,mBAMM,OAAA;IALJ,iCAAA;IACA,MAAK;IACJ,OAAK,eAAA,CAAG,MAAA,gBAAA,GAAkB,MAAA,SAAA,CAAS,CAAC,QAAA,KAAI,CAAA;OAEzC,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA"}
|
|
@@ -250,7 +250,9 @@ var GrDropdownMenuEntries_default = /* @__PURE__ */ defineComponent({
|
|
|
250
250
|
role: item.role,
|
|
251
251
|
checked: item.checked,
|
|
252
252
|
icon: item.icon,
|
|
253
|
-
shortcut: item.shortcut
|
|
253
|
+
shortcut: item.shortcut,
|
|
254
|
+
as: item.as,
|
|
255
|
+
align: item.align
|
|
254
256
|
};
|
|
255
257
|
}
|
|
256
258
|
function onSelect(item) {
|
|
@@ -264,7 +266,10 @@ var GrDropdownMenuEntries_default = /* @__PURE__ */ defineComponent({
|
|
|
264
266
|
inset: entry.inset
|
|
265
267
|
}, null, 8, ["inset"])) : unref(isMenuSection)(entry) ? (openBlock(), createBlock(GrDropdownMenuGroup_default, {
|
|
266
268
|
key: `group-${entry.title ?? index}`,
|
|
267
|
-
title: entry.title
|
|
269
|
+
title: entry.title,
|
|
270
|
+
"title-align": entry.titleAlign,
|
|
271
|
+
dividers: entry.dividers,
|
|
272
|
+
uppercase: entry.uppercase
|
|
268
273
|
}, {
|
|
269
274
|
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(entry.items, (item) => {
|
|
270
275
|
return openBlock(), createBlock(GrDropdownMenuItem_default, mergeProps({ key: item.key }, { ref_for: true }, itemProps(item), { onClick: ($event) => onSelect(item) }), {
|
|
@@ -273,7 +278,12 @@ var GrDropdownMenuEntries_default = /* @__PURE__ */ defineComponent({
|
|
|
273
278
|
}, 1040, ["onClick"]);
|
|
274
279
|
}), 128))]),
|
|
275
280
|
_: 2
|
|
276
|
-
}, 1032, [
|
|
281
|
+
}, 1032, [
|
|
282
|
+
"title",
|
|
283
|
+
"title-align",
|
|
284
|
+
"dividers",
|
|
285
|
+
"uppercase"
|
|
286
|
+
])) : unref(isMenuAction)(entry) ? (openBlock(), createBlock(GrDropdownMenuItem_default, mergeProps({ key: entry.key }, { ref_for: true }, itemProps(entry), { onClick: ($event) => onSelect(entry) }), {
|
|
277
287
|
default: withCtx(() => [createTextVNode(toDisplayString(entry.label), 1)]),
|
|
278
288
|
_: 2
|
|
279
289
|
}, 1040, ["onClick"])) : createCommentVNode("", true)], 64);
|