@feugene/granularity 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{config-vYMJ3Cr1.js → config-CYthTVK2.js} +2 -2
- package/dist/chunks/config-CYthTVK2.js.map +1 -0
- package/dist/chunks/{config-CPSxM7XE.js → config-CsCUuukB.js} +1 -0
- package/dist/chunks/config-CsCUuukB.js.map +1 -0
- package/dist/chunks/{config-BTwYMQaz2.js → config-DBXCIb6r2.js} +4 -3
- package/dist/chunks/config-DBXCIb6r2.js.map +1 -0
- package/dist/chunks/{config-uzUuosv7.js → config-DGGaKf4R.js} +6 -5
- package/dist/chunks/config-DGGaKf4R.js.map +1 -0
- package/dist/chunks/{config-DnF472Ox.js → config-DOYz_SVK.js} +2 -2
- package/dist/chunks/{config-DnF472Ox.js.map → config-DOYz_SVK.js.map} +1 -1
- package/dist/chunks/{config-T3-DMXYm.js → config-DUNH02Fg.js} +2 -2
- package/dist/chunks/config-DUNH02Fg.js.map +1 -0
- package/dist/chunks/{generated-CSyPsWtM.js → generated-Buw6W7Te.js} +7 -0
- package/dist/chunks/{generated-CSyPsWtM.js.map → generated-Buw6W7Te.js.map} +1 -1
- package/dist/chunks/grDropdownStyles-DLToHWMX.js +5 -0
- package/dist/chunks/grDropdownStyles-DLToHWMX.js.map +1 -0
- package/dist/chunks/grPopoverStyles-DKwigt7b.js +91 -0
- package/dist/chunks/grPopoverStyles-DKwigt7b.js.map +1 -0
- package/dist/chunks/{grSelectStyles-Cdtd10eY.js → grSelectStyles-D9EQ-pgK.js} +2 -1
- package/dist/chunks/grSelectStyles-D9EQ-pgK.js.map +1 -0
- package/dist/chunks/{granular-provider-CrQua8PT.js → granular-provider-3LUawDPh.js} +6 -6
- package/dist/chunks/{granular-provider-CrQua8PT.js.map → granular-provider-3LUawDPh.js.map} +1 -1
- package/dist/chunks/{overlayTransition-0Fj5tQbq.js → overlayPanelSurface-BKeMhRx4.js} +24 -1
- package/dist/chunks/overlayPanelSurface-BKeMhRx4.js.map +1 -0
- package/dist/chunks/{focusables-BkzK42SQ.js → useFocusTrap-D9bIPNF6.js} +123 -1
- package/dist/chunks/useFocusTrap-D9bIPNF6.js.map +1 -0
- package/dist/chunks/{useMenuItemsFocus-B6TldmGf.js → useMenuItemsFocus-B7Bl_aFA.js} +1 -1
- package/dist/chunks/{useMenuItemsFocus-B6TldmGf.js.map → useMenuItemsFocus-B7Bl_aFA.js.map} +1 -1
- package/dist/chunks/{useModalOverlay-D__CqLGn.js → useModalOverlay-BnAGZpcI.js} +1 -1
- package/dist/chunks/{useModalOverlay-D__CqLGn.js.map → useModalOverlay-BnAGZpcI.js.map} +1 -1
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-CoQjEF8h.js → GrAutocomplete-BQsSE7sj.js} +2 -2
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-CoQjEF8h.js.map → GrAutocomplete-BQsSE7sj.js.map} +1 -1
- package/dist/components/GrAutocomplete/index.js +2 -2
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-p2XH_8cE.js → GrColorPicker-D1Iis7jf.js} +2 -1
- package/dist/components/GrColorPicker/chunks/GrColorPicker-D1Iis7jf.js.map +1 -0
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BXbh3q7O.js → GrCommandPalette-BZHUKlux.js} +1 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BXbh3q7O.js.map → GrCommandPalette-BZHUKlux.js.map} +1 -1
- package/dist/components/GrCommandPalette/index.js +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-CUP43q__.js → GrConfirmDialog-jRDs_E6C.js} +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-CUP43q__.js.map → GrConfirmDialog-jRDs_E6C.js.map} +1 -1
- package/dist/components/GrConfirmDialog/index.js +1 -1
- package/dist/components/GrContextMenu/chunks/{GrContextMenu-ClkVjdcP.js → GrContextMenu-CWmgrzpi.js} +17 -3
- package/dist/components/GrContextMenu/chunks/GrContextMenu-CWmgrzpi.js.map +1 -0
- package/dist/components/GrContextMenu/index.js +2 -2
- package/dist/components/GrDialog/chunks/{GrDialog-BiMJ9_T4.js → GrDialog-C01Hhk6G.js} +1 -1
- package/dist/components/GrDialog/chunks/{GrDialog-BiMJ9_T4.js.map → GrDialog-C01Hhk6G.js.map} +1 -1
- package/dist/components/GrDialog/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-BxNK6crD.js → GrDialogService-CmsJNUmM.js} +7 -2
- package/dist/components/GrDialogService/chunks/{GrDialogService-BxNK6crD.js.map → GrDialogService-CmsJNUmM.js.map} +1 -1
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-Ce2l3zaG.js → GrDrawer-xfoy-MCj.js} +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-Ce2l3zaG.js.map → GrDrawer-xfoy-MCj.js.map} +1 -1
- package/dist/components/GrDrawer/index.js +1 -1
- package/dist/components/GrDropdown/chunks/GrDropdown-CDkPkvIJ.js +180 -0
- package/dist/components/GrDropdown/chunks/GrDropdown-CDkPkvIJ.js.map +1 -0
- package/dist/components/GrDropdown/index.js +2 -2
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-GOzuAXsi.js → GrDropdownMenu-DQrZ_yPV.js} +1 -1
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-GOzuAXsi.js.map → GrDropdownMenu-DQrZ_yPV.js.map} +1 -1
- package/dist/components/GrDropdownMenu/index.js +1 -1
- package/dist/components/GrFileUpload/chunks/{GrFileUpload-NsHkRRIa.js → GrFileUpload-LL8RZvC0.js} +10 -0
- package/dist/components/GrFileUpload/chunks/GrFileUpload-LL8RZvC0.js.map +1 -0
- package/dist/components/GrFileUpload/index.js +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-CG4vUBg_.js → GrImageViewer--BU8yrfx.js} +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-CG4vUBg_.js.map → GrImageViewer--BU8yrfx.js.map} +1 -1
- package/dist/components/GrImageViewer/index.js +1 -1
- package/dist/components/GrModal/chunks/{GrModal-D0mf1SAx.js → GrModal-_JrDhon-.js} +1 -1
- package/dist/components/GrModal/chunks/{GrModal-D0mf1SAx.js.map → GrModal-_JrDhon-.js.map} +1 -1
- package/dist/components/GrModal/index.js +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-FiiXzVyv.js → GrPagination-gBoWVHk6.js} +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-FiiXzVyv.js.map → GrPagination-gBoWVHk6.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrPopover/chunks/{GrPopover-faJanmGs.js → GrPopover-CzeXMcvh.js} +87 -13
- package/dist/components/GrPopover/chunks/GrPopover-CzeXMcvh.js.map +1 -0
- package/dist/components/GrPopover/index.js +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-C6QGC7QJ.js → GrPromptDialog-CailXSqW.js} +1 -1
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-C6QGC7QJ.js.map → GrPromptDialog-CailXSqW.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-Dt8iqruc.js → GrSelect-CZqV1Eg4.js} +2 -2
- package/dist/components/GrSelect/chunks/{GrSelect-Dt8iqruc.js.map → GrSelect-CZqV1Eg4.js.map} +1 -1
- package/dist/components/GrSelect/index.js +2 -2
- package/dist/components/GrTabs/chunks/{GrTabs-ugDnkk-2.js → GrTabs-UsO4j8zv.js} +5 -0
- package/dist/components/GrTabs/chunks/GrTabs-UsO4j8zv.js.map +1 -0
- package/dist/components/GrTabs/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-BHwgGJcT.js → GrTreeSelect-DOXcpWq2.js} +2 -2
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-BHwgGJcT.js.map → GrTreeSelect-DOXcpWq2.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +2 -2
- package/dist/composables/useFocusTrap.js +1 -123
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/index.js +26 -26
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrAutocomplete/grAutocompleteStyles.d.ts +1 -1
- package/dist/types/components/GrDropdown/GrDropdown.vue.d.ts +5 -5
- package/dist/types/components/GrDropdown/grDropdownStyles.d.ts +8 -2
- package/dist/types/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +2 -2
- package/dist/types/components/GrPopover/GrPopover.vue.d.ts +54 -4
- package/dist/types/components/GrPopover/grPopoverStyles.d.ts +19 -11
- package/dist/types/components/GrSelect/grSelectStyles.d.ts +1 -1
- package/dist/types/components/GrTreeSelect/grTreeSelectStyles.d.ts +1 -1
- package/dist/types/components/shared/overlayOrigin.d.ts +22 -0
- package/dist/types/components/shared/overlayPanelSurface.d.ts +21 -0
- package/dist/web-types.json +40 -4
- package/package.json +1 -1
- package/dist/chunks/config-BTwYMQaz2.js.map +0 -1
- package/dist/chunks/config-CPSxM7XE.js.map +0 -1
- package/dist/chunks/config-T3-DMXYm.js.map +0 -1
- package/dist/chunks/config-uzUuosv7.js.map +0 -1
- package/dist/chunks/config-vYMJ3Cr1.js.map +0 -1
- package/dist/chunks/focusables-BkzK42SQ.js.map +0 -1
- package/dist/chunks/grDropdownStyles-V7TRiCXn.js +0 -24
- package/dist/chunks/grDropdownStyles-V7TRiCXn.js.map +0 -1
- package/dist/chunks/grPopoverStyles-Djh5RNhn.js +0 -59
- package/dist/chunks/grPopoverStyles-Djh5RNhn.js.map +0 -1
- package/dist/chunks/grSelectStyles-Cdtd10eY.js.map +0 -1
- package/dist/chunks/overlayTransition-0Fj5tQbq.js.map +0 -1
- package/dist/components/GrColorPicker/chunks/GrColorPicker-p2XH_8cE.js.map +0 -1
- package/dist/components/GrContextMenu/chunks/GrContextMenu-ClkVjdcP.js.map +0 -1
- package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js +0 -239
- package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js.map +0 -1
- package/dist/components/GrFileUpload/chunks/GrFileUpload-NsHkRRIa.js.map +0 -1
- package/dist/components/GrPopover/chunks/GrPopover-faJanmGs.js.map +0 -1
- package/dist/components/GrTabs/chunks/GrTabs-ugDnkk-2.js.map +0 -1
- package/dist/composables/useFocusTrap.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grDropdownStyles-DLToHWMX.js","names":[],"sources":["../../src/components/GrDropdown/grDropdownStyles.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { overlayOriginClass } from '../shared/overlayOrigin'\n\n/** Ширина панели: число — пиксели, строка — CSS-длина, `auto` — по контенту. */\nexport type GrDropdownWidth = number | string\n\nexport function grDropdownOriginClass(placement: Placement): string {\n return overlayOriginClass(placement)\n}\n\n/**\n * Поле вокруг пунктов меню.\n *\n * Поверхности здесь больше нет: панель рисует `GrPopover`, на котором меню и\n * стоит. Осталось только поле — пункты не должны прилегать к рамке вплотную, —\n * и классы потребителя.\n */\nexport const dropdownContentBaseClass = 'p-1'\n\nexport function grDropdownContentClass(contentClass?: string): string {\n return [\n dropdownContentBaseClass,\n contentClass,\n ].filter(Boolean).join(' ')\n}\n"],"mappings":"AAmBA,SAAgB,uBAAuB,cAA+B;CACpE,OAAO,CAAA,OAEL,YACF,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AAC5B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { t as overlayPanelSurfaceClass } from "./overlayPanelSurface-BKeMhRx4.js";
|
|
2
|
+
//#region src/components/shared/overlayOrigin.ts
|
|
3
|
+
/**
|
|
4
|
+
* `transform-origin` для scale-анимации всплывающей панели.
|
|
5
|
+
*
|
|
6
|
+
* Ключ — `resolvedPlacement`, то есть положение **после** `flip`: если снизу не
|
|
7
|
+
* хватило места и панель перевернуло вверх, origin переворачивается вместе с
|
|
8
|
+
* ней. Координаты панели к этому моменту уже посчитал `useFloating`, здесь
|
|
9
|
+
* остаётся только направление роста относительно триггера.
|
|
10
|
+
*
|
|
11
|
+
* До выноса карта существовала копиями в `GrPopover` и `GrDropdown` — двенадцать
|
|
12
|
+
* записей, совпадавших дословно, вместе с комментарием об одном и том же.
|
|
13
|
+
*
|
|
14
|
+
* Отдельным модулем от поверхности намеренно: списки выбора берут поверхность,
|
|
15
|
+
* но своей анимации роста не имеют, и импорт соседа затащил бы им в чанк восемь
|
|
16
|
+
* классов, которыми они не пользуются. Гейт `src/__tests__/safelist.test.ts`
|
|
17
|
+
* назвал бы их поимённо — и был бы прав.
|
|
18
|
+
*
|
|
19
|
+
* Модуль безадресный: в `dist` он уезжает в общий чанк, который пресет не
|
|
20
|
+
* сканирует, поэтому каждый потребитель объявляет эти классы в своём `safelist.ts`.
|
|
21
|
+
*/
|
|
22
|
+
var overlayOriginClassByPlacement = {
|
|
23
|
+
"bottom-start": "origin-top-left",
|
|
24
|
+
"bottom-end": "origin-top-right",
|
|
25
|
+
"bottom": "origin-top",
|
|
26
|
+
"top-start": "origin-bottom-left",
|
|
27
|
+
"top-end": "origin-bottom-right",
|
|
28
|
+
"top": "origin-bottom",
|
|
29
|
+
"left-start": "origin-top-right",
|
|
30
|
+
"left-end": "origin-bottom-right",
|
|
31
|
+
"left": "origin-right",
|
|
32
|
+
"right-start": "origin-top-left",
|
|
33
|
+
"right-end": "origin-bottom-left",
|
|
34
|
+
"right": "origin-left"
|
|
35
|
+
};
|
|
36
|
+
function overlayOriginClass(placement) {
|
|
37
|
+
return overlayOriginClassByPlacement[placement] ?? "origin-top";
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/components/GrPopover/grPopoverStyles.ts
|
|
41
|
+
var panelSizes = {
|
|
42
|
+
xs: "p-2 text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]",
|
|
43
|
+
sm: "p-2.5 text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]",
|
|
44
|
+
md: "p-3 text-[length:var(--gr-control-text-md)] leading-[var(--gr-leading-sm)]",
|
|
45
|
+
lg: "p-4 text-[length:var(--gr-control-text-lg)] leading-[var(--gr-leading-base)]"
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Поверхность панели.
|
|
49
|
+
*
|
|
50
|
+
* Ширина — `min()` из двух пределов, и они разной природы. Первый, `22rem`, —
|
|
51
|
+
* потолок содержимого: читаемая ширина колонки текста. Он **дефолт**, а не
|
|
52
|
+
* закон, и снимается хуком `--gr-popover-max-width` — примитив объявил, что
|
|
53
|
+
* содержимое панели дело потребителя, и навязывать ему форму не вправе.
|
|
54
|
+
* Тулбар, палитра и сетка шире прозы законно.
|
|
55
|
+
*
|
|
56
|
+
* Второй, `calc(100vw-1rem)`, — инвариант слоя: `useFloating` смещает панель в
|
|
57
|
+
* пределах вьюпорта, но не сужает её. Он не настраивается ничем и снаружи не
|
|
58
|
+
* отключается, потому что стоит вторым операндом `min()`: даже
|
|
59
|
+
* `--gr-popover-max-width: 100vw` его не снимает, а лишь уступает ему место.
|
|
60
|
+
*
|
|
61
|
+
* Хуком, а не пропом, — по трём причинам. Спор специфичности не возникает
|
|
62
|
+
* вовсе: кастомное свойство разрешается каскадом на самом элементе, а не
|
|
63
|
+
* порядком правил в сгенерированном CSS (та же ловушка, что описана у
|
|
64
|
+
* `panelSizesFlush`). Потолок можно менять по брейкпоинту и по теме — пропом
|
|
65
|
+
* этого не сделать. И словарь компонента не растёт: значение остаётся
|
|
66
|
+
* значением. Так же устроена высота списка у `GrCommandPalette`.
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Кегль без поля.
|
|
70
|
+
*
|
|
71
|
+
* Отдельная мапа, а не `contentClass="p-0"`: поле и кегль приезжают одной
|
|
72
|
+
* строкой равной специфичности, и кто победит — решал бы порядок правил в
|
|
73
|
+
* сгенерированном CSS. Меню внутри поповера с `p-3` — визуально не меню.
|
|
74
|
+
*/
|
|
75
|
+
var panelSizesFlush = {
|
|
76
|
+
xs: "text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]",
|
|
77
|
+
sm: "text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]",
|
|
78
|
+
md: "text-[length:var(--gr-control-text-md)] leading-[var(--gr-leading-sm)]",
|
|
79
|
+
lg: "text-[length:var(--gr-control-text-lg)] leading-[var(--gr-leading-base)]"
|
|
80
|
+
};
|
|
81
|
+
var popoverPanelBaseClass = `${overlayPanelSurfaceClass} max-w-[min(var(--gr-popover-max-width,22rem),calc(100vw-1rem))] focus:outline-none`;
|
|
82
|
+
function grPopoverPanelClass(size, placement, extra, padding = "default") {
|
|
83
|
+
return [
|
|
84
|
+
popoverPanelBaseClass,
|
|
85
|
+
padding === "none" ? panelSizesFlush[size] : panelSizes[size],
|
|
86
|
+
overlayOriginClass(placement),
|
|
87
|
+
extra ?? ""
|
|
88
|
+
].filter(Boolean).join(" ");
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { overlayOriginClassByPlacement as a, popoverPanelBaseClass as i, panelSizes as n, panelSizesFlush as r, grPopoverPanelClass as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grPopoverStyles-DKwigt7b.js","names":[],"sources":["../../src/components/shared/overlayOrigin.ts","../../src/components/GrPopover/grPopoverStyles.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\n\n/**\n * `transform-origin` для scale-анимации всплывающей панели.\n *\n * Ключ — `resolvedPlacement`, то есть положение **после** `flip`: если снизу не\n * хватило места и панель перевернуло вверх, origin переворачивается вместе с\n * ней. Координаты панели к этому моменту уже посчитал `useFloating`, здесь\n * остаётся только направление роста относительно триггера.\n *\n * До выноса карта существовала копиями в `GrPopover` и `GrDropdown` — двенадцать\n * записей, совпадавших дословно, вместе с комментарием об одном и том же.\n *\n * Отдельным модулем от поверхности намеренно: списки выбора берут поверхность,\n * но своей анимации роста не имеют, и импорт соседа затащил бы им в чанк восемь\n * классов, которыми они не пользуются. Гейт `src/__tests__/safelist.test.ts`\n * назвал бы их поимённо — и был бы прав.\n *\n * Модуль безадресный: в `dist` он уезжает в общий чанк, который пресет не\n * сканирует, поэтому каждый потребитель объявляет эти классы в своём `safelist.ts`.\n */\nexport const overlayOriginClassByPlacement: Record<Placement, string> = {\n 'bottom-start': 'origin-top-left',\n 'bottom-end': 'origin-top-right',\n 'bottom': 'origin-top',\n 'top-start': 'origin-bottom-left',\n 'top-end': 'origin-bottom-right',\n 'top': 'origin-bottom',\n 'left-start': 'origin-top-right',\n 'left-end': 'origin-bottom-right',\n 'left': 'origin-right',\n 'right-start': 'origin-top-left',\n 'right-end': 'origin-bottom-left',\n 'right': 'origin-left',\n}\n\nexport function overlayOriginClass(placement: Placement): string {\n return overlayOriginClassByPlacement[placement] ?? 'origin-top'\n}\n","import type { Placement } from '@floating-ui/dom'\n\nimport { overlayOriginClass } from '../shared/overlayOrigin'\nimport { overlayPanelSurfaceClass } from '../shared/overlayPanelSurface'\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type GrPopoverSize = GrComponentSize\n\n/**\n * Роль панели.\n *\n * `dialog` — произвольный интерактивный контент (значение по умолчанию).\n * Остальные нужны компонентам, которые строятся поверх этого примитива:\n * меню (`GrMenu`, `GrContextMenu`), списки, палитры. Роль задаёт потребитель,\n * потому что клавиатурный паттерн принадлежит ему, а не поповеру.\n */\nexport type GrPopoverRole = 'dialog' | 'menu' | 'listbox' | 'grid' | 'group' | 'none'\n\n/** Поле панели. `none` — поле рисует содержимое (меню, список опций). */\nexport type GrPopoverPadding = 'default' | 'none'\n\nexport const panelSizes: Record<GrPopoverSize, string> = {\n xs: 'p-2 text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]',\n sm: 'p-2.5 text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]',\n md: 'p-3 text-[length:var(--gr-control-text-md)] leading-[var(--gr-leading-sm)]',\n lg: 'p-4 text-[length:var(--gr-control-text-lg)] leading-[var(--gr-leading-base)]',\n}\n\n/**\n * Поверхность панели.\n *\n * Ширина — `min()` из двух пределов, и они разной природы. Первый, `22rem`, —\n * потолок содержимого: читаемая ширина колонки текста. Он **дефолт**, а не\n * закон, и снимается хуком `--gr-popover-max-width` — примитив объявил, что\n * содержимое панели дело потребителя, и навязывать ему форму не вправе.\n * Тулбар, палитра и сетка шире прозы законно.\n *\n * Второй, `calc(100vw-1rem)`, — инвариант слоя: `useFloating` смещает панель в\n * пределах вьюпорта, но не сужает её. Он не настраивается ничем и снаружи не\n * отключается, потому что стоит вторым операндом `min()`: даже\n * `--gr-popover-max-width: 100vw` его не снимает, а лишь уступает ему место.\n *\n * Хуком, а не пропом, — по трём причинам. Спор специфичности не возникает\n * вовсе: кастомное свойство разрешается каскадом на самом элементе, а не\n * порядком правил в сгенерированном CSS (та же ловушка, что описана у\n * `panelSizesFlush`). Потолок можно менять по брейкпоинту и по теме — пропом\n * этого не сделать. И словарь компонента не растёт: значение остаётся\n * значением. Так же устроена высота списка у `GrCommandPalette`.\n */\n/**\n * Кегль без поля.\n *\n * Отдельная мапа, а не `contentClass=\"p-0\"`: поле и кегль приезжают одной\n * строкой равной специфичности, и кто победит — решал бы порядок правил в\n * сгенерированном CSS. Меню внутри поповера с `p-3` — визуально не меню.\n */\nexport const panelSizesFlush: Record<GrPopoverSize, string> = {\n xs: 'text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]',\n sm: 'text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]',\n md: 'text-[length:var(--gr-control-text-md)] leading-[var(--gr-leading-sm)]',\n lg: 'text-[length:var(--gr-control-text-lg)] leading-[var(--gr-leading-base)]',\n}\n\nexport const popoverPanelBaseClass = `${overlayPanelSurfaceClass} max-w-[min(var(--gr-popover-max-width,22rem),calc(100vw-1rem))] focus:outline-none`\n\nexport function grPopoverPanelClass(\n size: GrPopoverSize,\n placement: Placement,\n extra?: string,\n padding: GrPopoverPadding = 'default',\n): string {\n return [\n popoverPanelBaseClass,\n padding === 'none' ? panelSizesFlush[size] : panelSizes[size],\n overlayOriginClass(placement),\n extra ?? '',\n ].filter(Boolean).join(' ')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,gCAA2D;CACtE,gBAAgB;CAChB,cAAc;CACd,UAAU;CACV,aAAa;CACb,WAAW;CACX,OAAO;CACP,cAAc;CACd,YAAY;CACZ,QAAQ;CACR,eAAe;CACf,aAAa;CACb,SAAS;AACX;AAEA,SAAgB,mBAAmB,WAA8B;CAC/D,OAAO,8BAA8B,cAAc;AACrD;;;ACjBA,IAAa,aAA4C;CACvD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,IAAa,kBAAiD;CAC5D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,wBAAwB,GAAG,yBAAyB;AAEjE,SAAgB,oBACd,MACA,WACA,OACA,UAA4B,WACpB;CACR,OAAO;EACL;EACA,YAAY,SAAS,gBAAgB,QAAQ,WAAW;EACxD,mBAAmB,SAAS;EAC5B,SAAS;CACX,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;AAC5B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as overlayPanelSurfaceClass } from "./overlayPanelSurface-BKeMhRx4.js";
|
|
1
2
|
//#region src/components/GrSelect/grSelectStyles.ts
|
|
2
3
|
var defaultBaseClass = "w-full rounded-[var(--gr-radius-control)] border bg-[var(--gr-bg)] text-[var(--gr-fg)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]";
|
|
3
4
|
/**
|
|
@@ -125,7 +126,7 @@ function grSelectTriggerClass(options) {
|
|
|
125
126
|
if (options.view === "link") return [grSelectClass(options), "inline-flex items-center gap-1 text-left"].join(" ");
|
|
126
127
|
return [grSelectClass(options), "flex items-center justify-between text-left"].join(" ");
|
|
127
128
|
}
|
|
128
|
-
var grSelectPanelClasses =
|
|
129
|
+
var grSelectPanelClasses = `${overlayPanelSurfaceClass} overflow-hidden`;
|
|
129
130
|
/** Общая подсветка наведения и активной опции панели. */
|
|
130
131
|
var selectOptionHighlight = "bg-[color-mix(in_srgb,var(--gr-muted)_30%,transparent)]";
|
|
131
132
|
var selectOptionBaseClass = "rounded-[var(--gr-radius-md)] px-3 py-2 text-left text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)]";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grSelectStyles-D9EQ-pgK.js","names":[],"sources":["../../src/components/GrSelect/grSelectStyles.ts"],"sourcesContent":["import type { GrComponentSize, GrControlState } from '../shared/sizes'\nimport { overlayPanelSurfaceClass } from '../shared/overlayPanelSurface'\n\nexport type GrSelectView = 'default' | 'link'\nexport type GrSelectSize = GrComponentSize\nexport type GrSelectVariant = 'primary' | 'default' | 'muted' | 'danger'\nexport type GrSelectUnderline = 'auto' | 'always' | 'none'\nimport type { GrChipTone } from '../GrChip/grChipStyles'\n\nexport type GrSelectOptionsView = 'native' | 'panel'\n/**\n * Тип значения опции. Ограничен примитивами, которые переживают путь через DOM:\n * нативный `<select>` хранит в `option.value` только строку, поэтому значение\n * должно однозначно восстанавливаться из своего строкового представления.\n * Объектные модели потребовали бы отдельного key-экстрактора — см. `docs/components.md`.\n */\n/**\n * Значение опции. Объект допустим, но тогда обязателен проп `valueKey`: в DOM\n * значение живёт строкой, и без ключа объекты неотличимы друг от друга.\n */\nexport type GrSelectValue = string | number | object\nexport type GrSelectState = GrControlState\n\nexport type GrSelectOption<TValue extends GrSelectValue = string> = {\n value: TValue\n label: string\n disabled?: boolean\n /**\n * Тон чипа в режиме `tags` — перекрывает `tagTone` для этой опции.\n *\n * Цвет у каждого тега свой — обычная модель (метки, категории, статусы), и\n * без неё потребитель уходил в слот `#value` рисовать выбранное сам, теряя\n * ровно то, что в `tags` сделано правильно: чипы снаружи `role=\"combobox\"`,\n * снятие крестиком, сворачивание в «+N» и клавиатуру.\n */\n tone?: GrChipTone\n /** Плотная заливка чипа этой опции — перекрывает `tagDark`. */\n dark?: boolean\n}\n/** Группа опций: заголовок `label` + вложенные опции `options`. */\nexport type GrSelectOptionGroup<TValue extends GrSelectValue = string> = {\n label: string\n options: GrSelectOption<TValue>[]\n}\n/** Элемент списка опций: либо одиночная опция, либо группа опций. */\nexport type GrSelectOptionOrGroup<TValue extends GrSelectValue = string>\n = | GrSelectOption<TValue>\n | GrSelectOptionGroup<TValue>\nexport type GrSelectModelValue<TValue extends GrSelectValue = string> = TValue | TValue[]\n\nexport const defaultBaseClass = 'w-full rounded-[var(--gr-radius-control)] border bg-[var(--gr-bg)] text-[var(--gr-fg)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]'\n\n/**\n * Цвет рамки по состоянию — та же карта, что у `GrInput` и `GrTextarea`: поля\n * одной формы обязаны показывать ошибку одинаково.\n */\nexport const borderClassByState: Record<GrSelectState, string> = {\n default: 'border-[var(--gr-brd)]',\n success: 'border-[var(--gr-success)] focus-visible:ring-[var(--gr-success)]',\n warning: 'border-[var(--gr-warning)] focus-visible:ring-[var(--gr-warning)]',\n danger: 'border-[var(--gr-danger)] focus-visible:ring-[var(--gr-danger)]',\n}\n\n/**\n * Ошибка валидации красится своей ролью, а не декоративным тоном `danger`:\n * `state=\"danger\"` — это подсветка по решению разработчика, `invalid` — вердикт\n * валидации, и тема вправе развести их по цвету.\n */\nexport const invalidBorderClass = 'border-[var(--gr-invalid-brd)] focus-visible:ring-[var(--gr-invalid-ring)]'\nexport const linkBaseClass = 'cursor-pointer inline-block w-auto align-baseline appearance-none bg-transparent border border-transparent px-0 py-0 rounded-[var(--gr-radius-control)] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]'\n\nexport const selectSizeClassBySize: Record<GrSelectSize, string> = {\n xs: 'h-7 px-2.5 text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]',\n sm: 'h-8 px-3 text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]',\n md: 'h-10 px-3 text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)]',\n lg: 'h-11 px-4 text-[length:var(--gr-control-text-lg)] leading-[var(--gr-control-leading-lg)]',\n}\n\nexport const selectLinkSizeClassBySize: Record<GrSelectSize, string> = {\n xs: 'text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]',\n sm: 'text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]',\n md: 'text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)]',\n lg: 'text-[length:var(--gr-control-text-lg)] leading-[var(--gr-control-leading-lg)]',\n}\n\n/**\n * Ссылка — это ТЕКСТ на фоне страницы, поэтому опасный тон берётся из\n * `--gr-danger-text`, а не из насыщенного `--gr-danger` (последний под текст не\n * рассчитан). Hover/active выводятся подмесом `--gr-fg` — той же формулой, что\n * `GrLink`. `primary` берётся из `--gr-primary-text` по той же причине: на фоне\n * страницы насыщенный `--gr-primary` под текст не рассчитан.\n *\n * Классы записаны литералами, а не собраны интерполяцией: safelist и скан\n * UnoCSS читают исходник, и вычисленная строка для них не класс.\n */\nexport const selectLinkVariantClassByVariant: Record<GrSelectVariant, string> = {\n primary: 'text-[var(--gr-primary-text)] hover:text-[color-mix(in_srgb,var(--gr-primary-text)_92%,var(--gr-fg))] active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n default: 'text-[var(--gr-fg)] hover:text-[var(--gr-primary-text)] active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n muted: 'text-[var(--gr-muted-fg)] hover:text-[var(--gr-fg)] active:text-[var(--gr-fg)]',\n danger: 'text-[var(--gr-danger-text)] hover:text-[color-mix(in_srgb,var(--gr-danger-text)_92%,var(--gr-fg))] active:text-[color-mix(in_srgb,var(--gr-danger-text)_84%,var(--gr-fg))]',\n}\n\n/**\n * Variant-классы для видимой метки в `view=\"link\"` + `optionsView=\"native\"`.\n * Hover-цвет приходит через `peer-hover:` от прозрачного `<select>`-overlay,\n * т.к. видимая метка имеет `pointer-events-none` и не получает hover напрямую.\n */\nexport const selectLinkNativeLabelVariantClassByVariant: Record<GrSelectVariant, string> = {\n primary: 'text-[var(--gr-primary-text)] peer-hover:text-[color-mix(in_srgb,var(--gr-primary-text)_92%,var(--gr-fg))] peer-active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n default: 'text-[var(--gr-fg)] peer-hover:text-[var(--gr-primary-text)] peer-active:text-[color-mix(in_srgb,var(--gr-primary-text)_84%,var(--gr-fg))]',\n muted: 'text-[var(--gr-muted-fg)] peer-hover:text-[var(--gr-fg)] peer-active:text-[var(--gr-fg)]',\n danger: 'text-[var(--gr-danger-text)] peer-hover:text-[color-mix(in_srgb,var(--gr-danger-text)_92%,var(--gr-fg))] peer-active:text-[color-mix(in_srgb,var(--gr-danger-text)_84%,var(--gr-fg))]',\n}\n\nfunction selectLinkUnderlineClass(options: { underline: GrSelectUnderline, disabled: boolean }): string {\n if (options.disabled)\n return 'no-underline'\n if (options.underline === 'always')\n return 'underline underline-offset-4'\n if (options.underline === 'none')\n return 'no-underline'\n return 'no-underline hover:underline hover:underline-offset-4'\n}\n\n/**\n * Underline-классы для видимой метки в `view=\"link\"` + `optionsView=\"native\"`.\n * Видимая метка имеет `pointer-events-none`, а hover получает прозрачный\n * `<select>`-overlay (peer), поэтому для `underline=\"auto\"` используем\n * `peer-hover:underline` вместо `hover:underline`.\n */\nfunction selectLinkNativeLabelUnderlineClass(options: { underline: GrSelectUnderline, disabled: boolean }): string {\n if (options.disabled)\n return 'no-underline'\n if (options.underline === 'always')\n return 'underline underline-offset-4'\n if (options.underline === 'none')\n return 'no-underline'\n return 'no-underline peer-hover:underline peer-hover:underline-offset-4'\n}\n\n/**\n * Классы прозрачного `<select>`-overlay для `view=\"link\"` + `optionsView=\"native\"`.\n * Нативный селект растягивается на всю обёртку, но визуально невидим — ширину обёртки\n * задаёт видимый `<span>` с выбранной меткой, благодаря чему компонент в закрытом\n * состоянии занимает столько места, сколько нужно текущей опции (а не самой длинной).\n */\nexport const grSelectLinkNativeOverlayClass = 'peer absolute inset-0 w-full h-full m-0 p-0 border-0 bg-transparent text-transparent appearance-none opacity-0 cursor-pointer disabled:cursor-not-allowed focus:outline-none'\n\n/**\n * Классы видимой метки для `view=\"link\"` + `optionsView=\"native\"` (поверх прозрачного `<select>`).\n * Получает все link-стили (size/variant/underline), а также focus-ring через `peer-focus-visible`.\n */\nexport const grSelectLinkNativeLabelBaseClass = 'pointer-events-none inline-block whitespace-nowrap align-baseline rounded-[var(--gr-radius-control)] transition-colors duration-[var(--gr-duration-fast)]'\nexport const grSelectLinkNativeLabelDisabledClass = 'cursor-not-allowed text-[var(--gr-muted-fg)] no-underline'\nexport const grSelectLinkNativeLabelFocusClass = 'peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--gr-ring)]'\n\nexport function grSelectLinkNativeLabelClass(options: {\n size: GrSelectSize\n variant: GrSelectVariant\n underline: GrSelectUnderline\n disabled: boolean\n}): string {\n return [\n grSelectLinkNativeLabelBaseClass,\n selectLinkSizeClassBySize[options.size],\n selectLinkNativeLabelUnderlineClass({ underline: options.underline, disabled: options.disabled }),\n selectLinkNativeLabelVariantClassByVariant[options.variant],\n options.disabled ? grSelectLinkNativeLabelDisabledClass : '',\n grSelectLinkNativeLabelFocusClass,\n ]\n .filter(Boolean)\n .join(' ')\n}\n\nexport function grSelectClass(options: {\n view: GrSelectView\n size: GrSelectSize\n disabled: boolean\n variant: GrSelectVariant\n underline: GrSelectUnderline\n state?: GrSelectState\n invalid?: boolean\n}): string {\n if (options.view === 'link') {\n return [\n selectLinkSizeClassBySize[options.size],\n selectLinkUnderlineClass({ underline: options.underline, disabled: options.disabled }),\n selectLinkVariantClassByVariant[options.variant],\n 'disabled:cursor-not-allowed disabled:text-[var(--gr-muted-fg)] disabled:no-underline',\n ].join(' ')\n }\n\n return [\n selectSizeClassBySize[options.size],\n // `invalid` сильнее `state`: ошибка перекрывает любую другую подсветку.\n options.invalid ? invalidBorderClass : borderClassByState[options.state ?? 'default'],\n // Заблокированный контрол гасится фоном и цветом текста, а не `opacity`:\n // прозрачность разбавляет выверенные на AA токены и роняет контраст.\n 'disabled:cursor-not-allowed disabled:bg-[var(--gr-muted)] disabled:text-[var(--gr-muted-fg)]',\n ].join(' ')\n}\n\nexport function grSelectNativeClass(options: {\n view: GrSelectView\n size: GrSelectSize\n disabled: boolean\n variant: GrSelectVariant\n underline: GrSelectUnderline\n showNativeChevron: boolean\n state?: GrSelectState\n invalid?: boolean\n}): string {\n return [\n grSelectClass(options),\n options.showNativeChevron ? 'appearance-none pr-9' : '',\n ]\n .filter(Boolean)\n .join(' ')\n}\n\nexport function grSelectTriggerClass(options: {\n view: GrSelectView\n optionsView: GrSelectOptionsView\n size: GrSelectSize\n disabled: boolean\n variant: GrSelectVariant\n underline: GrSelectUnderline\n state?: GrSelectState\n invalid?: boolean\n}): string {\n if (options.optionsView !== 'panel') {\n return grSelectClass(options)\n }\n\n if (options.view === 'link') {\n return [\n grSelectClass(options),\n 'inline-flex items-center gap-1 text-left',\n ].join(' ')\n }\n\n return [\n grSelectClass(options),\n 'flex items-center justify-between text-left',\n ].join(' ')\n}\n\nexport const grSelectPanelClasses = `${overlayPanelSurfaceClass} overflow-hidden`\n\n/** Общая подсветка наведения и активной опции панели. */\nconst selectOptionHighlight = 'bg-[color-mix(in_srgb,var(--gr-muted)_30%,transparent)]'\n\nexport const selectOptionBaseClass = 'rounded-[var(--gr-radius-md)] px-3 py-2 text-left text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)]'\nexport const selectOptionLinkWidthClass = 'block min-w-full w-max whitespace-nowrap'\nexport const selectOptionWidthClass = 'w-full'\nexport const selectOptionEnabledClass = `hover:${selectOptionHighlight}`\nexport const selectOptionActiveClass = selectOptionHighlight\n/** Выключенная опция гасится токеном текста — по той же причине, что и контрол. */\nexport const selectOptionDisabledClass = 'cursor-not-allowed text-[var(--gr-muted-fg)]'\n\nexport function grSelectOptionClass(options: {\n view: GrSelectView\n disabled: boolean\n active: boolean\n}): string {\n return [\n selectOptionBaseClass,\n options.view === 'link' ? selectOptionLinkWidthClass : selectOptionWidthClass,\n options.disabled ? selectOptionDisabledClass : selectOptionEnabledClass,\n !options.disabled && options.active ? selectOptionActiveClass : '',\n ]\n .filter(Boolean)\n .join(' ')\n}\n"],"mappings":";;AAkDA,IAAa,mBAAmB;;;;;AAMhC,IAAa,qBAAoD;CAC/D,SAAS;CACT,SAAS;CACT,SAAS;CACT,QAAQ;AACV;;;;;;AAOA,IAAa,qBAAqB;AAClC,IAAa,gBAAgB;AAE7B,IAAa,wBAAsD;CACjE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,IAAa,4BAA0D;CACrE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;;;;;;;;;;AAYA,IAAa,kCAAmE;CAC9E,SAAS;CACT,SAAS;CACT,OAAO;CACP,QAAQ;AACV;;;;;;AAOA,IAAa,6CAA8E;CACzF,SAAS;CACT,SAAS;CACT,OAAO;CACP,QAAQ;AACV;AAEA,SAAS,yBAAyB,SAAsE;CACtG,IAAI,QAAQ,UACV,OAAO;CACT,IAAI,QAAQ,cAAc,UACxB,OAAO;CACT,IAAI,QAAQ,cAAc,QACxB,OAAO;CACT,OAAO;AACT;;;;;;;AAQA,SAAS,oCAAoC,SAAsE;CACjH,IAAI,QAAQ,UACV,OAAO;CACT,IAAI,QAAQ,cAAc,UACxB,OAAO;CACT,IAAI,QAAQ,cAAc,QACxB,OAAO;CACT,OAAO;AACT;;;;;;;AAQA,IAAa,iCAAiC;;;;;AAM9C,IAAa,mCAAmC;AAChD,IAAa,uCAAuC;AACpD,IAAa,oCAAoC;AAEjD,SAAgB,6BAA6B,SAKlC;CACT,OAAO;EACL;EACA,0BAA0B,QAAQ;EAClC,oCAAoC;GAAE,WAAW,QAAQ;GAAW,UAAU,QAAQ;EAAS,CAAC;EAChG,2CAA2C,QAAQ;EACnD,QAAQ,WAAW,uCAAuC;EAC1D;CACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb;AAEA,SAAgB,cAAc,SAQnB;CACT,IAAI,QAAQ,SAAS,QACnB,OAAO;EACL,0BAA0B,QAAQ;EAClC,yBAAyB;GAAE,WAAW,QAAQ;GAAW,UAAU,QAAQ;EAAS,CAAC;EACrF,gCAAgC,QAAQ;EACxC;CACF,CAAC,CAAC,KAAK,GAAG;CAGZ,OAAO;EACL,sBAAsB,QAAQ;EAE9B,QAAQ,UAAU,qBAAqB,mBAAmB,QAAQ,SAAS;EAG3E;CACF,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,SAAgB,oBAAoB,SASzB;CACT,OAAO,CACL,cAAc,OAAO,GACrB,QAAQ,oBAAoB,yBAAyB,EACvD,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb;AAEA,SAAgB,qBAAqB,SAS1B;CACT,IAAI,QAAQ,gBAAgB,SAC1B,OAAO,cAAc,OAAO;CAG9B,IAAI,QAAQ,SAAS,QACnB,OAAO,CACL,cAAc,OAAO,GACrB,0CACF,CAAC,CAAC,KAAK,GAAG;CAGZ,OAAO,CACL,cAAc,OAAO,GACrB,6CACF,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,IAAa,uBAAuB,GAAG,yBAAyB;;AAGhE,IAAM,wBAAwB;AAE9B,IAAa,wBAAwB;AACrC,IAAa,6BAA6B;AAC1C,IAAa,yBAAyB;AACtC,IAAa,2BAA2B,SAAS;AACjD,IAAa,0BAA0B;;AAEvC,IAAa,4BAA4B;AAEzC,SAAgB,oBAAoB,SAIzB;CACT,OAAO;EACL;EACA,QAAQ,SAAS,SAAS,6BAA6B;EACvD,QAAQ,WAAW,4BAA4B;EAC/C,CAAC,QAAQ,YAAY,QAAQ,SAAS,0BAA0B;CAClE,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as grAlertConfig } from "./config-BNu8lFQB.js";
|
|
2
|
-
import { t as grAutocompleteConfig } from "./config-
|
|
2
|
+
import { t as grAutocompleteConfig } from "./config-CYthTVK2.js";
|
|
3
3
|
import { t as grAvatarConfig } from "./config-B3J0alyY.js";
|
|
4
4
|
import { t as grBadgeConfig } from "./config-BscVxZR3.js";
|
|
5
5
|
import { t as grBadgeWrapConfig } from "./config-DwABJHEo.js";
|
|
@@ -19,7 +19,7 @@ import { t as grColorPickerConfig } from "./config-DIDz_E5m.js";
|
|
|
19
19
|
import { t as grCommandPaletteConfig } from "./config-D0NzGYaE.js";
|
|
20
20
|
import { t as grConfigProviderConfig } from "./config-B7W9VlZ7.js";
|
|
21
21
|
import { t as grConfirmDialogConfig } from "./config-A-bBiotF.js";
|
|
22
|
-
import { t as grContextMenuConfig } from "./config-
|
|
22
|
+
import { t as grContextMenuConfig } from "./config-CsCUuukB.js";
|
|
23
23
|
import { t as grDataTableConfig } from "./config-DeyT9dJl.js";
|
|
24
24
|
import { t as grValueConfig } from "./config-Z5LeUXys.js";
|
|
25
25
|
import { t as grDeltaConfig } from "./config-CjWOTgLL.js";
|
|
@@ -28,7 +28,7 @@ import { t as grDialogSafelist } from "./safelist-Y3zrlhOa.js";
|
|
|
28
28
|
import { t as grDialogServiceConfig } from "./config-CjXjhphs.js";
|
|
29
29
|
import { t as grDividerConfig } from "./config-D82r782v.js";
|
|
30
30
|
import { t as grDrawerConfig } from "./config-ChPg-BQv.js";
|
|
31
|
-
import { t as grDropdownConfig } from "./config-
|
|
31
|
+
import { t as grDropdownConfig } from "./config-DGGaKf4R.js";
|
|
32
32
|
import { t as grDropdownMenuConfig } from "./config-_jbgsFpw.js";
|
|
33
33
|
import { t as grEmptyStateConfig } from "./config-CWtfxkM9.js";
|
|
34
34
|
import { t as grFilePreviewConfig } from "./config-BaWANzzB.js";
|
|
@@ -49,7 +49,7 @@ import { t as grModalConfig } from "./config-mlheIMtV.js";
|
|
|
49
49
|
import { t as grNavbarConfig } from "./config-2ZHRsdOc.js";
|
|
50
50
|
import { t as grNumberInputConfig } from "./config-MmPym-Z72.js";
|
|
51
51
|
import { t as grPaginationConfig } from "./config-CjKpUcmy.js";
|
|
52
|
-
import { t as grPopoverConfig } from "./config-
|
|
52
|
+
import { t as grPopoverConfig } from "./config-DBXCIb6r2.js";
|
|
53
53
|
import { t as grProgressBarConfig } from "./config-DWL81-aM.js";
|
|
54
54
|
import { t as grProgressCircleConfig } from "./config-CJzmDHfI.js";
|
|
55
55
|
import { t as grPromptDialogConfig } from "./config-C0Ea9ePP.js";
|
|
@@ -57,7 +57,7 @@ import { t as grRadioConfig } from "./config-CuQmUkMw2.js";
|
|
|
57
57
|
import { t as grRadioGroupConfig } from "./config-QaIc_gRa.js";
|
|
58
58
|
import { t as grRatingConfig } from "./config-DRbojEzO.js";
|
|
59
59
|
import { t as grSegmentedConfig } from "./config-rLkc9Zn2.js";
|
|
60
|
-
import { t as grSelectConfig } from "./config-
|
|
60
|
+
import { t as grSelectConfig } from "./config-DOYz_SVK.js";
|
|
61
61
|
import { t as grSidebarConfig } from "./config-DNDgp9bB.js";
|
|
62
62
|
import { t as grSkeletonConfig } from "./config-8Pcutu1j.js";
|
|
63
63
|
import { t as grSliderConfig } from "./config-E1cTyRVV.js";
|
|
@@ -74,7 +74,7 @@ import { t as grTimelineConfig } from "./config-Bd7QfiGj.js";
|
|
|
74
74
|
import { t as grToasterConfig } from "./config-B5SBUa6u.js";
|
|
75
75
|
import { t as grTooltipConfig } from "./config-BTmfQFwi.js";
|
|
76
76
|
import { t as grTreeConfig } from "./config-CDat1wXf.js";
|
|
77
|
-
import { t as grTreeSelectConfig } from "./config-
|
|
77
|
+
import { t as grTreeSelectConfig } from "./config-DUNH02Fg.js";
|
|
78
78
|
import { defineGranularComponent, defineGranularProvider, resolvePackageBaseUrl } from "@feugene/unocss-preset-granular/contract";
|
|
79
79
|
//#region src/components/GrDialog/config.ts
|
|
80
80
|
var grDialogConfig = defineGranularComponent(import.meta.url, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"granular-provider-CrQua8PT.js","names":[],"sources":["../../src/components/GrDialog/config.ts","../../src/components/GrResponseErrorBanner/config.ts","../../src/granular-provider/shared.ts","../../src/granular-provider/index.ts"],"sourcesContent":["import { defineGranularComponent } from '@feugene/unocss-preset-granular/contract'\n\nimport { grDialogSafelist } from './safelist'\n\nexport const grDialogConfig = defineGranularComponent(import.meta.url, {\n name: 'GrDialog',\n dependencies: ['GrButton', 'GrModal'],\n safelist: grDialogSafelist,\n})\n","import { defineGranularComponent } from '@feugene/unocss-preset-granular/contract'\n\nexport const grResponseErrorBannerConfig = defineGranularComponent(import.meta.url, {\n name: 'GrResponseErrorBanner',\n dependencies: ['GrAlert', 'GrButton'],\n})\n","// `id`, `theme.*`, `packageBaseUrl` и реестр компонентов провайдера.\n//\n// Все `new URL('../styles/...', import.meta.url)` обязаны лежать именно здесь:\n// `shared.ts` в одной директории с `index.ts` / `node.ts`, поэтому\n// относительные пути совпадают. Для браузера безопасно — бандлеры\n// транслируют такие литералы в статические asset-URL.\n//\n// `apps/showcase/scripts/generate-component-api.mjs` читает список компонентов\n// из `Object.keys(granularityComponentConfigs)` через vite SSR.\nimport {\n defineGranularProvider,\n type GranularComponentDescriptor,\n type GranularProvider,\n resolvePackageBaseUrl,\n} from '@feugene/unocss-preset-granular/contract'\n// <granularity:components:imports> — блок генерируется `yarn generate:registry`\nimport { grAlertConfig } from '../components/GrAlert/config'\nimport { grAutocompleteConfig } from '../components/GrAutocomplete/config'\nimport { grAvatarConfig } from '../components/GrAvatar/config'\nimport { grBadgeConfig } from '../components/GrBadge/config'\nimport { grBadgeWrapConfig } from '../components/GrBadgeWrap/config'\nimport { grBottomNavConfig } from '../components/GrBottomNav/config'\nimport { grBreadcrumbsConfig } from '../components/GrBreadcrumbs/config'\nimport { grButtonConfig } from '../components/GrButton/config'\nimport { grButtonGroupConfig } from '../components/GrButtonGroup/config'\nimport { grCardConfig } from '../components/GrCard/config'\nimport { grCheckboxConfig } from '../components/GrCheckbox/config'\nimport { grCheckboxGroupConfig } from '../components/GrCheckboxGroup/config'\nimport { grChipConfig } from '../components/GrChip/config'\nimport { grChipGroupConfig } from '../components/GrChipGroup/config'\nimport { grCodeBlockConfig } from '../components/GrCodeBlock/config'\nimport { grCollapseConfig } from '../components/GrCollapse/config'\nimport { grColorPickerConfig } from '../components/GrColorPicker/config'\nimport { grCommandPaletteConfig } from '../components/GrCommandPalette/config'\nimport { grConfigProviderConfig } from '../components/GrConfigProvider/config'\nimport { grConfirmDialogConfig } from '../components/GrConfirmDialog/config'\nimport { grContextMenuConfig } from '../components/GrContextMenu/config'\nimport { grDataTableConfig } from '../components/GrDataTable/config'\nimport { grDeltaConfig } from '../components/GrDelta/config'\nimport { grDescriptionListConfig } from '../components/GrDescriptionList/config'\nimport { grDialogConfig } from '../components/GrDialog/config'\nimport { grDialogServiceConfig } from '../components/GrDialogService/config'\nimport { grDividerConfig } from '../components/GrDivider/config'\nimport { grDrawerConfig } from '../components/GrDrawer/config'\nimport { grDropdownConfig } from '../components/GrDropdown/config'\nimport { grDropdownMenuConfig } from '../components/GrDropdownMenu/config'\nimport { grEmptyStateConfig } from '../components/GrEmptyState/config'\nimport { grFilePreviewConfig } from '../components/GrFilePreview/config'\nimport { grFileUploadConfig } from '../components/GrFileUpload/config'\nimport { grFormConfig } from '../components/GrForm/config'\nimport { grFormFieldConfig } from '../components/GrFormField/config'\nimport { grFormFileConfig } from '../components/GrFormFile/config'\nimport { grFormSectionConfig } from '../components/GrFormSection/config'\nimport { grIconConfig } from '../components/GrIcon/config'\nimport { grImageViewerConfig } from '../components/GrImageViewer/config'\nimport { grInputConfig } from '../components/GrInput/config'\nimport { grInputTagConfig } from '../components/GrInputTag/config'\nimport { grJsonViewerConfig } from '../components/GrJsonViewer/config'\nimport { grKbdConfig } from '../components/GrKbd/config'\nimport { grLinkConfig } from '../components/GrLink/config'\nimport { grListConfig } from '../components/GrList/config'\nimport { grLoadingConfig } from '../components/GrLoading/config'\nimport { grModalConfig } from '../components/GrModal/config'\nimport { grNavbarConfig } from '../components/GrNavbar/config'\nimport { grNumberInputConfig } from '../components/GrNumberInput/config'\nimport { grPaginationConfig } from '../components/GrPagination/config'\nimport { grPopoverConfig } from '../components/GrPopover/config'\nimport { grProgressBarConfig } from '../components/GrProgressBar/config'\nimport { grProgressCircleConfig } from '../components/GrProgressCircle/config'\nimport { grPromptDialogConfig } from '../components/GrPromptDialog/config'\nimport { grRadioConfig } from '../components/GrRadio/config'\nimport { grRadioGroupConfig } from '../components/GrRadioGroup/config'\nimport { grRatingConfig } from '../components/GrRating/config'\nimport { grResponseErrorBannerConfig } from '../components/GrResponseErrorBanner/config'\nimport { grSegmentedConfig } from '../components/GrSegmented/config'\nimport { grSelectConfig } from '../components/GrSelect/config'\nimport { grSidebarConfig } from '../components/GrSidebar/config'\nimport { grSkeletonConfig } from '../components/GrSkeleton/config'\nimport { grSliderConfig } from '../components/GrSlider/config'\nimport { grSortableListConfig } from '../components/GrSortableList/config'\nimport { grSplitterConfig } from '../components/GrSplitter/config'\nimport { grStatisticConfig } from '../components/GrStatistic/config'\nimport { grStepsConfig } from '../components/GrSteps/config'\nimport { grSwitchConfig } from '../components/GrSwitch/config'\nimport { grTableConfig } from '../components/GrTable/config'\nimport { grTabPanelsConfig } from '../components/GrTabPanels/config'\nimport { grTabsConfig } from '../components/GrTabs/config'\nimport { grTextareaConfig } from '../components/GrTextarea/config'\nimport { grTimelineConfig } from '../components/GrTimeline/config'\nimport { grToasterConfig } from '../components/GrToaster/config'\nimport { grTooltipConfig } from '../components/GrTooltip/config'\nimport { grTreeConfig } from '../components/GrTree/config'\nimport { grTreeSelectConfig } from '../components/GrTreeSelect/config'\nimport { grValueConfig } from '../components/GrValue/config'\n// </granularity:components:imports>\n\n/** Идентификатор провайдера — совпадает с именем пакета. */\nexport const GRANULARITY_PROVIDER_ID = '@feugene/granularity'\n\n// Не заменять на `new URL('..', import.meta.url)`: Vite и rolldown распознают\n// этот литерал и подставляют `data:`-URL, после чего scan-директории пустеют.\nconst packageBaseUrl = resolvePackageBaseUrl(import.meta.url)\n\n/** Встроенные темы пакета. Единственный источник правды о списке тем. */\nexport const granularityThemeNames = ['light', 'dark'] as const\nexport type GranularityThemeName = (typeof granularityThemeNames)[number]\n\n/** Темы, активные по умолчанию, если окружение не переопределяет выбор. */\nexport const granularityDefaultThemes: readonly GranularityThemeName[] = ['light']\n\nconst theme = {\n baseCssUrl: new URL('../styles/base.css', import.meta.url).href,\n tokensCssUrl: new URL('../styles/tokens.css', import.meta.url).href,\n themes: {\n light: new URL('../styles/themes/light.css', import.meta.url).href,\n dark: new URL('../styles/themes/dark.css', import.meta.url).href,\n },\n defaultThemes: granularityDefaultThemes,\n} as const\n\n/**\n * Реестр публичных компонентов. Ключи совпадают с subpath-экспортом\n * `@feugene/granularity/components/<Name>`.\n */\nexport const granularityComponentConfigs = {\n // <granularity:components:registry> — блок генерируется `yarn generate:registry`\n GrAlert: grAlertConfig,\n GrAutocomplete: grAutocompleteConfig,\n GrAvatar: grAvatarConfig,\n GrBadge: grBadgeConfig,\n GrBadgeWrap: grBadgeWrapConfig,\n GrBottomNav: grBottomNavConfig,\n GrBreadcrumbs: grBreadcrumbsConfig,\n GrButton: grButtonConfig,\n GrButtonGroup: grButtonGroupConfig,\n GrCard: grCardConfig,\n GrCheckbox: grCheckboxConfig,\n GrCheckboxGroup: grCheckboxGroupConfig,\n GrChip: grChipConfig,\n GrChipGroup: grChipGroupConfig,\n GrCodeBlock: grCodeBlockConfig,\n GrCollapse: grCollapseConfig,\n GrColorPicker: grColorPickerConfig,\n GrCommandPalette: grCommandPaletteConfig,\n GrConfigProvider: grConfigProviderConfig,\n GrConfirmDialog: grConfirmDialogConfig,\n GrContextMenu: grContextMenuConfig,\n GrDataTable: grDataTableConfig,\n GrDelta: grDeltaConfig,\n GrDescriptionList: grDescriptionListConfig,\n GrDialog: grDialogConfig,\n GrDialogService: grDialogServiceConfig,\n GrDivider: grDividerConfig,\n GrDrawer: grDrawerConfig,\n GrDropdown: grDropdownConfig,\n GrDropdownMenu: grDropdownMenuConfig,\n GrEmptyState: grEmptyStateConfig,\n GrFilePreview: grFilePreviewConfig,\n GrFileUpload: grFileUploadConfig,\n GrForm: grFormConfig,\n GrFormField: grFormFieldConfig,\n GrFormFile: grFormFileConfig,\n GrFormSection: grFormSectionConfig,\n GrIcon: grIconConfig,\n GrImageViewer: grImageViewerConfig,\n GrInput: grInputConfig,\n GrInputTag: grInputTagConfig,\n GrJsonViewer: grJsonViewerConfig,\n GrKbd: grKbdConfig,\n GrLink: grLinkConfig,\n GrList: grListConfig,\n GrLoading: grLoadingConfig,\n GrModal: grModalConfig,\n GrNavbar: grNavbarConfig,\n GrNumberInput: grNumberInputConfig,\n GrPagination: grPaginationConfig,\n GrPopover: grPopoverConfig,\n GrProgressBar: grProgressBarConfig,\n GrProgressCircle: grProgressCircleConfig,\n GrPromptDialog: grPromptDialogConfig,\n GrRadio: grRadioConfig,\n GrRadioGroup: grRadioGroupConfig,\n GrRating: grRatingConfig,\n GrResponseErrorBanner: grResponseErrorBannerConfig,\n GrSegmented: grSegmentedConfig,\n GrSelect: grSelectConfig,\n GrSidebar: grSidebarConfig,\n GrSkeleton: grSkeletonConfig,\n GrSlider: grSliderConfig,\n GrSortableList: grSortableListConfig,\n GrSplitter: grSplitterConfig,\n GrStatistic: grStatisticConfig,\n GrSteps: grStepsConfig,\n GrSwitch: grSwitchConfig,\n GrTable: grTableConfig,\n GrTabPanels: grTabPanelsConfig,\n GrTabs: grTabsConfig,\n GrTextarea: grTextareaConfig,\n GrTimeline: grTimelineConfig,\n GrToaster: grToasterConfig,\n GrTooltip: grTooltipConfig,\n GrTree: grTreeConfig,\n GrTreeSelect: grTreeSelectConfig,\n GrValue: grValueConfig,\n // </granularity:components:registry>\n}\n\nexport type GranularityComponentName = keyof typeof granularityComponentConfigs\n\n/** Базовый набор компонентов в порядке реестра. */\nconst baseComponents: readonly GranularComponentDescriptor[] = Object.values(\n granularityComponentConfigs,\n)\n\n/**\n * Собирает granular-provider пакета.\n *\n * `overrides` — точка расширения для потребителя: дескриптор с именем из\n * базового реестра заменяет его, остальные дописываются в конец.\n */\nexport function createGranularityProvider(\n overrides: readonly GranularComponentDescriptor[] = [],\n): GranularProvider {\n const overrideByName = new Map(\n overrides.map(component => [component.name, component]),\n )\n const components: GranularComponentDescriptor[] = [\n ...baseComponents.map(component => overrideByName.get(component.name) ?? component),\n ...overrides.filter(component => !baseComponents.some(base => base.name === component.name)),\n ]\n\n return defineGranularProvider({\n id: GRANULARITY_PROVIDER_ID,\n contractVersion: 1,\n packageBaseUrl,\n components,\n theme,\n })\n}\n","import { createGranularityProvider } from './shared'\n\nexport * from './shared'\n\nexport const granularityProvider = createGranularityProvider()\n\nexport default granularityProvider\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAa,iBAAiB,wBAAwB,OAAO,KAAK,KAAK;CACrE,MAAM;CACN,cAAc,CAAC,YAAY,SAAS;CACpC,UAAU;AACZ,CAAC;;;ACND,IAAa,8BAA8B,wBAAwB,OAAO,KAAK,KAAK;CAClF,MAAM;CACN,cAAc,CAAC,WAAW,UAAU;AACtC,CAAC;;;;AC4FD,IAAa,0BAA0B;AAIvC,IAAM,iBAAiB,sBAAsB,OAAO,KAAK,GAAG;;AAG5D,IAAa,wBAAwB,CAAC,SAAS,MAAM;;AAIrD,IAAa,2BAA4D,CAAC,OAAO;AAEjF,IAAM,QAAQ;CACZ,YAAY,IAAA,IAAA,q5KAAA,KAAA,OAAA,KAAA,GAAA,CAA8C,CAAC;CAC3D,cAAc,IAAA,IAAA,65YAAA,KAAA,OAAA,KAAA,GAAA,CAAgD,CAAC;CAC/D,QAAQ;EACN,OAAO,IAAA,IAAA,iyYAAA,KAAA,OAAA,KAAA,GAAA,CAAsD,CAAC;EAC9D,MAAM,IAAA,IAAA,y/SAAA,KAAA,OAAA,KAAA,GAAA,CAAqD,CAAC;CAC9D;CACA,eAAe;AACjB;;;;;AAMA,IAAa,8BAA8B;CAEzC,SAAS;CACT,gBAAgB;CAChB,UAAU;CACV,SAAS;CACT,aAAa;CACb,aAAa;CACb,eAAe;CACf,UAAU;CACV,eAAe;CACf,QAAQ;CACR,YAAY;CACZ,iBAAiB;CACjB,QAAQ;CACR,aAAa;CACb,aAAa;CACb,YAAY;CACZ,eAAe;CACf,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,aAAa;CACb,SAAS;CACT,mBAAmB;CACnB,UAAU;CACV,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,cAAc;CACd,QAAQ;CACR,aAAa;CACb,YAAY;CACZ,eAAe;CACf,QAAQ;CACR,eAAe;CACf,SAAS;CACT,YAAY;CACZ,cAAc;CACd,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,WAAW;CACX,SAAS;CACT,UAAU;CACV,eAAe;CACf,cAAc;CACd,WAAW;CACX,eAAe;CACf,kBAAkB;CAClB,gBAAgB;CAChB,SAAS;CACT,cAAc;CACd,UAAU;CACV,uBAAuB;CACvB,aAAa;CACb,UAAU;CACV,WAAW;CACX,YAAY;CACZ,UAAU;CACV,gBAAgB;CAChB,YAAY;CACZ,aAAa;CACb,SAAS;CACT,UAAU;CACV,SAAS;CACT,aAAa;CACb,QAAQ;CACR,YAAY;CACZ,YAAY;CACZ,WAAW;CACX,WAAW;CACX,QAAQ;CACR,cAAc;CACd,SAAS;AAEX;;AAKA,IAAM,iBAAyD,OAAO,OACpE,2BACF;;;;;;;AAQA,SAAgB,0BACd,YAAoD,CAAC,GACnC;CAClB,MAAM,iBAAiB,IAAI,IACzB,UAAU,KAAI,cAAa,CAAC,UAAU,MAAM,SAAS,CAAC,CACxD;CAMA,OAAO,uBAAuB;EAC5B,IAAI;EACJ,iBAAiB;EACjB;EACA,aARA,GAAG,eAAe,KAAI,cAAa,eAAe,IAAI,UAAU,IAAI,KAAK,SAAS,GAClF,GAAG,UAAU,QAAO,cAAa,CAAC,eAAe,MAAK,SAAQ,KAAK,SAAS,UAAU,IAAI,CAAC,CAO3F;EACA;CACF,CAAC;AACH;;;AC1OA,IAAa,sBAAsB,0BAA0B"}
|
|
1
|
+
{"version":3,"file":"granular-provider-3LUawDPh.js","names":[],"sources":["../../src/components/GrDialog/config.ts","../../src/components/GrResponseErrorBanner/config.ts","../../src/granular-provider/shared.ts","../../src/granular-provider/index.ts"],"sourcesContent":["import { defineGranularComponent } from '@feugene/unocss-preset-granular/contract'\n\nimport { grDialogSafelist } from './safelist'\n\nexport const grDialogConfig = defineGranularComponent(import.meta.url, {\n name: 'GrDialog',\n dependencies: ['GrButton', 'GrModal'],\n safelist: grDialogSafelist,\n})\n","import { defineGranularComponent } from '@feugene/unocss-preset-granular/contract'\n\nexport const grResponseErrorBannerConfig = defineGranularComponent(import.meta.url, {\n name: 'GrResponseErrorBanner',\n dependencies: ['GrAlert', 'GrButton'],\n})\n","// `id`, `theme.*`, `packageBaseUrl` и реестр компонентов провайдера.\n//\n// Все `new URL('../styles/...', import.meta.url)` обязаны лежать именно здесь:\n// `shared.ts` в одной директории с `index.ts` / `node.ts`, поэтому\n// относительные пути совпадают. Для браузера безопасно — бандлеры\n// транслируют такие литералы в статические asset-URL.\n//\n// `apps/showcase/scripts/generate-component-api.mjs` читает список компонентов\n// из `Object.keys(granularityComponentConfigs)` через vite SSR.\nimport {\n defineGranularProvider,\n type GranularComponentDescriptor,\n type GranularProvider,\n resolvePackageBaseUrl,\n} from '@feugene/unocss-preset-granular/contract'\n// <granularity:components:imports> — блок генерируется `yarn generate:registry`\nimport { grAlertConfig } from '../components/GrAlert/config'\nimport { grAutocompleteConfig } from '../components/GrAutocomplete/config'\nimport { grAvatarConfig } from '../components/GrAvatar/config'\nimport { grBadgeConfig } from '../components/GrBadge/config'\nimport { grBadgeWrapConfig } from '../components/GrBadgeWrap/config'\nimport { grBottomNavConfig } from '../components/GrBottomNav/config'\nimport { grBreadcrumbsConfig } from '../components/GrBreadcrumbs/config'\nimport { grButtonConfig } from '../components/GrButton/config'\nimport { grButtonGroupConfig } from '../components/GrButtonGroup/config'\nimport { grCardConfig } from '../components/GrCard/config'\nimport { grCheckboxConfig } from '../components/GrCheckbox/config'\nimport { grCheckboxGroupConfig } from '../components/GrCheckboxGroup/config'\nimport { grChipConfig } from '../components/GrChip/config'\nimport { grChipGroupConfig } from '../components/GrChipGroup/config'\nimport { grCodeBlockConfig } from '../components/GrCodeBlock/config'\nimport { grCollapseConfig } from '../components/GrCollapse/config'\nimport { grColorPickerConfig } from '../components/GrColorPicker/config'\nimport { grCommandPaletteConfig } from '../components/GrCommandPalette/config'\nimport { grConfigProviderConfig } from '../components/GrConfigProvider/config'\nimport { grConfirmDialogConfig } from '../components/GrConfirmDialog/config'\nimport { grContextMenuConfig } from '../components/GrContextMenu/config'\nimport { grDataTableConfig } from '../components/GrDataTable/config'\nimport { grDeltaConfig } from '../components/GrDelta/config'\nimport { grDescriptionListConfig } from '../components/GrDescriptionList/config'\nimport { grDialogConfig } from '../components/GrDialog/config'\nimport { grDialogServiceConfig } from '../components/GrDialogService/config'\nimport { grDividerConfig } from '../components/GrDivider/config'\nimport { grDrawerConfig } from '../components/GrDrawer/config'\nimport { grDropdownConfig } from '../components/GrDropdown/config'\nimport { grDropdownMenuConfig } from '../components/GrDropdownMenu/config'\nimport { grEmptyStateConfig } from '../components/GrEmptyState/config'\nimport { grFilePreviewConfig } from '../components/GrFilePreview/config'\nimport { grFileUploadConfig } from '../components/GrFileUpload/config'\nimport { grFormConfig } from '../components/GrForm/config'\nimport { grFormFieldConfig } from '../components/GrFormField/config'\nimport { grFormFileConfig } from '../components/GrFormFile/config'\nimport { grFormSectionConfig } from '../components/GrFormSection/config'\nimport { grIconConfig } from '../components/GrIcon/config'\nimport { grImageViewerConfig } from '../components/GrImageViewer/config'\nimport { grInputConfig } from '../components/GrInput/config'\nimport { grInputTagConfig } from '../components/GrInputTag/config'\nimport { grJsonViewerConfig } from '../components/GrJsonViewer/config'\nimport { grKbdConfig } from '../components/GrKbd/config'\nimport { grLinkConfig } from '../components/GrLink/config'\nimport { grListConfig } from '../components/GrList/config'\nimport { grLoadingConfig } from '../components/GrLoading/config'\nimport { grModalConfig } from '../components/GrModal/config'\nimport { grNavbarConfig } from '../components/GrNavbar/config'\nimport { grNumberInputConfig } from '../components/GrNumberInput/config'\nimport { grPaginationConfig } from '../components/GrPagination/config'\nimport { grPopoverConfig } from '../components/GrPopover/config'\nimport { grProgressBarConfig } from '../components/GrProgressBar/config'\nimport { grProgressCircleConfig } from '../components/GrProgressCircle/config'\nimport { grPromptDialogConfig } from '../components/GrPromptDialog/config'\nimport { grRadioConfig } from '../components/GrRadio/config'\nimport { grRadioGroupConfig } from '../components/GrRadioGroup/config'\nimport { grRatingConfig } from '../components/GrRating/config'\nimport { grResponseErrorBannerConfig } from '../components/GrResponseErrorBanner/config'\nimport { grSegmentedConfig } from '../components/GrSegmented/config'\nimport { grSelectConfig } from '../components/GrSelect/config'\nimport { grSidebarConfig } from '../components/GrSidebar/config'\nimport { grSkeletonConfig } from '../components/GrSkeleton/config'\nimport { grSliderConfig } from '../components/GrSlider/config'\nimport { grSortableListConfig } from '../components/GrSortableList/config'\nimport { grSplitterConfig } from '../components/GrSplitter/config'\nimport { grStatisticConfig } from '../components/GrStatistic/config'\nimport { grStepsConfig } from '../components/GrSteps/config'\nimport { grSwitchConfig } from '../components/GrSwitch/config'\nimport { grTableConfig } from '../components/GrTable/config'\nimport { grTabPanelsConfig } from '../components/GrTabPanels/config'\nimport { grTabsConfig } from '../components/GrTabs/config'\nimport { grTextareaConfig } from '../components/GrTextarea/config'\nimport { grTimelineConfig } from '../components/GrTimeline/config'\nimport { grToasterConfig } from '../components/GrToaster/config'\nimport { grTooltipConfig } from '../components/GrTooltip/config'\nimport { grTreeConfig } from '../components/GrTree/config'\nimport { grTreeSelectConfig } from '../components/GrTreeSelect/config'\nimport { grValueConfig } from '../components/GrValue/config'\n// </granularity:components:imports>\n\n/** Идентификатор провайдера — совпадает с именем пакета. */\nexport const GRANULARITY_PROVIDER_ID = '@feugene/granularity'\n\n// Не заменять на `new URL('..', import.meta.url)`: Vite и rolldown распознают\n// этот литерал и подставляют `data:`-URL, после чего scan-директории пустеют.\nconst packageBaseUrl = resolvePackageBaseUrl(import.meta.url)\n\n/** Встроенные темы пакета. Единственный источник правды о списке тем. */\nexport const granularityThemeNames = ['light', 'dark'] as const\nexport type GranularityThemeName = (typeof granularityThemeNames)[number]\n\n/** Темы, активные по умолчанию, если окружение не переопределяет выбор. */\nexport const granularityDefaultThemes: readonly GranularityThemeName[] = ['light']\n\nconst theme = {\n baseCssUrl: new URL('../styles/base.css', import.meta.url).href,\n tokensCssUrl: new URL('../styles/tokens.css', import.meta.url).href,\n themes: {\n light: new URL('../styles/themes/light.css', import.meta.url).href,\n dark: new URL('../styles/themes/dark.css', import.meta.url).href,\n },\n defaultThemes: granularityDefaultThemes,\n} as const\n\n/**\n * Реестр публичных компонентов. Ключи совпадают с subpath-экспортом\n * `@feugene/granularity/components/<Name>`.\n */\nexport const granularityComponentConfigs = {\n // <granularity:components:registry> — блок генерируется `yarn generate:registry`\n GrAlert: grAlertConfig,\n GrAutocomplete: grAutocompleteConfig,\n GrAvatar: grAvatarConfig,\n GrBadge: grBadgeConfig,\n GrBadgeWrap: grBadgeWrapConfig,\n GrBottomNav: grBottomNavConfig,\n GrBreadcrumbs: grBreadcrumbsConfig,\n GrButton: grButtonConfig,\n GrButtonGroup: grButtonGroupConfig,\n GrCard: grCardConfig,\n GrCheckbox: grCheckboxConfig,\n GrCheckboxGroup: grCheckboxGroupConfig,\n GrChip: grChipConfig,\n GrChipGroup: grChipGroupConfig,\n GrCodeBlock: grCodeBlockConfig,\n GrCollapse: grCollapseConfig,\n GrColorPicker: grColorPickerConfig,\n GrCommandPalette: grCommandPaletteConfig,\n GrConfigProvider: grConfigProviderConfig,\n GrConfirmDialog: grConfirmDialogConfig,\n GrContextMenu: grContextMenuConfig,\n GrDataTable: grDataTableConfig,\n GrDelta: grDeltaConfig,\n GrDescriptionList: grDescriptionListConfig,\n GrDialog: grDialogConfig,\n GrDialogService: grDialogServiceConfig,\n GrDivider: grDividerConfig,\n GrDrawer: grDrawerConfig,\n GrDropdown: grDropdownConfig,\n GrDropdownMenu: grDropdownMenuConfig,\n GrEmptyState: grEmptyStateConfig,\n GrFilePreview: grFilePreviewConfig,\n GrFileUpload: grFileUploadConfig,\n GrForm: grFormConfig,\n GrFormField: grFormFieldConfig,\n GrFormFile: grFormFileConfig,\n GrFormSection: grFormSectionConfig,\n GrIcon: grIconConfig,\n GrImageViewer: grImageViewerConfig,\n GrInput: grInputConfig,\n GrInputTag: grInputTagConfig,\n GrJsonViewer: grJsonViewerConfig,\n GrKbd: grKbdConfig,\n GrLink: grLinkConfig,\n GrList: grListConfig,\n GrLoading: grLoadingConfig,\n GrModal: grModalConfig,\n GrNavbar: grNavbarConfig,\n GrNumberInput: grNumberInputConfig,\n GrPagination: grPaginationConfig,\n GrPopover: grPopoverConfig,\n GrProgressBar: grProgressBarConfig,\n GrProgressCircle: grProgressCircleConfig,\n GrPromptDialog: grPromptDialogConfig,\n GrRadio: grRadioConfig,\n GrRadioGroup: grRadioGroupConfig,\n GrRating: grRatingConfig,\n GrResponseErrorBanner: grResponseErrorBannerConfig,\n GrSegmented: grSegmentedConfig,\n GrSelect: grSelectConfig,\n GrSidebar: grSidebarConfig,\n GrSkeleton: grSkeletonConfig,\n GrSlider: grSliderConfig,\n GrSortableList: grSortableListConfig,\n GrSplitter: grSplitterConfig,\n GrStatistic: grStatisticConfig,\n GrSteps: grStepsConfig,\n GrSwitch: grSwitchConfig,\n GrTable: grTableConfig,\n GrTabPanels: grTabPanelsConfig,\n GrTabs: grTabsConfig,\n GrTextarea: grTextareaConfig,\n GrTimeline: grTimelineConfig,\n GrToaster: grToasterConfig,\n GrTooltip: grTooltipConfig,\n GrTree: grTreeConfig,\n GrTreeSelect: grTreeSelectConfig,\n GrValue: grValueConfig,\n // </granularity:components:registry>\n}\n\nexport type GranularityComponentName = keyof typeof granularityComponentConfigs\n\n/** Базовый набор компонентов в порядке реестра. */\nconst baseComponents: readonly GranularComponentDescriptor[] = Object.values(\n granularityComponentConfigs,\n)\n\n/**\n * Собирает granular-provider пакета.\n *\n * `overrides` — точка расширения для потребителя: дескриптор с именем из\n * базового реестра заменяет его, остальные дописываются в конец.\n */\nexport function createGranularityProvider(\n overrides: readonly GranularComponentDescriptor[] = [],\n): GranularProvider {\n const overrideByName = new Map(\n overrides.map(component => [component.name, component]),\n )\n const components: GranularComponentDescriptor[] = [\n ...baseComponents.map(component => overrideByName.get(component.name) ?? component),\n ...overrides.filter(component => !baseComponents.some(base => base.name === component.name)),\n ]\n\n return defineGranularProvider({\n id: GRANULARITY_PROVIDER_ID,\n contractVersion: 1,\n packageBaseUrl,\n components,\n theme,\n })\n}\n","import { createGranularityProvider } from './shared'\n\nexport * from './shared'\n\nexport const granularityProvider = createGranularityProvider()\n\nexport default granularityProvider\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAa,iBAAiB,wBAAwB,OAAO,KAAK,KAAK;CACrE,MAAM;CACN,cAAc,CAAC,YAAY,SAAS;CACpC,UAAU;AACZ,CAAC;;;ACND,IAAa,8BAA8B,wBAAwB,OAAO,KAAK,KAAK;CAClF,MAAM;CACN,cAAc,CAAC,WAAW,UAAU;AACtC,CAAC;;;;AC4FD,IAAa,0BAA0B;AAIvC,IAAM,iBAAiB,sBAAsB,OAAO,KAAK,GAAG;;AAG5D,IAAa,wBAAwB,CAAC,SAAS,MAAM;;AAIrD,IAAa,2BAA4D,CAAC,OAAO;AAEjF,IAAM,QAAQ;CACZ,YAAY,IAAA,IAAA,q5KAAA,KAAA,OAAA,KAAA,GAAA,CAA8C,CAAC;CAC3D,cAAc,IAAA,IAAA,65YAAA,KAAA,OAAA,KAAA,GAAA,CAAgD,CAAC;CAC/D,QAAQ;EACN,OAAO,IAAA,IAAA,iyYAAA,KAAA,OAAA,KAAA,GAAA,CAAsD,CAAC;EAC9D,MAAM,IAAA,IAAA,y/SAAA,KAAA,OAAA,KAAA,GAAA,CAAqD,CAAC;CAC9D;CACA,eAAe;AACjB;;;;;AAMA,IAAa,8BAA8B;CAEzC,SAAS;CACT,gBAAgB;CAChB,UAAU;CACV,SAAS;CACT,aAAa;CACb,aAAa;CACb,eAAe;CACf,UAAU;CACV,eAAe;CACf,QAAQ;CACR,YAAY;CACZ,iBAAiB;CACjB,QAAQ;CACR,aAAa;CACb,aAAa;CACb,YAAY;CACZ,eAAe;CACf,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,aAAa;CACb,SAAS;CACT,mBAAmB;CACnB,UAAU;CACV,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,cAAc;CACd,QAAQ;CACR,aAAa;CACb,YAAY;CACZ,eAAe;CACf,QAAQ;CACR,eAAe;CACf,SAAS;CACT,YAAY;CACZ,cAAc;CACd,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,WAAW;CACX,SAAS;CACT,UAAU;CACV,eAAe;CACf,cAAc;CACd,WAAW;CACX,eAAe;CACf,kBAAkB;CAClB,gBAAgB;CAChB,SAAS;CACT,cAAc;CACd,UAAU;CACV,uBAAuB;CACvB,aAAa;CACb,UAAU;CACV,WAAW;CACX,YAAY;CACZ,UAAU;CACV,gBAAgB;CAChB,YAAY;CACZ,aAAa;CACb,SAAS;CACT,UAAU;CACV,SAAS;CACT,aAAa;CACb,QAAQ;CACR,YAAY;CACZ,YAAY;CACZ,WAAW;CACX,WAAW;CACX,QAAQ;CACR,cAAc;CACd,SAAS;AAEX;;AAKA,IAAM,iBAAyD,OAAO,OACpE,2BACF;;;;;;;AAQA,SAAgB,0BACd,YAAoD,CAAC,GACnC;CAClB,MAAM,iBAAiB,IAAI,IACzB,UAAU,KAAI,cAAa,CAAC,UAAU,MAAM,SAAS,CAAC,CACxD;CAMA,OAAO,uBAAuB;EAC5B,IAAI;EACJ,iBAAiB;EACjB;EACA,aARA,GAAG,eAAe,KAAI,cAAa,eAAe,IAAI,UAAU,IAAI,KAAK,SAAS,GAClF,GAAG,UAAU,QAAO,cAAa,CAAC,eAAe,MAAK,SAAQ,KAAK,SAAS,UAAU,IAAI,CAAC,CAO3F;EACA;CACF,CAAC;AACH;;;AC1OA,IAAa,sBAAsB,0BAA0B"}
|
|
@@ -33,4 +33,27 @@ var panelPopTransition = {
|
|
|
33
33
|
leaveTo: "transform opacity-0 scale-95"
|
|
34
34
|
};
|
|
35
35
|
//#endregion
|
|
36
|
-
|
|
36
|
+
//#region src/components/shared/overlayPanelSurface.ts
|
|
37
|
+
/**
|
|
38
|
+
* Общая поверхность всплывающей панели: радиус, рамка, фон, тень.
|
|
39
|
+
*
|
|
40
|
+
* До выноса шесть этих утилит были скопированы пять раз — у поповера,
|
|
41
|
+
* выпадающего меню и трёх списков выбора. Расходились бы молча: разница в одном
|
|
42
|
+
* токене видна только рядом с эталоном, а эталоны у компонентов свои.
|
|
43
|
+
*
|
|
44
|
+
* Фон — `--gr-popover`, и это не выбор из двух равных. Описание токена в
|
|
45
|
+
* `tokens/themes/*.json` — «Фон popover-, dropdown- и overlay-поверхностей», то
|
|
46
|
+
* есть он заведён ровно под этот случай; `--gr-card` — фон карточки. Четверо из
|
|
47
|
+
* пяти брали карточный, и значения совпадали, поэтому расхождение не
|
|
48
|
+
* проявлялось. Проявилось бы у первого, кто перекрасит `--gr-popover`, чтобы
|
|
49
|
+
* отличать всплывающие поверхности от карточек.
|
|
50
|
+
*
|
|
51
|
+
* Всё, что специфично для компонента, — поле, потолок ширины, `overflow` — он
|
|
52
|
+
* дописывает сам: это уже не поверхность, а его собственная раскладка.
|
|
53
|
+
*
|
|
54
|
+
* Модуль безадресный: в `dist` он уезжает в общий чанк, который пресет не
|
|
55
|
+
* сканирует, поэтому каждый потребитель объявляет эти классы в своём `safelist.ts`.
|
|
56
|
+
*/
|
|
57
|
+
var overlayPanelSurfaceClass = "rounded-[var(--gr-radius-xl)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-2)]";
|
|
58
|
+
//#endregion
|
|
59
|
+
export { panelPopTransition as n, overlayPanelSurfaceClass as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlayPanelSurface-BKeMhRx4.js","names":[],"sources":["../../src/components/shared/overlayTransition.ts","../../src/components/shared/overlayPanelSurface.ts"],"sourcesContent":["/**\n * Набор перехода всплывающей панели.\n *\n * Пять панелей — выпадающее меню, поповер, списки выбора — появляются\n * одинаково, и до выноса держали этот набор литералами в своих шаблонах:\n * шесть строк, повторённых дословно пять раз. Разъезжались они молча, потому\n * что визуально расхождение в одной фазе заметно только рядом с эталоном.\n *\n * Форма — объект по фазам, как `overlayTransition`/`panelTransition` у\n * `GrModal`: так набор биндится в `<transition>` по одному имени на фазу и\n * разворачивается в safelist одним вызовом, не превращаясь в «мешок токенов».\n *\n * Два правила, оба следуют из того, что модуль безадресный (в `dist` он лежит\n * в общем чанке, который пресет не сканирует):\n *\n * 1. **Каждый потребитель обязан объявить эти классы в своём `safelist.ts`** —\n * через `flattenTransitionTokens`. Гейт `src/__tests__/safelist.test.ts`\n * проверяет это обходом импортов; забывшего он назовёт поимённо.\n * 2. **Один пресет — один файл.** Второй набор, положенный сюда же, обложил бы\n * налогом потребителей первого: safelist считается по модулю целиком, и\n * `GrDropdown` понёс бы классы, которых не рендерит.\n *\n * Набор с единственным потребителем сюда не переезжает вовсе — литералы в его\n * `.vue` находит скан, и это дешевле. Поэтому здесь нет фейда `GrTooltip`.\n */\nexport const panelPopTransition = {\n enter: 'transition ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]',\n enterFrom: 'transform opacity-0 scale-95',\n enterTo: 'transform opacity-100 scale-100',\n leave: 'transition ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]',\n leaveFrom: 'transform opacity-100 scale-100',\n leaveTo: 'transform opacity-0 scale-95',\n} as const\n","/**\n * Общая поверхность всплывающей панели: радиус, рамка, фон, тень.\n *\n * До выноса шесть этих утилит были скопированы пять раз — у поповера,\n * выпадающего меню и трёх списков выбора. Расходились бы молча: разница в одном\n * токене видна только рядом с эталоном, а эталоны у компонентов свои.\n *\n * Фон — `--gr-popover`, и это не выбор из двух равных. Описание токена в\n * `tokens/themes/*.json` — «Фон popover-, dropdown- и overlay-поверхностей», то\n * есть он заведён ровно под этот случай; `--gr-card` — фон карточки. Четверо из\n * пяти брали карточный, и значения совпадали, поэтому расхождение не\n * проявлялось. Проявилось бы у первого, кто перекрасит `--gr-popover`, чтобы\n * отличать всплывающие поверхности от карточек.\n *\n * Всё, что специфично для компонента, — поле, потолок ширины, `overflow` — он\n * дописывает сам: это уже не поверхность, а его собственная раскладка.\n *\n * Модуль безадресный: в `dist` он уезжает в общий чанк, который пресет не\n * сканирует, поэтому каждый потребитель объявляет эти классы в своём `safelist.ts`.\n */\nexport const overlayPanelSurfaceClass = 'rounded-[var(--gr-radius-xl)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-2)]'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,qBAAqB;CAChC,OAAO;CACP,WAAW;CACX,SAAS;CACT,OAAO;CACP,WAAW;CACX,SAAS;AACX;;;;;;;;;;;;;;;;;;;;;;;ACZA,IAAa,2BAA2B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nextTick, onScopeDispose, watch } from "vue";
|
|
1
2
|
//#region src/composables/internal/focusables.ts
|
|
2
3
|
/**
|
|
3
4
|
* Что пакет считает фокусируемым.
|
|
@@ -84,4 +85,125 @@ function getFocusableElementsIn(roots) {
|
|
|
84
85
|
});
|
|
85
86
|
}
|
|
86
87
|
//#endregion
|
|
87
|
-
|
|
88
|
+
//#region src/composables/useFocusTrap.ts
|
|
89
|
+
/**
|
|
90
|
+
* Ловушка фокуса: пока активна, Tab ходит по кругу внутри слоя, а фокус,
|
|
91
|
+
* утёкший наружу, возвращается обратно.
|
|
92
|
+
*
|
|
93
|
+
* Реализована на `keydown` и `focusin`, **без sentinel-узлов**: распространённый
|
|
94
|
+
* подход с двумя focus-guard кнопками по краям кладёт в разметку интерактивные
|
|
95
|
+
* элементы, а внутрь контейнера с ARIA-ролью их класть нельзя — роль объявляет
|
|
96
|
+
* потомков презентационными (axe: `nested-interactive`).
|
|
97
|
+
*
|
|
98
|
+
* Слой из ловушки не выводится: порядок окон, Esc и `inert` — забота
|
|
99
|
+
* `useOverlayLayer`. Композиция такая:
|
|
100
|
+
*
|
|
101
|
+
* ```ts
|
|
102
|
+
* const layer = useOverlayLayer(open, close, { modal: true, root: panelRef })
|
|
103
|
+
* useFocusTrap(panelRef, {
|
|
104
|
+
* active: () => open.value && isTopmost.value,
|
|
105
|
+
* containers: layer.rootsAbove,
|
|
106
|
+
* })
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
function useFocusTrap(container, options = {}) {
|
|
110
|
+
let previouslyFocused = null;
|
|
111
|
+
let listening = false;
|
|
112
|
+
let lastFocusedInside = null;
|
|
113
|
+
function roots() {
|
|
114
|
+
const extra = options.containers?.() ?? [];
|
|
115
|
+
return [container.value, ...extra].filter((root) => Boolean(root));
|
|
116
|
+
}
|
|
117
|
+
function contains(target) {
|
|
118
|
+
if (!target) return false;
|
|
119
|
+
return roots().some((root) => root === target || root.contains(target));
|
|
120
|
+
}
|
|
121
|
+
function focusables() {
|
|
122
|
+
return getFocusableElementsIn(roots());
|
|
123
|
+
}
|
|
124
|
+
function focusInitial() {
|
|
125
|
+
if (contains(document.activeElement)) return;
|
|
126
|
+
const requested = options.initialFocus?.();
|
|
127
|
+
if (requested && isFocusable(requested)) {
|
|
128
|
+
requested.focus();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const [first] = focusables();
|
|
132
|
+
if (first) {
|
|
133
|
+
first.focus();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
(options.fallbackFocus?.() ?? container.value)?.focus();
|
|
137
|
+
}
|
|
138
|
+
function onKeydown(event) {
|
|
139
|
+
if (event.key !== "Tab") return;
|
|
140
|
+
if (!contains(document.activeElement)) return;
|
|
141
|
+
const items = focusables();
|
|
142
|
+
if (items.length === 0) {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
(options.fallbackFocus?.() ?? container.value)?.focus();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const first = items[0];
|
|
148
|
+
const last = items[items.length - 1];
|
|
149
|
+
const active = document.activeElement;
|
|
150
|
+
if ((active ? items.indexOf(active) : -1) === -1) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
(event.shiftKey ? last : first).focus();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (event.shiftKey && active === first) {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
last.focus();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (!event.shiftKey && active === last) {
|
|
161
|
+
event.preventDefault();
|
|
162
|
+
first.focus();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function onFocusIn(event) {
|
|
166
|
+
const target = event.target;
|
|
167
|
+
if (contains(target)) {
|
|
168
|
+
lastFocusedInside = target;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (lastFocusedInside && contains(lastFocusedInside) && isFocusable(lastFocusedInside)) {
|
|
172
|
+
lastFocusedInside.focus();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
focusInitial();
|
|
176
|
+
}
|
|
177
|
+
function activate() {
|
|
178
|
+
if (listening) return;
|
|
179
|
+
if (typeof document === "undefined") return;
|
|
180
|
+
listening = true;
|
|
181
|
+
previouslyFocused = document.activeElement ?? null;
|
|
182
|
+
lastFocusedInside = null;
|
|
183
|
+
document.addEventListener("keydown", onKeydown, true);
|
|
184
|
+
document.addEventListener("focusin", onFocusIn, true);
|
|
185
|
+
nextTick(() => {
|
|
186
|
+
if (!listening) return;
|
|
187
|
+
focusInitial();
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function deactivate() {
|
|
191
|
+
if (!listening) return;
|
|
192
|
+
listening = false;
|
|
193
|
+
document.removeEventListener("keydown", onKeydown, true);
|
|
194
|
+
document.removeEventListener("focusin", onFocusIn, true);
|
|
195
|
+
const target = previouslyFocused;
|
|
196
|
+
previouslyFocused = null;
|
|
197
|
+
lastFocusedInside = null;
|
|
198
|
+
if (options.restoreFocus === false || !target) return;
|
|
199
|
+
if (!target.isConnected) return;
|
|
200
|
+
target.focus?.();
|
|
201
|
+
}
|
|
202
|
+
watch(() => Boolean(options.active?.() ?? true) && Boolean(container.value), (isActive) => {
|
|
203
|
+
if (isActive) activate();
|
|
204
|
+
else deactivate();
|
|
205
|
+
}, { immediate: true });
|
|
206
|
+
onScopeDispose(deactivate);
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
export { getFocusableElements as n, useFocusTrap as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFocusTrap-D9bIPNF6.js","names":[],"sources":["../../src/composables/internal/focusables.ts","../../src/composables/useFocusTrap.ts"],"sourcesContent":["/**\n * Что пакет считает фокусируемым.\n *\n * Один источник правды на ловушку фокуса, roving-навигацию и «первый элемент\n * панели». До него каждый компонент собирал свой селектор: у `GrDropdown` в\n * список не попадали `select` и `textarea`, а фильтр видимости стоял на\n * `offsetParent !== null` — он врёт на `position: fixed` (у таких элементов\n * `offsetParent` всегда `null`), то есть на панелях самих оверлеев.\n */\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'iframe',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n '[tabindex]',\n].join(',')\n\n/**\n * Видимость.\n *\n * В браузере спрашиваем `checkVisibility()` — он единственный учитывает скрытых\n * предков, `content-visibility` и `display: contents`. В jsdom его нет и\n * раскладки тоже нет, поэтому там остаётся проверка собственных стилей: этого\n * достаточно, чтобы тесты видели ту же логику, а не пустой список.\n */\nfunction isVisible(element: HTMLElement): boolean {\n if (element.hidden)\n return false\n\n if (typeof element.checkVisibility === 'function')\n return element.checkVisibility({ contentVisibilityAuto: true, visibilityProperty: true })\n\n const style = element.ownerDocument.defaultView?.getComputedStyle(element)\n if (!style)\n return true\n return style.display !== 'none' && style.visibility !== 'hidden'\n}\n\n/** Элемент внутри `inert`-поддерева не фокусируем — браузер его не отдаёт. */\nfunction isInert(element: HTMLElement): boolean {\n return element.closest('[inert]') !== null\n}\n\nexport function isFocusable(element: HTMLElement): boolean {\n if (!element.matches(FOCUSABLE_SELECTOR))\n return false\n if (element.hasAttribute('disabled'))\n return false\n if (element.getAttribute('aria-hidden') === 'true')\n return false\n return !isInert(element) && isVisible(element)\n}\n\n/** Участвует ли элемент в таб-порядке (`tabindex=\"-1\"` — нет). */\nexport function isTabbable(element: HTMLElement): boolean {\n if (!isFocusable(element))\n return false\n return Number.parseInt(element.getAttribute('tabindex') ?? '0', 10) >= 0\n}\n\n/**\n * Фокусируемые внутри корня, в порядке документа.\n *\n * `tabbableOnly` (по умолчанию) оставляет только участников таб-порядка — это\n * то, что нужно циклу Tab. Полный список нужен «первому фокусируемому» при\n * открытии: панель с `tabindex=\"-1\"` фокусируется программно.\n */\nexport function getFocusableElements(\n root: HTMLElement | null | undefined,\n options: { tabbableOnly?: boolean } = {},\n): HTMLElement[] {\n if (!root)\n return []\n\n const tabbableOnly = options.tabbableOnly ?? true\n const matches = Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))\n\n return matches.filter(element => (tabbableOnly ? isTabbable(element) : isFocusable(element)))\n}\n\n/**\n * Фокусируемые сразу в нескольких корнях, в порядке документа.\n *\n * Нужен ловушке: панель селекта, открытого внутри модалки, телепортирована в\n * `body` и лежит вне DOM-поддерева окна, но для пользователя это продолжение\n * того же слоя.\n */\nexport function getFocusableElementsIn(roots: (HTMLElement | null | undefined)[]): HTMLElement[] {\n const unique = new Set<HTMLElement>()\n\n for (const root of roots) {\n for (const element of getFocusableElements(root)) unique.add(element)\n }\n\n return Array.from(unique).sort((left, right) => {\n const position = left.compareDocumentPosition(right)\n if (position & Node.DOCUMENT_POSITION_FOLLOWING)\n return -1\n if (position & Node.DOCUMENT_POSITION_PRECEDING)\n return 1\n return 0\n })\n}\n","import type { Ref } from 'vue'\nimport { nextTick, onScopeDispose, watch } from 'vue'\n\nimport { getFocusableElementsIn, isFocusable } from './internal/focusables'\n\nexport interface UseFocusTrapOptions {\n /**\n * Работает ли ловушка сейчас. Геттер, а не значение: модальное окно держит\n * фокус, только пока оно верхнее в стеке слоёв, — иначе нижнее окно отбирало\n * бы фокус у открытого поверх него.\n */\n active?: () => boolean\n /** Кому отдать фокус при активации. Не задан или не фокусируем — первый в контейнере. */\n initialFocus?: () => HTMLElement | null | undefined\n /**\n * Запасной приёмник фокуса, когда фокусировать внутри нечего: обычно сама\n * панель с `tabindex=\"-1\"`. Без него фокус остался бы снаружи, и Tab увёл бы\n * пользователя на страницу под окном.\n */\n fallbackFocus?: () => HTMLElement | null | undefined\n /**\n * Дополнительные корни, которые считаются частью слоя. Панель селекта,\n * открытого внутри окна, телепортирована в `body` и лежит вне поддерева\n * контейнера — без этого списка ловушка отбирала бы у неё фокус.\n */\n containers?: () => (HTMLElement | null | undefined)[]\n /**\n * Возвращать фокус туда, где он был до активации. По умолчанию — да.\n * Оверлеи пакета выключают: возврат делает стек слоёв по более строгому\n * правилу (только если фокус на момент закрытия ещё внутри слоя).\n */\n restoreFocus?: boolean\n}\n\n/**\n * Ловушка фокуса: пока активна, Tab ходит по кругу внутри слоя, а фокус,\n * утёкший наружу, возвращается обратно.\n *\n * Реализована на `keydown` и `focusin`, **без sentinel-узлов**: распространённый\n * подход с двумя focus-guard кнопками по краям кладёт в разметку интерактивные\n * элементы, а внутрь контейнера с ARIA-ролью их класть нельзя — роль объявляет\n * потомков презентационными (axe: `nested-interactive`).\n *\n * Слой из ловушки не выводится: порядок окон, Esc и `inert` — забота\n * `useOverlayLayer`. Композиция такая:\n *\n * ```ts\n * const layer = useOverlayLayer(open, close, { modal: true, root: panelRef })\n * useFocusTrap(panelRef, {\n * active: () => open.value && isTopmost.value,\n * containers: layer.rootsAbove,\n * })\n * ```\n */\nexport function useFocusTrap(\n container: Ref<HTMLElement | null>,\n options: UseFocusTrapOptions = {},\n): void {\n let previouslyFocused: HTMLElement | null = null\n let listening = false\n // Последний законный держатель фокуса: с него начинается возврат, когда\n // фокус ушёл в никуда (например, кликом по неинтерактивному фону).\n let lastFocusedInside: HTMLElement | null = null\n\n function roots(): HTMLElement[] {\n const extra = options.containers?.() ?? []\n return [container.value, ...extra].filter((root): root is HTMLElement => Boolean(root))\n }\n\n function contains(target: Node | null): boolean {\n if (!target)\n return false\n return roots().some(root => root === target || root.contains(target))\n }\n\n function focusables(): HTMLElement[] {\n return getFocusableElementsIn(roots())\n }\n\n function focusInitial(): void {\n // Фокус уже внутри слоя — его поставили осознанно: диалог наводит на свою\n // кнопку, окно ввода — на поле, и делают это в тот же такт, когда панель\n // появилась. `initialFocus` — дефолт слоя, а не приказ, поэтому такую\n // установку он не перебивает.\n if (contains(document.activeElement))\n return\n\n const requested = options.initialFocus?.()\n if (requested && isFocusable(requested)) {\n requested.focus()\n return\n }\n\n const [first] = focusables()\n if (first) {\n first.focus()\n return\n }\n\n // Фокусировать нечего — забираем фокус на сам слой, иначе он останется\n // снаружи и Tab уведёт пользователя под окно.\n const fallback = options.fallbackFocus?.() ?? container.value\n fallback?.focus()\n }\n\n function onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Tab')\n return\n if (!contains(document.activeElement))\n return\n\n const items = focusables()\n if (items.length === 0) {\n // Внутри некуда идти — держим фокус на слое, а не отпускаем на страницу.\n event.preventDefault()\n ;(options.fallbackFocus?.() ?? container.value)?.focus()\n return\n }\n\n const first = items[0]\n const last = items[items.length - 1]\n const active = document.activeElement as HTMLElement | null\n const index = active ? items.indexOf(active) : -1\n\n // Фокус на самой панели (`tabindex=\"-1\"`, в списке её нет): Tab обязан\n // войти внутрь, а не выпрыгнуть за слой.\n if (index === -1) {\n event.preventDefault()\n ;(event.shiftKey ? last : first).focus()\n return\n }\n\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last.focus()\n return\n }\n\n if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n function onFocusIn(event: FocusEvent): void {\n const target = event.target as HTMLElement | null\n\n if (contains(target)) {\n lastFocusedInside = target\n return\n }\n\n // Фокус утёк наружу — программным `focus()`, кликом по фону или обходом\n // через адресную строку. Возвращаем его на последнее законное место.\n if (lastFocusedInside && contains(lastFocusedInside) && isFocusable(lastFocusedInside)) {\n lastFocusedInside.focus()\n return\n }\n\n focusInitial()\n }\n\n function activate(): void {\n if (listening)\n return\n if (typeof document === 'undefined')\n return\n listening = true\n\n previouslyFocused = (document.activeElement as HTMLElement) ?? null\n lastFocusedInside = null\n\n document.addEventListener('keydown', onKeydown, true)\n document.addEventListener('focusin', onFocusIn, true)\n\n // Ждём, пока Vue домонтирует содержимое слоя: до этого фокусировать нечего.\n void nextTick(() => {\n if (!listening)\n return\n focusInitial()\n })\n }\n\n function deactivate(): void {\n if (!listening)\n return\n listening = false\n\n document.removeEventListener('keydown', onKeydown, true)\n document.removeEventListener('focusin', onFocusIn, true)\n\n const target = previouslyFocused\n previouslyFocused = null\n lastFocusedInside = null\n\n if (options.restoreFocus === false || !target)\n return\n if (!target.isConnected)\n return\n target.focus?.()\n }\n\n watch(\n () => Boolean(options.active?.() ?? true) && Boolean(container.value),\n (isActive) => {\n if (isActive)\n activate()\n else deactivate()\n },\n { immediate: true },\n )\n\n onScopeDispose(deactivate)\n}\n"],"mappings":";;;;;;;;;;;AAUA,IAAM,qBAAqB;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG;;;;;;;;;AAUV,SAAS,UAAU,SAA+B;CAChD,IAAI,QAAQ,QACV,OAAO;CAET,IAAI,OAAO,QAAQ,oBAAoB,YACrC,OAAO,QAAQ,gBAAgB;EAAE,uBAAuB;EAAM,oBAAoB;CAAK,CAAC;CAE1F,MAAM,QAAQ,QAAQ,cAAc,aAAa,iBAAiB,OAAO;CACzE,IAAI,CAAC,OACH,OAAO;CACT,OAAO,MAAM,YAAY,UAAU,MAAM,eAAe;AAC1D;;AAGA,SAAS,QAAQ,SAA+B;CAC9C,OAAO,QAAQ,QAAQ,SAAS,MAAM;AACxC;AAEA,SAAgB,YAAY,SAA+B;CACzD,IAAI,CAAC,QAAQ,QAAQ,kBAAkB,GACrC,OAAO;CACT,IAAI,QAAQ,aAAa,UAAU,GACjC,OAAO;CACT,IAAI,QAAQ,aAAa,aAAa,MAAM,QAC1C,OAAO;CACT,OAAO,CAAC,QAAQ,OAAO,KAAK,UAAU,OAAO;AAC/C;;AAGA,SAAgB,WAAW,SAA+B;CACxD,IAAI,CAAC,YAAY,OAAO,GACtB,OAAO;CACT,OAAO,OAAO,SAAS,QAAQ,aAAa,UAAU,KAAK,KAAK,EAAE,KAAK;AACzE;;;;;;;;AASA,SAAgB,qBACd,MACA,UAAsC,CAAC,GACxB;CACf,IAAI,CAAC,MACH,OAAO,CAAC;CAEV,MAAM,eAAe,QAAQ,gBAAgB;CAG7C,OAFgB,MAAM,KAAK,KAAK,iBAA8B,kBAAkB,CAEzE,CAAA,CAAQ,QAAO,YAAY,eAAe,WAAW,OAAO,IAAI,YAAY,OAAO,CAAE;AAC9F;;;;;;;;AASA,SAAgB,uBAAuB,OAA0D;CAC/F,MAAM,yBAAS,IAAI,IAAiB;CAEpC,KAAK,MAAM,QAAQ,OACjB,KAAK,MAAM,WAAW,qBAAqB,IAAI,GAAG,OAAO,IAAI,OAAO;CAGtE,OAAO,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,MAAM,UAAU;EAC9C,MAAM,WAAW,KAAK,wBAAwB,KAAK;EACnD,IAAI,WAAW,KAAK,6BAClB,OAAO;EACT,IAAI,WAAW,KAAK,6BAClB,OAAO;EACT,OAAO;CACT,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;ACvDA,SAAgB,aACd,WACA,UAA+B,CAAC,GAC1B;CACN,IAAI,oBAAwC;CAC5C,IAAI,YAAY;CAGhB,IAAI,oBAAwC;CAE5C,SAAS,QAAuB;EAC9B,MAAM,QAAQ,QAAQ,aAAa,KAAK,CAAC;EACzC,OAAO,CAAC,UAAU,OAAO,GAAG,KAAK,CAAC,CAAC,QAAQ,SAA8B,QAAQ,IAAI,CAAC;CACxF;CAEA,SAAS,SAAS,QAA8B;EAC9C,IAAI,CAAC,QACH,OAAO;EACT,OAAO,MAAM,CAAC,CAAC,MAAK,SAAQ,SAAS,UAAU,KAAK,SAAS,MAAM,CAAC;CACtE;CAEA,SAAS,aAA4B;EACnC,OAAO,uBAAuB,MAAM,CAAC;CACvC;CAEA,SAAS,eAAqB;EAK5B,IAAI,SAAS,SAAS,aAAa,GACjC;EAEF,MAAM,YAAY,QAAQ,eAAe;EACzC,IAAI,aAAa,YAAY,SAAS,GAAG;GACvC,UAAU,MAAM;GAChB;EACF;EAEA,MAAM,CAAC,SAAS,WAAW;EAC3B,IAAI,OAAO;GACT,MAAM,MAAM;GACZ;EACF;EAKA,CADiB,QAAQ,gBAAgB,KAAK,UAAU,MAAA,EAC9C,MAAM;CAClB;CAEA,SAAS,UAAU,OAA4B;EAC7C,IAAI,MAAM,QAAQ,OAChB;EACF,IAAI,CAAC,SAAS,SAAS,aAAa,GAClC;EAEF,MAAM,QAAQ,WAAW;EACzB,IAAI,MAAM,WAAW,GAAG;GAEtB,MAAM,eAAe;GACpB,CAAC,QAAQ,gBAAgB,KAAK,UAAU,MAAA,EAAQ,MAAM;GACvD;EACF;EAEA,MAAM,QAAQ,MAAM;EACpB,MAAM,OAAO,MAAM,MAAM,SAAS;EAClC,MAAM,SAAS,SAAS;EAKxB,KAJc,SAAS,MAAM,QAAQ,MAAM,IAAI,QAIjC,IAAI;GAChB,MAAM,eAAe;GACpB,CAAC,MAAM,WAAW,OAAO,MAAA,CAAO,MAAM;GACvC;EACF;EAEA,IAAI,MAAM,YAAY,WAAW,OAAO;GACtC,MAAM,eAAe;GACrB,KAAK,MAAM;GACX;EACF;EAEA,IAAI,CAAC,MAAM,YAAY,WAAW,MAAM;GACtC,MAAM,eAAe;GACrB,MAAM,MAAM;EACd;CACF;CAEA,SAAS,UAAU,OAAyB;EAC1C,MAAM,SAAS,MAAM;EAErB,IAAI,SAAS,MAAM,GAAG;GACpB,oBAAoB;GACpB;EACF;EAIA,IAAI,qBAAqB,SAAS,iBAAiB,KAAK,YAAY,iBAAiB,GAAG;GACtF,kBAAkB,MAAM;GACxB;EACF;EAEA,aAAa;CACf;CAEA,SAAS,WAAiB;EACxB,IAAI,WACF;EACF,IAAI,OAAO,aAAa,aACtB;EACF,YAAY;EAEZ,oBAAqB,SAAS,iBAAiC;EAC/D,oBAAoB;EAEpB,SAAS,iBAAiB,WAAW,WAAW,IAAI;EACpD,SAAS,iBAAiB,WAAW,WAAW,IAAI;EAGpD,eAAoB;GAClB,IAAI,CAAC,WACH;GACF,aAAa;EACf,CAAC;CACH;CAEA,SAAS,aAAmB;EAC1B,IAAI,CAAC,WACH;EACF,YAAY;EAEZ,SAAS,oBAAoB,WAAW,WAAW,IAAI;EACvD,SAAS,oBAAoB,WAAW,WAAW,IAAI;EAEvD,MAAM,SAAS;EACf,oBAAoB;EACpB,oBAAoB;EAEpB,IAAI,QAAQ,iBAAiB,SAAS,CAAC,QACrC;EACF,IAAI,CAAC,OAAO,aACV;EACF,OAAO,QAAQ;CACjB;CAEA,YACQ,QAAQ,QAAQ,SAAS,KAAK,IAAI,KAAK,QAAQ,UAAU,KAAK,IACnE,aAAa;EACZ,IAAI,UACF,SAAS;OACN,WAAW;CAClB,GACA,EAAE,WAAW,KAAK,CACpB;CAEA,eAAe,UAAU;AAC3B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as isEditableTarget } from "./keyboard-DE2yhz_b.js";
|
|
2
|
-
import {
|
|
2
|
+
import { n as getFocusableElements } from "./useFocusTrap-D9bIPNF6.js";
|
|
3
3
|
import { t as useTypeahead } from "./useTypeahead-mxPtPBf2.js";
|
|
4
4
|
//#region src/composables/internal/useMenuItemsFocus.ts
|
|
5
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMenuItemsFocus-
|
|
1
|
+
{"version":3,"file":"useMenuItemsFocus-B7Bl_aFA.js","names":[],"sources":["../../src/composables/internal/useMenuItemsFocus.ts"],"sourcesContent":["import { isEditableTarget } from '../../internal/keyboard'\n\nimport { getFocusableElements } from './focusables'\nimport { useTypeahead } from './useTypeahead'\n\nexport interface UseMenuItemsFocusOptions {\n /** Корень, внутри которого живут пункты. */\n container: () => HTMLElement | null\n /** Закрыть меню — `Tab` уводит фокус наружу, панель при этом закрывается. */\n close: () => void\n}\n\nexport interface UseMenuItemsFocusReturn {\n /** Пункты в порядке документа. */\n items: () => HTMLElement[]\n /** Сфокусировать по индексу; отрицательный и превышающий заворачиваются. */\n focusAt: (index: number) => void\n /** Стрелки, `Home`/`End`, `Tab`, typeahead. `true` — клавиша обработана. */\n onKeydown: (event: KeyboardEvent) => boolean\n reset: () => void\n}\n\n/**\n * Клавиатура панели-меню: кольцо фокуса плюс поиск по первым буквам.\n *\n * Пункты ищутся в DOM, а не в модели, и это осознанно: у панели бывает\n * произвольное содержимое из слота, в котором ключей нет вовсе. По той же\n * причине здесь не `useRovingFocus` — он адресуется ключами и ведёт\n * `tabindex=\"0\"` на активном элементе, а пункты меню обязаны быть `-1`.\n */\nexport function useMenuItemsFocus(options: UseMenuItemsFocusOptions): UseMenuItemsFocusReturn {\n /**\n * `tabbableOnly: false` — не оплошность: пункты меню намеренно `tabindex=\"-1\"`,\n * и список участников таб-порядка был бы пуст.\n */\n function items(): HTMLElement[] {\n return getFocusableElements(options.container(), { tabbableOnly: false })\n }\n\n function focusAt(index: number): void {\n const list = items()\n if (list.length === 0)\n return\n\n // `preventScroll` обязателен: панель уже приведена во вьюпорт позиционером,\n // поэтому прокрутка ради фокуса внутри неё паразитная. У контекстного меню\n // она к тому же порождала `scroll`, по которому меню закрывало само себя.\n list[(index + list.length) % list.length]?.focus({ preventScroll: true })\n }\n\n const typeahead = useTypeahead<HTMLElement>({\n items,\n textOf: element => element.textContent ?? '',\n currentIndex: list => list.indexOf(document.activeElement as HTMLElement),\n onMatch: element => element.focus({ preventScroll: true }),\n })\n\n function onKeydown(event: KeyboardEvent): boolean {\n const list = items()\n const currentIndex = list.indexOf(document.activeElement as HTMLElement)\n\n // Панель бывает не только меню из кнопок: с `closeOnContentClick={false}` в\n // ней живут поля и чекбоксы. Клавиши, которые сфокусированный контрол умеет\n // сам, остаются ему — иначе в поле не напечатать, а чекбокс не переключить.\n const editable = isEditableTarget(event.target)\n\n switch (event.key) {\n // Стрелки — исключение: они за меню даже в поле. `Tab` панель закрывает, и\n // без них из поля внутри панели не было бы выхода вовсе.\n case 'ArrowDown':\n event.preventDefault()\n focusAt(currentIndex + 1)\n return true\n case 'ArrowUp':\n event.preventDefault()\n focusAt(currentIndex - 1)\n return true\n case 'Home':\n if (editable)\n return false\n event.preventDefault()\n focusAt(0)\n return true\n case 'End':\n if (editable)\n return false\n event.preventDefault()\n focusAt(-1)\n return true\n case 'Tab':\n options.close()\n return true\n }\n\n if (editable)\n return false\n\n // Пробел при пустом буфере — активация сфокусированного пункта, а не поиск:\n // пункты меню это кнопки, и пробел для них родная клавиша. В буфер он входит,\n // только когда поиск уже идёт (правило typeahead из WAI-ARIA APG).\n if (event.key === ' ' && typeahead.isEmpty())\n return false\n\n // Печатный символ без модификаторов — поиск по пунктам, а не команда.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n event.preventDefault()\n typeahead.type(event.key)\n return true\n }\n\n return false\n }\n\n return { items, focusAt, onKeydown, reset: typeahead.reset }\n}\n"],"mappings":";;;;;;;;;;;;AA8BA,SAAgB,kBAAkB,SAA4D;;;;;CAK5F,SAAS,QAAuB;EAC9B,OAAO,qBAAqB,QAAQ,UAAU,GAAG,EAAE,cAAc,MAAM,CAAC;CAC1E;CAEA,SAAS,QAAQ,OAAqB;EACpC,MAAM,OAAO,MAAM;EACnB,IAAI,KAAK,WAAW,GAClB;EAKF,MAAM,QAAQ,KAAK,UAAU,KAAK,OAAO,EAAE,MAAM,EAAE,eAAe,KAAK,CAAC;CAC1E;CAEA,MAAM,YAAY,aAA0B;EAC1C;EACA,SAAQ,YAAW,QAAQ,eAAe;EAC1C,eAAc,SAAQ,KAAK,QAAQ,SAAS,aAA4B;EACxE,UAAS,YAAW,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;CAC3D,CAAC;CAED,SAAS,UAAU,OAA+B;EAEhD,MAAM,eADO,MACQ,CAAA,CAAK,QAAQ,SAAS,aAA4B;EAKvE,MAAM,WAAW,iBAAiB,MAAM,MAAM;EAE9C,QAAQ,MAAM,KAAd;GAGE,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,MAAM,eAAe;IACrB,QAAQ,eAAe,CAAC;IACxB,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,CAAC;IACT,OAAO;GACT,KAAK;IACH,IAAI,UACF,OAAO;IACT,MAAM,eAAe;IACrB,QAAQ,EAAE;IACV,OAAO;GACT,KAAK;IACH,QAAQ,MAAM;IACd,OAAO;EACX;EAEA,IAAI,UACF,OAAO;EAKT,IAAI,MAAM,QAAQ,OAAO,UAAU,QAAQ,GACzC,OAAO;EAGT,IAAI,MAAM,IAAI,WAAW,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,CAAC,MAAM,QAAQ;GAC/E,MAAM,eAAe;GACrB,UAAU,KAAK,MAAM,GAAG;GACxB,OAAO;EACT;EAEA,OAAO;CACT;CAEA,OAAO;EAAE;EAAO;EAAS;EAAW,OAAO,UAAU;CAAM;AAC7D"}
|
|
@@ -3,7 +3,7 @@ import { t as usePortalTarget } from "./usePortalTarget-C0p-mhaM.js";
|
|
|
3
3
|
import { n as markInert, t as inertableOutside } from "./inert-DIHCCn_1.js";
|
|
4
4
|
import { r as modalLayerZIndex } from "./overlayStack-0CZS99Da.js";
|
|
5
5
|
import { useOverlayLayer } from "../composables/useOverlayLayer.js";
|
|
6
|
-
import { useFocusTrap } from "
|
|
6
|
+
import { t as useFocusTrap } from "./useFocusTrap-D9bIPNF6.js";
|
|
7
7
|
import { computed, onBeforeUnmount, onScopeDispose, ref, watch } from "vue";
|
|
8
8
|
//#region src/composables/internal/useInertOthers.ts
|
|
9
9
|
/**
|