@feugene/granularity 0.21.0 → 0.22.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-BTCOJO5z2.js.map +1 -0
- package/dist/chunks/config-B_qi4Fma.js.map +1 -0
- package/dist/chunks/config-Baj7Wvx-.js.map +1 -0
- package/dist/chunks/{config-BKvX0q10.js → config-C5mO5DYZ.js} +10 -1
- package/dist/chunks/config-C5mO5DYZ.js.map +1 -0
- package/dist/chunks/config-CIcynHGr.js +96 -0
- package/dist/chunks/config-CIcynHGr.js.map +1 -0
- package/dist/chunks/{config-BsuDM3Hb.js → config-CKULiuvc.js} +15 -1
- package/dist/chunks/config-CKULiuvc.js.map +1 -0
- package/dist/chunks/config-QaIc_gRa.js.map +1 -0
- package/dist/chunks/config-eXB87DOi.js.map +1 -0
- package/dist/chunks/copy-DChChcq-.js +29 -0
- package/dist/chunks/copy-DChChcq-.js.map +1 -0
- package/dist/chunks/{en-CwAr3C_x.js → en-DCCFP4qu.js} +12 -2
- package/dist/chunks/{en-CwAr3C_x.js.map → en-DCCFP4qu.js.map} +1 -1
- package/dist/chunks/{es-TR4g2I4A.js → es-CT34HBVM.js} +12 -2
- package/dist/chunks/{es-TR4g2I4A.js.map → es-CT34HBVM.js.map} +1 -1
- package/dist/chunks/{es-Do1q9_SF.js → es-DdJsxlUr.js} +3 -3
- package/dist/chunks/{es-Do1q9_SF.js.map → es-DdJsxlUr.js.map} +1 -1
- package/dist/chunks/{generated-Do60Aq2I.js → generated-CiCrGDQO.js} +49 -0
- package/dist/chunks/{generated-Do60Aq2I.js.map → generated-CiCrGDQO.js.map} +1 -1
- package/dist/chunks/{granular-provider-BW2uopTG.js → granular-provider-CYroWMjO.js} +9 -7
- package/dist/chunks/granular-provider-CYroWMjO.js.map +1 -0
- package/dist/chunks/{ru-Bm-zWJ3F.js → ru-CLiAMwrx.js} +12 -2
- package/dist/chunks/{ru-Bm-zWJ3F.js.map → ru-CLiAMwrx.js.map} +1 -1
- package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-CW3zY1ee.js → GrCodeBlock-BmXCLgLS.js} +4 -30
- package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-CW3zY1ee.js.map → GrCodeBlock-BmXCLgLS.js.map} +1 -1
- package/dist/components/GrCodeBlock/index.js +2 -2
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-LOaFSCyd.js → GrDropdownMenu-DyS6dWDp.js} +2 -2
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-LOaFSCyd.js.map → GrDropdownMenu-DyS6dWDp.js.map} +1 -1
- package/dist/components/GrDropdownMenu/index.js +2 -2
- package/dist/components/GrJsonViewer/chunks/GrJsonViewer-DFUppRNb.js +389 -0
- package/dist/components/GrJsonViewer/chunks/GrJsonViewer-DFUppRNb.js.map +1 -0
- package/dist/components/GrJsonViewer/index.js +3 -0
- package/dist/components/GrList/chunks/{GrList-BUzFRa8-.js → GrList-BRYhpqIe.js} +3 -3
- package/dist/components/GrList/chunks/GrList-BRYhpqIe.js.map +1 -0
- package/dist/components/GrList/index.js +2 -2
- package/dist/components/GrList/styles.css +1 -1
- package/dist/components/GrRadio/index.js +1 -1
- package/dist/components/GrRadioGroup/index.js +1 -1
- package/dist/components/GrRating/chunks/{GrRating-BiAVT9H7.js → GrRating-BSEtaibx.js} +1 -1
- package/dist/components/GrRating/chunks/{GrRating-BiAVT9H7.js.map → GrRating-BSEtaibx.js.map} +1 -1
- package/dist/components/GrRating/index.js +2 -2
- package/dist/components/GrTabPanels/index.js +1 -1
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/i18n/all.js +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/index.js +15 -13
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +1 -1
- package/dist/types/components/GrDropdownMenu/grDropdownMenuStyles.d.ts +10 -1
- package/dist/types/components/GrJsonViewer/GrJsonViewer.vue.d.ts +129 -0
- package/dist/types/components/GrJsonViewer/config.d.ts +1 -0
- package/dist/types/components/GrJsonViewer/defaults.d.ts +23 -0
- package/dist/types/components/GrJsonViewer/grJsonViewerStyles.d.ts +57 -0
- package/dist/types/components/GrJsonViewer/index.d.ts +9 -0
- package/dist/types/components/GrJsonViewer/jsonToNodes.d.ts +47 -0
- package/dist/types/components/GrJsonViewer/safelist.d.ts +1 -0
- package/dist/types/components/GrList/grListStyles.d.ts +13 -0
- package/dist/types/granular-provider/shared.d.ts +1 -0
- package/dist/types/i18n/messages/en.d.ts +18 -0
- package/dist/types/i18n/messages/es.d.ts +18 -0
- package/dist/types/i18n/messages/ru.d.ts +18 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/web-types.json +123 -3
- package/package.json +5 -1
- package/dist/chunks/config-BKvX0q10.js.map +0 -1
- package/dist/chunks/config-BTCOJO5z.js.map +0 -1
- package/dist/chunks/config-B_qi4Fma2.js.map +0 -1
- package/dist/chunks/config-Baj7Wvx-2.js.map +0 -1
- package/dist/chunks/config-BsuDM3Hb.js.map +0 -1
- package/dist/chunks/config-QaIc_gRa2.js.map +0 -1
- package/dist/chunks/config-eXB87DOi2.js.map +0 -1
- package/dist/chunks/granular-provider-BW2uopTG.js.map +0 -1
- package/dist/components/GrList/chunks/GrList-BUzFRa8-.js.map +0 -1
- /package/dist/chunks/{config-BTCOJO5z.js → config-BTCOJO5z2.js} +0 -0
- /package/dist/chunks/{config-B_qi4Fma2.js → config-B_qi4Fma.js} +0 -0
- /package/dist/chunks/{config-Baj7Wvx-2.js → config-Baj7Wvx-.js} +0 -0
- /package/dist/chunks/{config-QaIc_gRa2.js → config-QaIc_gRa.js} +0 -0
- /package/dist/chunks/{config-eXB87DOi2.js → config-eXB87DOi.js} +0 -0
|
@@ -10,7 +10,7 @@ import { t as grButtonGroupConfig } from "./config-DBmdgoVf.js";
|
|
|
10
10
|
import { t as grCardConfig } from "./config-Bm_Sovla.js";
|
|
11
11
|
import { t as grCheckboxConfig } from "./config-esdviAPL.js";
|
|
12
12
|
import { t as grCheckboxGroupConfig } from "./config-BuGlNPE9.js";
|
|
13
|
-
import { t as grCodeBlockConfig } from "./config-
|
|
13
|
+
import { t as grCodeBlockConfig } from "./config-B_qi4Fma.js";
|
|
14
14
|
import { t as grCollapseConfig } from "./config-DU2DNSRx.js";
|
|
15
15
|
import { t as grIconConfig } from "./config-Cp0DE0GH.js";
|
|
16
16
|
import { t as grColorPickerConfig } from "./config-BsPDQ0dS.js";
|
|
@@ -25,7 +25,7 @@ import { t as grDialogServiceConfig } from "./config-CjXjhphs.js";
|
|
|
25
25
|
import { t as grDividerConfig } from "./config-CY0_6cMi.js";
|
|
26
26
|
import { t as grDrawerConfig } from "./config-D94Z9po5.js";
|
|
27
27
|
import { t as grDropdownConfig } from "./config-BViooYNv.js";
|
|
28
|
-
import { t as grDropdownMenuConfig } from "./config-
|
|
28
|
+
import { t as grDropdownMenuConfig } from "./config-C5mO5DYZ.js";
|
|
29
29
|
import { t as grEmptyStateConfig } from "./config-vSji8w90.js";
|
|
30
30
|
import { t as grFilePreviewConfig } from "./config-QJ54_fcM.js";
|
|
31
31
|
import { t as grFileUploadConfig } from "./config-CN970ZcJ.js";
|
|
@@ -36,9 +36,10 @@ import { t as grFormSectionConfig } from "./config-Dgj1Q6JY.js";
|
|
|
36
36
|
import { t as grImageViewerConfig } from "./config-BeZRuonp.js";
|
|
37
37
|
import { t as grInputConfig } from "./config-DpeQchsV.js";
|
|
38
38
|
import { t as grInputTagConfig } from "./config-BN4sXYXn.js";
|
|
39
|
+
import { t as grJsonViewerConfig } from "./config-CIcynHGr.js";
|
|
39
40
|
import { t as grKbdConfig } from "./config-DA6SnEoy.js";
|
|
40
41
|
import { t as grLinkConfig } from "./config-Cl4ST6Qd.js";
|
|
41
|
-
import { t as grListConfig } from "./config-
|
|
42
|
+
import { t as grListConfig } from "./config-CKULiuvc.js";
|
|
42
43
|
import { t as grLoadingConfig } from "./config-CjnbevbD.js";
|
|
43
44
|
import { t as grModalConfig } from "./config-BtXRpAtq.js";
|
|
44
45
|
import { t as grNavbarConfig } from "./config-Bpt9Xbxm.js";
|
|
@@ -48,9 +49,9 @@ import { t as grPopoverConfig } from "./config-BSyiFlbi.js";
|
|
|
48
49
|
import { t as grProgressBarConfig } from "./config-CQzGyi1C.js";
|
|
49
50
|
import { t as grProgressCircleConfig } from "./config-MRvhUl-O.js";
|
|
50
51
|
import { t as grPromptDialogConfig } from "./config-C0Ea9ePP.js";
|
|
51
|
-
import { t as grRadioConfig } from "./config-
|
|
52
|
-
import { t as grRadioGroupConfig } from "./config-
|
|
53
|
-
import { t as grRatingConfig } from "./config-Baj7Wvx
|
|
52
|
+
import { t as grRadioConfig } from "./config-BTCOJO5z2.js";
|
|
53
|
+
import { t as grRadioGroupConfig } from "./config-QaIc_gRa.js";
|
|
54
|
+
import { t as grRatingConfig } from "./config-Baj7Wvx-.js";
|
|
54
55
|
import { t as grSegmentedConfig } from "./config-BEVBsvni2.js";
|
|
55
56
|
import { t as grSelectConfig } from "./config-Cwd3yjRw.js";
|
|
56
57
|
import { t as grSidebarConfig } from "./config-BfsBepkH2.js";
|
|
@@ -61,7 +62,7 @@ import { t as grSplitterConfig } from "./config-BE6n20np.js";
|
|
|
61
62
|
import { t as grStatisticConfig } from "./config-Be9k9bOf.js";
|
|
62
63
|
import { t as grSwitchConfig } from "./config-BoNR7_PD2.js";
|
|
63
64
|
import { t as grTableConfig } from "./config-WTNhdh6T.js";
|
|
64
|
-
import { t as grTabPanelsConfig } from "./config-
|
|
65
|
+
import { t as grTabPanelsConfig } from "./config-eXB87DOi.js";
|
|
65
66
|
import { t as grTabsConfig } from "./config-DWN4x6_e.js";
|
|
66
67
|
import { t as grTextareaConfig } from "./config-BDfTXHo1.js";
|
|
67
68
|
import { t as grTimelineConfig } from "./config-COpuWGr_.js";
|
|
@@ -143,6 +144,7 @@ var granularityComponentConfigs = {
|
|
|
143
144
|
GrImageViewer: grImageViewerConfig,
|
|
144
145
|
GrInput: grInputConfig,
|
|
145
146
|
GrInputTag: grInputTagConfig,
|
|
147
|
+
GrJsonViewer: grJsonViewerConfig,
|
|
146
148
|
GrKbd: grKbdConfig,
|
|
147
149
|
GrLink: grLinkConfig,
|
|
148
150
|
GrList: grListConfig,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"granular-provider-CYroWMjO.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 { 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 { 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 { 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'\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 GrCodeBlock: grCodeBlockConfig,\n GrCollapse: grCollapseConfig,\n GrColorPicker: grColorPickerConfig,\n GrCommandPalette: grCommandPaletteConfig,\n GrConfigProvider: grConfigProviderConfig,\n GrConfirmDialog: grConfirmDialogConfig,\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 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 // </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;;;;ACuFD,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,ykXAAA,KAAA,OAAA,KAAA,GAAA,CAAgD,CAAC;CAC/D,QAAQ;EACN,OAAO,IAAA,IAAA,qjYAAA,KAAA,OAAA,KAAA,GAAA,CAAsD,CAAC;EAC9D,MAAM,IAAA,IAAA,yxSAAA,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,aAAa;CACb,YAAY;CACZ,eAAe;CACf,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,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,UAAU;CACV,SAAS;CACT,aAAa;CACb,QAAQ;CACR,YAAY;CACZ,YAAY;CACZ,WAAW;CACX,WAAW;CACX,QAAQ;CACR,cAAc;AAEhB;;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;;;AChOA,IAAa,sBAAsB,0BAA0B"}
|
|
@@ -307,6 +307,15 @@ var codeBlock = {
|
|
|
307
307
|
"copied": "Код скопирован",
|
|
308
308
|
"copyFailed": "Не удалось скопировать код"
|
|
309
309
|
};
|
|
310
|
+
var jsonViewer = {
|
|
311
|
+
"search": "Поиск по ключу или значению",
|
|
312
|
+
"expandAll": "Раскрыть всё",
|
|
313
|
+
"collapseAll": "Свернуть всё",
|
|
314
|
+
"copy": "Скопировать значение",
|
|
315
|
+
"copied": "Значение скопировано",
|
|
316
|
+
"copyFailed": "Не удалось скопировать значение",
|
|
317
|
+
"moreItems": "ещё"
|
|
318
|
+
};
|
|
310
319
|
var ru_default = {
|
|
311
320
|
common,
|
|
312
321
|
loading,
|
|
@@ -356,7 +365,8 @@ var ru_default = {
|
|
|
356
365
|
bottomNav,
|
|
357
366
|
emptyState,
|
|
358
367
|
sortable,
|
|
359
|
-
codeBlock
|
|
368
|
+
codeBlock,
|
|
369
|
+
jsonViewer
|
|
360
370
|
};
|
|
361
371
|
//#endregion
|
|
362
|
-
export { autocomplete, avatar, badgeWrap, bottomNav, breadcrumbs, button, codeBlock, collapse, colorPicker, colors, commandPalette, common, dataTable, ru_default as default, dialog, drawer, emptyState, fileUpload, fileValidation, form, formErrorBanner, formFile, imageViewer, input, inputTag, kbd, link, list, loading, modal, navbar, numberInput, pagination, rating, responseError, select, sidebar, slider, sortable, splitter, statistic, table, tabs, textarea, timeline, toaster, tree, treeSelect, uploadErrorBanner };
|
|
372
|
+
export { autocomplete, avatar, badgeWrap, bottomNav, breadcrumbs, button, codeBlock, collapse, colorPicker, colors, commandPalette, common, dataTable, ru_default as default, dialog, drawer, emptyState, fileUpload, fileValidation, form, formErrorBanner, formFile, imageViewer, input, inputTag, jsonViewer, kbd, link, list, loading, modal, navbar, numberInput, pagination, rating, responseError, select, sidebar, slider, sortable, splitter, statistic, table, tabs, textarea, timeline, toaster, tree, treeSelect, uploadErrorBanner };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ru-Bm-zWJ3F.js","names":[],"sources":["../../src/i18n/locales/ru.json"],"sourcesContent":["{\n \"common\": {\n \"confirm\": \"Подтвердить\",\n \"cancel\": \"Отмена\",\n \"add\": \"Добавить\",\n \"clear\": \"Очистить\",\n \"close\": \"Закрыть\"\n },\n \"loading\": {\n \"defaultText\": \"Загрузка...\"\n },\n \"treeSelect\": {\n \"filterPlaceholder\": \"Поиск…\",\n \"empty\": \"Нет данных\",\n \"loading\": \"Загрузка…\"\n },\n \"badgeWrap\": {\n \"count\": \"{count} новых\"\n },\n \"avatar\": {\n \"group\": \"Участники\",\n \"groupOverflow\": \"и ещё {count}\",\n \"status\": {\n \"online\": \"в сети\",\n \"offline\": \"не в сети\",\n \"busy\": \"занят\",\n \"away\": \"отошёл\"\n }\n },\n \"tree\": {\n \"drag\": \"Перетащить\",\n \"expand\": \"Развернуть\",\n \"collapse\": \"Свернуть\",\n \"moved\": \"{label} перемещён\"\n },\n \"select\": {\n \"customValuePlaceholder\": \"Добавить значение…\",\n \"addOption\": \"Добавить «{value}»\",\n \"clearOption\": \"Не выбрано\",\n \"searchPlaceholder\": \"Поиск…\",\n \"loading\": \"Загрузка…\",\n \"noResults\": \"Ничего не найдено\",\n \"removeTag\": \"Удалить {label}\"\n },\n \"dataTable\": {\n \"loading\": \"Загрузка…\",\n \"empty\": \"Нет данных\",\n \"selectAll\": \"Выбрать все строки\",\n \"selectRow\": \"Выбрать строку\",\n \"sortAsc\": \"Нажмите для сортировки по возрастанию\",\n \"sortDesc\": \"Нажмите для сортировки по убыванию\",\n \"sortNone\": \"Нажмите, чтобы снять сортировку\",\n \"moveColumn\": \"Переставить колонку {label}\",\n \"columnMoved\": \"Колонка {label} перемещена на позицию {position} из {count}\",\n \"resizeColumn\": \"Ширина колонки {label}\",\n \"columnResized\": \"Ширина колонки {label} — {width} пикселей\"\n },\n \"breadcrumbs\": {\n \"label\": \"Навигационная цепочка\",\n \"expand\": \"Показать скрытые уровни\"\n },\n \"autocomplete\": {\n \"loading\": \"Загрузка…\",\n \"noResults\": \"Ничего не найдено\",\n \"addOption\": \"Добавить «{value}»\",\n \"removeValue\": \"Убрать {label}\",\n \"typeMore\": {\n \"one\": \"Введите минимум {n} символ\",\n \"few\": \"Введите минимум {n} символа\",\n \"many\": \"Введите минимум {n} символов\",\n \"other\": \"Введите минимум {n} символа\"\n }\n },\n \"form\": {\n \"required\": \"Обязательное поле\",\n \"min\": \"Минимум {min}\",\n \"max\": \"Максимум {max}\",\n \"len\": \"Длина должна быть {len}\",\n \"pattern\": \"Неверный формат\",\n \"email\": \"Введите корректный e-mail\",\n \"url\": \"Введите корректный URL\",\n \"file\": \"Некорректный файл\",\n \"invalid\": \"Некорректное значение\",\n \"readonly\": \"Только для чтения\",\n \"validating\": \"Проверяем…\"\n },\n \"dialog\": {\n \"confirm\": {\n \"title\": \"Подтверждение\"\n },\n \"prompt\": {\n \"title\": \"Введите значение\",\n \"label\": \"Значение\",\n \"required\": \"Введите значение.\"\n },\n \"ok\": \"ОК\"\n },\n \"switch\": {\n \"loading\": \"Сохраняем…\"\n },\n \"slider\": {\n \"min\": \"минимум\",\n \"max\": \"максимум\"\n },\n \"sidebar\": {\n \"expand\": \"Развернуть панель\",\n \"collapse\": \"Свернуть панель\"\n },\n \"splitter\": {\n \"label\": \"Изменить размер панелей\"\n },\n \"statistic\": {\n \"trendUp\": \"Рост\",\n \"trendDown\": \"Падение\",\n \"trendFlat\": \"Без изменений\"\n },\n \"pagination\": {\n \"label\": \"Пагинация\",\n \"pageSize\": \"Размер страницы\",\n \"prev\": \"Назад\",\n \"next\": \"Вперёд\",\n \"page\": \"Страница {n}\",\n \"first\": \"Первая страница\",\n \"last\": \"Последняя страница\",\n \"jumpTo\": \"Перейти к\",\n \"total\": \"{from}–{to} из {total}\",\n \"status\": \"Страница {page} из {count}\"\n },\n \"navbar\": {\n \"openMenu\": \"Открыть меню\"\n },\n \"responseError\": {\n \"networkTitle\": \"Нет связи с сервером\",\n \"networkMessage\": \"Проверьте подключение к интернету и попробуйте ещё раз.\",\n \"abortedTitle\": \"Запрос отменён\",\n \"abortedMessage\": \"Запрос был отменён.\",\n \"validationTitle\": \"Проверьте данные\",\n \"validationMessage\": \"Сервер вернул ошибку валидации. Исправьте поля и попробуйте снова.\",\n \"clientTitle\": \"Ошибка запроса\",\n \"clientMessage\": \"Сервер отклонил запрос.\",\n \"serverTitle\": \"Ошибка сервера\",\n \"serverMessage\": \"Произошла ошибка на сервере. Попробуйте ещё раз.\",\n \"unknownTitle\": \"Что-то пошло не так\",\n \"unknownMessage\": \"Не удалось выполнить операцию. Попробуйте ещё раз.\",\n \"statusLabel\": \"HTTP {status}\",\n \"retryLabel\": \"Повторить\",\n \"dismissLabel\": \"Скрыть\"\n },\n \"colors\": {\n \"neutral\": \"Нейтральный\",\n \"success\": \"Успех\",\n \"warning\": \"Предупреждение\",\n \"danger\": \"Опасность\",\n \"info\": \"Инфо\",\n \"primary\": \"Primary\",\n \"slate\": \"Сланцевый\",\n \"azure\": \"Лазурный\"\n },\n \"drawer\": {\n \"title\": \"Панель\"\n },\n \"modal\": {\n \"title\": \"Диалог\"\n },\n \"numberInput\": {\n \"increase\": \"Увеличить\",\n \"decrease\": \"Уменьшить\"\n },\n \"toaster\": {\n \"dismiss\": \"Закрыть\",\n \"region\": \"Уведомления\"\n },\n \"inputTag\": {\n \"removeTag\": \"Удалить тег\",\n \"removeTagNamed\": \"Удалить тег {tag}\",\n \"clearAll\": \"Удалить все теги\",\n \"added\": \"Добавлен тег: {tag}\",\n \"addedMany\": \"Добавлено тегов: {count}\",\n \"removed\": \"Удалён тег: {tag}\",\n \"cleared\": \"Все теги удалены\",\n \"limitReached\": \"Достигнут предел тегов\"\n },\n \"fileUpload\": {\n \"placeholder\": \"Перетащите файлы сюда или нажмите для выбора\",\n \"progress\": \"Прогресс загрузки\",\n \"uploading\": \"Загрузка…\",\n \"success\": \"Загрузка завершена\",\n \"error\": \"Не удалось загрузить\",\n \"remove\": \"Убрать {fileName}\",\n \"retry\": \"Повторить загрузку\",\n \"retryFile\": \"Повторить {fileName}\",\n \"abortFile\": \"Отменить загрузку {fileName}\",\n \"statusPending\": \"В очереди\",\n \"statusUploading\": \"Загружается\",\n \"statusSuccess\": \"Загружен\",\n \"statusError\": \"Ошибка\"\n },\n \"formFile\": {\n \"upload\": \"Загрузить файл\",\n \"change\": \"Заменить файл\",\n \"remove\": \"Удалить\",\n \"clearAll\": \"Очистить всё\",\n \"placeholder\": \"Файлы не выбраны\",\n \"validateError\": \"Не удалось проверить перетащенные файлы\",\n \"removeFile\": \"Убрать {fileName}\"\n },\n \"imageViewer\": {\n \"close\": \"Закрыть просмотрщик\",\n \"prev\": \"Предыдущее изображение\",\n \"next\": \"Следующее изображение\",\n \"zoomIn\": \"Увеличить\",\n \"zoomOut\": \"Уменьшить\",\n \"resetZoom\": \"Сбросить масштаб\",\n \"zoomToNatural\": \"Натуральный размер (1:1)\",\n \"rotateLeft\": \"Повернуть влево\",\n \"rotateRight\": \"Повернуть вправо\",\n \"download\": \"Скачать изображение\",\n \"empty\": \"Нет изображения\",\n \"label\": \"Просмотр изображений\",\n \"position\": \"Изображение {index} из {total}\"\n },\n \"formErrorBanner\": {\n \"validationTitle\": \"Проверьте поля формы\",\n \"validationMessage\": \"Сервер отклонил данные. Исправьте выделенные поля и попробуйте снова.\",\n \"clientTitle\": \"Не удалось сохранить\",\n \"serverTitle\": \"Ошибка сохранения\",\n \"serverMessage\": \"Не удалось сохранить данные. Попробуйте позже.\"\n },\n \"uploadErrorBanner\": {\n \"networkTitle\": \"Не удалось загрузить файлы\",\n \"networkMessage\": \"Проверьте подключение и попробуйте снова.\",\n \"validationTitle\": \"Файлы не прошли проверку\",\n \"validationMessage\": \"Некоторые файлы не соответствуют требованиям.\",\n \"serverTitle\": \"Ошибка загрузки на сервере\",\n \"serverMessage\": \"Сервер не смог обработать загрузку. Попробуйте снова.\"\n },\n \"input\": {\n \"clear\": \"Очистить\",\n \"showPassword\": \"Показать пароль\",\n \"hidePassword\": \"Скрыть пароль\",\n \"limitReached\": \"Достигнут предел символов\"\n },\n \"textarea\": {\n \"lines\": {\n \"one\": \"{n} строка\",\n \"few\": \"{n} строки\",\n \"many\": \"{n} строк\",\n \"other\": \"{n} строки\"\n }\n },\n \"rating\": {\n \"label\": \"Оценка\",\n \"valueText\": \"{value} из {max}\",\n \"valueTextWithLabel\": \"{value} из {max}, {label}\"\n },\n \"commandPalette\": {\n \"label\": \"Палитра команд\",\n \"placeholder\": \"Введите команду или запрос…\",\n \"empty\": \"Ничего не найдено\",\n \"recent\": \"Недавние\",\n \"loading\": \"Загрузка…\"\n },\n \"fileValidation\": {\n \"accept\": \"Файл «{fileName}» не подходит под accept=\\\"{accept}\\\"\",\n \"extension\": \"У файла «{fileName}» недопустимое расширение «.{extension}»\",\n \"mimeType\": \"У файла «{fileName}» недопустимый тип «{mimeType}»\",\n \"mimeTypeFallback\": \"У файла «{fileName}» неопределённый тип «{mimeType}»\",\n \"maxFileSize\": \"Файл «{fileName}» слишком большой ({size} байт), максимум {maxBytes}\",\n \"maxCount\": \"Выбрано слишком много файлов ({count}), максимум — {maxCount}\",\n \"maxTotalSize\": \"Суммарный размер файлов слишком большой ({total} байт), максимум {maxBytes}\"\n },\n \"kbd\": {\n \"command\": \"Command\",\n \"option\": \"Option\",\n \"shift\": \"Shift\",\n \"control\": \"Control\",\n \"enter\": \"Enter\",\n \"escape\": \"Escape\",\n \"tab\": \"Tab\",\n \"backspace\": \"Backspace\",\n \"delete\": \"Delete\",\n \"pageUp\": \"Page Up\",\n \"pageDown\": \"Page Down\",\n \"arrowUp\": \"стрелка вверх\",\n \"arrowDown\": \"стрелка вниз\",\n \"arrowLeft\": \"стрелка влево\",\n \"arrowRight\": \"стрелка вправо\",\n \"then\": \"затем\"\n },\n \"link\": {\n \"opensInNewTab\": \"откроется в новой вкладке\"\n },\n \"colorPicker\": {\n \"panelLabel\": \"Выбор цвета\",\n \"hue\": \"Оттенок\",\n \"saturation\": \"Насыщенность\",\n \"lightness\": \"Светлота\",\n \"opacity\": \"Непрозрачность\",\n \"hexLabel\": \"Значение hex\",\n \"presetsLabel\": \"Палитра\",\n \"swatchLabel\": \"Цвет {color}\"\n },\n \"collapse\": {\n \"empty\": \"Пока пусто\"\n },\n \"list\": {\n \"empty\": \"Пока пусто\"\n },\n \"button\": {\n \"loading\": \"Загрузка…\"\n },\n \"table\": {\n \"empty\": \"Пока пусто\"\n },\n \"timeline\": {\n \"empty\": \"Пока пусто\"\n },\n \"tabs\": {\n \"empty\": \"Пока пусто\",\n \"close\": \"Закрыть вкладку\"\n },\n \"bottomNav\": {\n \"label\": \"Нижняя навигация\",\n \"badge\": \"{count} новых\"\n },\n \"emptyState\": {\n \"title\": \"Пока пусто\"\n },\n \"sortable\": {\n \"label\": \"Сортируемый список\",\n \"handle\": \"Переставить\",\n \"empty\": \"Пока нечего сортировать\",\n \"grabbed\": \"Взято. Позиция {position} из {count}\",\n \"moved\": \"Позиция {position} из {count}\",\n \"dropped\": \"Отпущено на позиции {position} из {count}\",\n \"cancelled\": \"Перенос отменён\"\n },\n \"codeBlock\": {\n \"copy\": \"Скопировать код\",\n \"copied\": \"Код скопирован\",\n \"copyFailed\": \"Не удалось скопировать код\"\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ru-CLiAMwrx.js","names":[],"sources":["../../src/i18n/locales/ru.json"],"sourcesContent":["{\n \"common\": {\n \"confirm\": \"Подтвердить\",\n \"cancel\": \"Отмена\",\n \"add\": \"Добавить\",\n \"clear\": \"Очистить\",\n \"close\": \"Закрыть\"\n },\n \"loading\": {\n \"defaultText\": \"Загрузка...\"\n },\n \"treeSelect\": {\n \"filterPlaceholder\": \"Поиск…\",\n \"empty\": \"Нет данных\",\n \"loading\": \"Загрузка…\"\n },\n \"badgeWrap\": {\n \"count\": \"{count} новых\"\n },\n \"avatar\": {\n \"group\": \"Участники\",\n \"groupOverflow\": \"и ещё {count}\",\n \"status\": {\n \"online\": \"в сети\",\n \"offline\": \"не в сети\",\n \"busy\": \"занят\",\n \"away\": \"отошёл\"\n }\n },\n \"tree\": {\n \"drag\": \"Перетащить\",\n \"expand\": \"Развернуть\",\n \"collapse\": \"Свернуть\",\n \"moved\": \"{label} перемещён\"\n },\n \"select\": {\n \"customValuePlaceholder\": \"Добавить значение…\",\n \"addOption\": \"Добавить «{value}»\",\n \"clearOption\": \"Не выбрано\",\n \"searchPlaceholder\": \"Поиск…\",\n \"loading\": \"Загрузка…\",\n \"noResults\": \"Ничего не найдено\",\n \"removeTag\": \"Удалить {label}\"\n },\n \"dataTable\": {\n \"loading\": \"Загрузка…\",\n \"empty\": \"Нет данных\",\n \"selectAll\": \"Выбрать все строки\",\n \"selectRow\": \"Выбрать строку\",\n \"sortAsc\": \"Нажмите для сортировки по возрастанию\",\n \"sortDesc\": \"Нажмите для сортировки по убыванию\",\n \"sortNone\": \"Нажмите, чтобы снять сортировку\",\n \"moveColumn\": \"Переставить колонку {label}\",\n \"columnMoved\": \"Колонка {label} перемещена на позицию {position} из {count}\",\n \"resizeColumn\": \"Ширина колонки {label}\",\n \"columnResized\": \"Ширина колонки {label} — {width} пикселей\"\n },\n \"breadcrumbs\": {\n \"label\": \"Навигационная цепочка\",\n \"expand\": \"Показать скрытые уровни\"\n },\n \"autocomplete\": {\n \"loading\": \"Загрузка…\",\n \"noResults\": \"Ничего не найдено\",\n \"addOption\": \"Добавить «{value}»\",\n \"removeValue\": \"Убрать {label}\",\n \"typeMore\": {\n \"one\": \"Введите минимум {n} символ\",\n \"few\": \"Введите минимум {n} символа\",\n \"many\": \"Введите минимум {n} символов\",\n \"other\": \"Введите минимум {n} символа\"\n }\n },\n \"form\": {\n \"required\": \"Обязательное поле\",\n \"min\": \"Минимум {min}\",\n \"max\": \"Максимум {max}\",\n \"len\": \"Длина должна быть {len}\",\n \"pattern\": \"Неверный формат\",\n \"email\": \"Введите корректный e-mail\",\n \"url\": \"Введите корректный URL\",\n \"file\": \"Некорректный файл\",\n \"invalid\": \"Некорректное значение\",\n \"readonly\": \"Только для чтения\",\n \"validating\": \"Проверяем…\"\n },\n \"dialog\": {\n \"confirm\": {\n \"title\": \"Подтверждение\"\n },\n \"prompt\": {\n \"title\": \"Введите значение\",\n \"label\": \"Значение\",\n \"required\": \"Введите значение.\"\n },\n \"ok\": \"ОК\"\n },\n \"switch\": {\n \"loading\": \"Сохраняем…\"\n },\n \"slider\": {\n \"min\": \"минимум\",\n \"max\": \"максимум\"\n },\n \"sidebar\": {\n \"expand\": \"Развернуть панель\",\n \"collapse\": \"Свернуть панель\"\n },\n \"splitter\": {\n \"label\": \"Изменить размер панелей\"\n },\n \"statistic\": {\n \"trendUp\": \"Рост\",\n \"trendDown\": \"Падение\",\n \"trendFlat\": \"Без изменений\"\n },\n \"pagination\": {\n \"label\": \"Пагинация\",\n \"pageSize\": \"Размер страницы\",\n \"prev\": \"Назад\",\n \"next\": \"Вперёд\",\n \"page\": \"Страница {n}\",\n \"first\": \"Первая страница\",\n \"last\": \"Последняя страница\",\n \"jumpTo\": \"Перейти к\",\n \"total\": \"{from}–{to} из {total}\",\n \"status\": \"Страница {page} из {count}\"\n },\n \"navbar\": {\n \"openMenu\": \"Открыть меню\"\n },\n \"responseError\": {\n \"networkTitle\": \"Нет связи с сервером\",\n \"networkMessage\": \"Проверьте подключение к интернету и попробуйте ещё раз.\",\n \"abortedTitle\": \"Запрос отменён\",\n \"abortedMessage\": \"Запрос был отменён.\",\n \"validationTitle\": \"Проверьте данные\",\n \"validationMessage\": \"Сервер вернул ошибку валидации. Исправьте поля и попробуйте снова.\",\n \"clientTitle\": \"Ошибка запроса\",\n \"clientMessage\": \"Сервер отклонил запрос.\",\n \"serverTitle\": \"Ошибка сервера\",\n \"serverMessage\": \"Произошла ошибка на сервере. Попробуйте ещё раз.\",\n \"unknownTitle\": \"Что-то пошло не так\",\n \"unknownMessage\": \"Не удалось выполнить операцию. Попробуйте ещё раз.\",\n \"statusLabel\": \"HTTP {status}\",\n \"retryLabel\": \"Повторить\",\n \"dismissLabel\": \"Скрыть\"\n },\n \"colors\": {\n \"neutral\": \"Нейтральный\",\n \"success\": \"Успех\",\n \"warning\": \"Предупреждение\",\n \"danger\": \"Опасность\",\n \"info\": \"Инфо\",\n \"primary\": \"Primary\",\n \"slate\": \"Сланцевый\",\n \"azure\": \"Лазурный\"\n },\n \"drawer\": {\n \"title\": \"Панель\"\n },\n \"modal\": {\n \"title\": \"Диалог\"\n },\n \"numberInput\": {\n \"increase\": \"Увеличить\",\n \"decrease\": \"Уменьшить\"\n },\n \"toaster\": {\n \"dismiss\": \"Закрыть\",\n \"region\": \"Уведомления\"\n },\n \"inputTag\": {\n \"removeTag\": \"Удалить тег\",\n \"removeTagNamed\": \"Удалить тег {tag}\",\n \"clearAll\": \"Удалить все теги\",\n \"added\": \"Добавлен тег: {tag}\",\n \"addedMany\": \"Добавлено тегов: {count}\",\n \"removed\": \"Удалён тег: {tag}\",\n \"cleared\": \"Все теги удалены\",\n \"limitReached\": \"Достигнут предел тегов\"\n },\n \"fileUpload\": {\n \"placeholder\": \"Перетащите файлы сюда или нажмите для выбора\",\n \"progress\": \"Прогресс загрузки\",\n \"uploading\": \"Загрузка…\",\n \"success\": \"Загрузка завершена\",\n \"error\": \"Не удалось загрузить\",\n \"remove\": \"Убрать {fileName}\",\n \"retry\": \"Повторить загрузку\",\n \"retryFile\": \"Повторить {fileName}\",\n \"abortFile\": \"Отменить загрузку {fileName}\",\n \"statusPending\": \"В очереди\",\n \"statusUploading\": \"Загружается\",\n \"statusSuccess\": \"Загружен\",\n \"statusError\": \"Ошибка\"\n },\n \"formFile\": {\n \"upload\": \"Загрузить файл\",\n \"change\": \"Заменить файл\",\n \"remove\": \"Удалить\",\n \"clearAll\": \"Очистить всё\",\n \"placeholder\": \"Файлы не выбраны\",\n \"validateError\": \"Не удалось проверить перетащенные файлы\",\n \"removeFile\": \"Убрать {fileName}\"\n },\n \"imageViewer\": {\n \"close\": \"Закрыть просмотрщик\",\n \"prev\": \"Предыдущее изображение\",\n \"next\": \"Следующее изображение\",\n \"zoomIn\": \"Увеличить\",\n \"zoomOut\": \"Уменьшить\",\n \"resetZoom\": \"Сбросить масштаб\",\n \"zoomToNatural\": \"Натуральный размер (1:1)\",\n \"rotateLeft\": \"Повернуть влево\",\n \"rotateRight\": \"Повернуть вправо\",\n \"download\": \"Скачать изображение\",\n \"empty\": \"Нет изображения\",\n \"label\": \"Просмотр изображений\",\n \"position\": \"Изображение {index} из {total}\"\n },\n \"formErrorBanner\": {\n \"validationTitle\": \"Проверьте поля формы\",\n \"validationMessage\": \"Сервер отклонил данные. Исправьте выделенные поля и попробуйте снова.\",\n \"clientTitle\": \"Не удалось сохранить\",\n \"serverTitle\": \"Ошибка сохранения\",\n \"serverMessage\": \"Не удалось сохранить данные. Попробуйте позже.\"\n },\n \"uploadErrorBanner\": {\n \"networkTitle\": \"Не удалось загрузить файлы\",\n \"networkMessage\": \"Проверьте подключение и попробуйте снова.\",\n \"validationTitle\": \"Файлы не прошли проверку\",\n \"validationMessage\": \"Некоторые файлы не соответствуют требованиям.\",\n \"serverTitle\": \"Ошибка загрузки на сервере\",\n \"serverMessage\": \"Сервер не смог обработать загрузку. Попробуйте снова.\"\n },\n \"input\": {\n \"clear\": \"Очистить\",\n \"showPassword\": \"Показать пароль\",\n \"hidePassword\": \"Скрыть пароль\",\n \"limitReached\": \"Достигнут предел символов\"\n },\n \"textarea\": {\n \"lines\": {\n \"one\": \"{n} строка\",\n \"few\": \"{n} строки\",\n \"many\": \"{n} строк\",\n \"other\": \"{n} строки\"\n }\n },\n \"rating\": {\n \"label\": \"Оценка\",\n \"valueText\": \"{value} из {max}\",\n \"valueTextWithLabel\": \"{value} из {max}, {label}\"\n },\n \"commandPalette\": {\n \"label\": \"Палитра команд\",\n \"placeholder\": \"Введите команду или запрос…\",\n \"empty\": \"Ничего не найдено\",\n \"recent\": \"Недавние\",\n \"loading\": \"Загрузка…\"\n },\n \"fileValidation\": {\n \"accept\": \"Файл «{fileName}» не подходит под accept=\\\"{accept}\\\"\",\n \"extension\": \"У файла «{fileName}» недопустимое расширение «.{extension}»\",\n \"mimeType\": \"У файла «{fileName}» недопустимый тип «{mimeType}»\",\n \"mimeTypeFallback\": \"У файла «{fileName}» неопределённый тип «{mimeType}»\",\n \"maxFileSize\": \"Файл «{fileName}» слишком большой ({size} байт), максимум {maxBytes}\",\n \"maxCount\": \"Выбрано слишком много файлов ({count}), максимум — {maxCount}\",\n \"maxTotalSize\": \"Суммарный размер файлов слишком большой ({total} байт), максимум {maxBytes}\"\n },\n \"kbd\": {\n \"command\": \"Command\",\n \"option\": \"Option\",\n \"shift\": \"Shift\",\n \"control\": \"Control\",\n \"enter\": \"Enter\",\n \"escape\": \"Escape\",\n \"tab\": \"Tab\",\n \"backspace\": \"Backspace\",\n \"delete\": \"Delete\",\n \"pageUp\": \"Page Up\",\n \"pageDown\": \"Page Down\",\n \"arrowUp\": \"стрелка вверх\",\n \"arrowDown\": \"стрелка вниз\",\n \"arrowLeft\": \"стрелка влево\",\n \"arrowRight\": \"стрелка вправо\",\n \"then\": \"затем\"\n },\n \"link\": {\n \"opensInNewTab\": \"откроется в новой вкладке\"\n },\n \"colorPicker\": {\n \"panelLabel\": \"Выбор цвета\",\n \"hue\": \"Оттенок\",\n \"saturation\": \"Насыщенность\",\n \"lightness\": \"Светлота\",\n \"opacity\": \"Непрозрачность\",\n \"hexLabel\": \"Значение hex\",\n \"presetsLabel\": \"Палитра\",\n \"swatchLabel\": \"Цвет {color}\"\n },\n \"collapse\": {\n \"empty\": \"Пока пусто\"\n },\n \"list\": {\n \"empty\": \"Пока пусто\"\n },\n \"button\": {\n \"loading\": \"Загрузка…\"\n },\n \"table\": {\n \"empty\": \"Пока пусто\"\n },\n \"timeline\": {\n \"empty\": \"Пока пусто\"\n },\n \"tabs\": {\n \"empty\": \"Пока пусто\",\n \"close\": \"Закрыть вкладку\"\n },\n \"bottomNav\": {\n \"label\": \"Нижняя навигация\",\n \"badge\": \"{count} новых\"\n },\n \"emptyState\": {\n \"title\": \"Пока пусто\"\n },\n \"sortable\": {\n \"label\": \"Сортируемый список\",\n \"handle\": \"Переставить\",\n \"empty\": \"Пока нечего сортировать\",\n \"grabbed\": \"Взято. Позиция {position} из {count}\",\n \"moved\": \"Позиция {position} из {count}\",\n \"dropped\": \"Отпущено на позиции {position} из {count}\",\n \"cancelled\": \"Перенос отменён\"\n },\n \"codeBlock\": {\n \"copy\": \"Скопировать код\",\n \"copied\": \"Код скопирован\",\n \"copyFailed\": \"Не удалось скопировать код\"\n },\n \"jsonViewer\": {\n \"search\": \"Поиск по ключу или значению\",\n \"expandAll\": \"Раскрыть всё\",\n \"collapseAll\": \"Свернуть всё\",\n \"copy\": \"Скопировать значение\",\n \"copied\": \"Значение скопировано\",\n \"copyFailed\": \"Не удалось скопировать значение\",\n \"moreItems\": \"ещё\"\n }\n}\n"],"mappings":""}
|
package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-CW3zY1ee.js → GrCodeBlock-BmXCLgLS.js}
RENAMED
|
@@ -3,36 +3,10 @@ import { n as useGranularityTranslations } from "../../../chunks/granularityI18n
|
|
|
3
3
|
import { t as check_default } from "../../../chunks/check-BZI24Qs4.js";
|
|
4
4
|
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-Bw2AH4uX.js";
|
|
5
5
|
import { useAnnouncer } from "../../../composables/useAnnouncer.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
viewBox: "0 0 24 24",
|
|
11
|
-
width: "1.2em",
|
|
12
|
-
height: "1.2em"
|
|
13
|
-
};
|
|
14
|
-
function render$1(_ctx, _cache) {
|
|
15
|
-
return openBlock(), createElementBlock("svg", _hoisted_1$1, [..._cache[0] || (_cache[0] = [createElementVNode("g", {
|
|
16
|
-
fill: "none",
|
|
17
|
-
stroke: "currentColor",
|
|
18
|
-
"stroke-linecap": "round",
|
|
19
|
-
"stroke-linejoin": "round",
|
|
20
|
-
"stroke-width": "2"
|
|
21
|
-
}, [createElementVNode("rect", {
|
|
22
|
-
width: "14",
|
|
23
|
-
height: "14",
|
|
24
|
-
x: "8",
|
|
25
|
-
y: "8",
|
|
26
|
-
rx: "2",
|
|
27
|
-
ry: "2"
|
|
28
|
-
}), createElementVNode("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })], -1)])]);
|
|
29
|
-
}
|
|
30
|
-
var copy_default = markRaw({
|
|
31
|
-
name: "lucide-copy",
|
|
32
|
-
render: render$1
|
|
33
|
-
});
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/components/GrCodeBlock/serializeCode.ts
|
|
6
|
+
import { t as copy_default } from "../../../chunks/copy-DChChcq-.js";
|
|
7
|
+
import { a as codeBlockHookClass, c as codeBlockPaddings, d as codeBlockTextSizes, f as codeBlockWrapClass, i as codeBlockGutterClass, l as codeBlockRootClass, o as codeBlockNowrapClass, p as codeTokenClass, r as codeBlockCopyClass, s as codeBlockNumberedClass, u as codeBlockScrollClass } from "../../../chunks/config-B_qi4Fma.js";
|
|
8
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, resolveDynamicComponent, toDisplayString, unref, withCtx } from "vue";
|
|
9
|
+
import '../styles.css';//#region src/components/GrCodeBlock/serializeCode.ts
|
|
36
10
|
/**
|
|
37
11
|
* Приведение произвольного значения к тексту для показа.
|
|
38
12
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrCodeBlock-CW3zY1ee.js","names":[],"sources":["../../../../src/components/GrCodeBlock/serializeCode.ts","../../../../src/components/GrCodeBlock/tokenizeJson.ts","../../../../src/components/GrCodeBlock/GrCodeBlock.vue","../../../../src/components/GrCodeBlock/GrCodeBlock.vue"],"sourcesContent":["/**\n * Приведение произвольного значения к тексту для показа.\n *\n * Компонент показывает данные из БД, и они бывают любыми: `unknown`-поле ответа\n * модели, `meta` запуска, тело ошибки. Поэтому сериализация обязана пережить всё\n * — уронить страницу она права не имеет.\n */\n\n/** Маркер повторной ссылки. Показывается вместо ветки, которая уже встречалась выше. */\nexport const CIRCULAR_MARKER = '[Circular]'\n\n/** Что печатается, когда сериализовать не удалось вовсе. */\nexport const UNSERIALIZABLE_MARKER = '[Unserializable]'\n\n/**\n * Замена значений, на которых штатный `JSON.stringify` спотыкается.\n *\n * Маркером помечается **повторная ссылка**, а не только настоящий цикл: объект,\n * положенный в дерево дважды, тоже её получит. Отличить одно от другого можно\n * лишь стеком предков, а `replacer` его не отдаёт — узнать, что обход вышел из\n * ветки, изнутри нечем. Размен осознанный: показать `[Circular]` там, где данные\n * просто переиспользуют объект, — неточность; зациклиться на настоящем цикле —\n * зависшая вкладка.\n */\nfunction createSafeReplacer(): (key: string, value: unknown) => unknown {\n const seen = new WeakSet<object>()\n\n return function safeReplacer(_key: string, value: unknown): unknown {\n // `BigInt` не имеет представления в JSON, и `stringify` на нём бросает.\n if (typeof value === 'bigint') return `${value}n`\n\n if (typeof value !== 'object' || value === null) return value\n\n if (seen.has(value)) return CIRCULAR_MARKER\n\n seen.add(value)\n return value\n }\n}\n\n/**\n * Значение → текст.\n *\n * Строка проходит как есть: это уже готовый текст, и оборачивать его в кавычки\n * значило бы показать не то, что пришло. `undefined` даёт пустую строку —\n * показывать нечего. Всё остальное сериализуется с отступом.\n */\nexport function serializeCode(value: unknown, indent = 2): string {\n if (typeof value === 'string') return value\n if (value === undefined) return ''\n\n try {\n const serialized = JSON.stringify(value, createSafeReplacer(), indent)\n // `stringify` отдаёт `undefined` на функции и символе — печатать «undefined»\n // строкой было бы враньём про содержимое.\n return serialized ?? ''\n }\n catch {\n return UNSERIALIZABLE_MARKER\n }\n}\n","/**\n * Токенизация JSON для подсветки.\n *\n * Своя, а не библиотечная: тянуть highlight.js ради четырёх ролей несоразмерно\n * — компонент показывает JSON и обычный текст, а не произвольные языки.\n *\n * Разбор посимвольный и **ничего не теряет**: пробелы, переносы и запятые тоже\n * становятся токенами. Отсюда инвариант, который держит тест: склейка `text`\n * всех токенов равна исходной строке. Любая потеря символа — сдвиг разметки.\n */\n\nexport type GrCodeTokenKind =\n /** Имя поля объекта: строка, за которой идёт двоеточие. */\n | 'key'\n | 'string'\n | 'number'\n /** `true`, `false`, `null`. */\n | 'literal'\n /** Скобки, запятые, двоеточия. */\n | 'punctuation'\n /** Пробелы, переносы и всё, что не опознано. */\n | 'plain'\n\nexport interface GrCodeToken {\n text: string\n kind: GrCodeTokenKind\n}\n\nconst PUNCTUATION = new Set(['{', '}', '[', ']', ',', ':'])\nconst LITERALS = ['true', 'false', 'null'] as const\n\n/** Строка от открывающей кавычки до закрывающей, с учётом экранирования. */\nfunction readString(source: string, start: number): number {\n let index = start + 1\n\n while (index < source.length) {\n const char = source[index]\n\n // Экранированная кавычка не закрывает строку: `\"a\\\":b\"` — одно значение,\n // а не строка плюс мусор.\n if (char === '\\\\') {\n index += 2\n continue\n }\n\n index += 1\n if (char === '\"') break\n }\n\n return index\n}\n\n/** Число целиком: знак, дробная часть, экспонента. */\nfunction readNumber(source: string, start: number): number {\n let index = start\n\n if (source[index] === '-') index += 1\n while (index < source.length && /[\\d.eE+-]/.test(source[index])) index += 1\n\n return index\n}\n\n/** Первый непробельный символ после позиции — по нему строка отличается от ключа. */\nfunction nextMeaningful(source: string, from: number): string | undefined {\n for (let index = from; index < source.length; index += 1) {\n if (!/\\s/.test(source[index])) return source[index]\n }\n\n return undefined\n}\n\nexport function tokenizeJson(source: string): GrCodeToken[] {\n const tokens: GrCodeToken[] = []\n let index = 0\n let plainStart = 0\n\n const flushPlain = (until: number): void => {\n if (until > plainStart) tokens.push({ text: source.slice(plainStart, until), kind: 'plain' })\n }\n\n while (index < source.length) {\n const char = source[index]\n\n if (char === '\"') {\n flushPlain(index)\n const end = readString(source, index)\n // Ключ от строки отличает только идущее следом двоеточие.\n const kind: GrCodeTokenKind = nextMeaningful(source, end) === ':' ? 'key' : 'string'\n tokens.push({ text: source.slice(index, end), kind })\n index = end\n plainStart = index\n continue\n }\n\n if (PUNCTUATION.has(char)) {\n flushPlain(index)\n tokens.push({ text: char, kind: 'punctuation' })\n index += 1\n plainStart = index\n continue\n }\n\n if (char === '-' || (char >= '0' && char <= '9')) {\n flushPlain(index)\n const end = readNumber(source, index)\n tokens.push({ text: source.slice(index, end), kind: 'number' })\n index = end\n plainStart = index\n continue\n }\n\n const literal = LITERALS.find(candidate => source.startsWith(candidate, index))\n if (literal) {\n flushPlain(index)\n tokens.push({ text: literal, kind: 'literal' })\n index += literal.length\n plainStart = index\n continue\n }\n\n index += 1\n }\n\n flushPlain(source.length)\n\n return tokens\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GrCodeBlock-BmXCLgLS.js","names":[],"sources":["../../../../src/components/GrCodeBlock/serializeCode.ts","../../../../src/components/GrCodeBlock/tokenizeJson.ts","../../../../src/components/GrCodeBlock/GrCodeBlock.vue","../../../../src/components/GrCodeBlock/GrCodeBlock.vue"],"sourcesContent":["/**\n * Приведение произвольного значения к тексту для показа.\n *\n * Компонент показывает данные из БД, и они бывают любыми: `unknown`-поле ответа\n * модели, `meta` запуска, тело ошибки. Поэтому сериализация обязана пережить всё\n * — уронить страницу она права не имеет.\n */\n\n/** Маркер повторной ссылки. Показывается вместо ветки, которая уже встречалась выше. */\nexport const CIRCULAR_MARKER = '[Circular]'\n\n/** Что печатается, когда сериализовать не удалось вовсе. */\nexport const UNSERIALIZABLE_MARKER = '[Unserializable]'\n\n/**\n * Замена значений, на которых штатный `JSON.stringify` спотыкается.\n *\n * Маркером помечается **повторная ссылка**, а не только настоящий цикл: объект,\n * положенный в дерево дважды, тоже её получит. Отличить одно от другого можно\n * лишь стеком предков, а `replacer` его не отдаёт — узнать, что обход вышел из\n * ветки, изнутри нечем. Размен осознанный: показать `[Circular]` там, где данные\n * просто переиспользуют объект, — неточность; зациклиться на настоящем цикле —\n * зависшая вкладка.\n */\nfunction createSafeReplacer(): (key: string, value: unknown) => unknown {\n const seen = new WeakSet<object>()\n\n return function safeReplacer(_key: string, value: unknown): unknown {\n // `BigInt` не имеет представления в JSON, и `stringify` на нём бросает.\n if (typeof value === 'bigint') return `${value}n`\n\n if (typeof value !== 'object' || value === null) return value\n\n if (seen.has(value)) return CIRCULAR_MARKER\n\n seen.add(value)\n return value\n }\n}\n\n/**\n * Значение → текст.\n *\n * Строка проходит как есть: это уже готовый текст, и оборачивать его в кавычки\n * значило бы показать не то, что пришло. `undefined` даёт пустую строку —\n * показывать нечего. Всё остальное сериализуется с отступом.\n */\nexport function serializeCode(value: unknown, indent = 2): string {\n if (typeof value === 'string') return value\n if (value === undefined) return ''\n\n try {\n const serialized = JSON.stringify(value, createSafeReplacer(), indent)\n // `stringify` отдаёт `undefined` на функции и символе — печатать «undefined»\n // строкой было бы враньём про содержимое.\n return serialized ?? ''\n }\n catch {\n return UNSERIALIZABLE_MARKER\n }\n}\n","/**\n * Токенизация JSON для подсветки.\n *\n * Своя, а не библиотечная: тянуть highlight.js ради четырёх ролей несоразмерно\n * — компонент показывает JSON и обычный текст, а не произвольные языки.\n *\n * Разбор посимвольный и **ничего не теряет**: пробелы, переносы и запятые тоже\n * становятся токенами. Отсюда инвариант, который держит тест: склейка `text`\n * всех токенов равна исходной строке. Любая потеря символа — сдвиг разметки.\n */\n\nexport type GrCodeTokenKind =\n /** Имя поля объекта: строка, за которой идёт двоеточие. */\n | 'key'\n | 'string'\n | 'number'\n /** `true`, `false`, `null`. */\n | 'literal'\n /** Скобки, запятые, двоеточия. */\n | 'punctuation'\n /** Пробелы, переносы и всё, что не опознано. */\n | 'plain'\n\nexport interface GrCodeToken {\n text: string\n kind: GrCodeTokenKind\n}\n\nconst PUNCTUATION = new Set(['{', '}', '[', ']', ',', ':'])\nconst LITERALS = ['true', 'false', 'null'] as const\n\n/** Строка от открывающей кавычки до закрывающей, с учётом экранирования. */\nfunction readString(source: string, start: number): number {\n let index = start + 1\n\n while (index < source.length) {\n const char = source[index]\n\n // Экранированная кавычка не закрывает строку: `\"a\\\":b\"` — одно значение,\n // а не строка плюс мусор.\n if (char === '\\\\') {\n index += 2\n continue\n }\n\n index += 1\n if (char === '\"') break\n }\n\n return index\n}\n\n/** Число целиком: знак, дробная часть, экспонента. */\nfunction readNumber(source: string, start: number): number {\n let index = start\n\n if (source[index] === '-') index += 1\n while (index < source.length && /[\\d.eE+-]/.test(source[index])) index += 1\n\n return index\n}\n\n/** Первый непробельный символ после позиции — по нему строка отличается от ключа. */\nfunction nextMeaningful(source: string, from: number): string | undefined {\n for (let index = from; index < source.length; index += 1) {\n if (!/\\s/.test(source[index])) return source[index]\n }\n\n return undefined\n}\n\nexport function tokenizeJson(source: string): GrCodeToken[] {\n const tokens: GrCodeToken[] = []\n let index = 0\n let plainStart = 0\n\n const flushPlain = (until: number): void => {\n if (until > plainStart) tokens.push({ text: source.slice(plainStart, until), kind: 'plain' })\n }\n\n while (index < source.length) {\n const char = source[index]\n\n if (char === '\"') {\n flushPlain(index)\n const end = readString(source, index)\n // Ключ от строки отличает только идущее следом двоеточие.\n const kind: GrCodeTokenKind = nextMeaningful(source, end) === ':' ? 'key' : 'string'\n tokens.push({ text: source.slice(index, end), kind })\n index = end\n plainStart = index\n continue\n }\n\n if (PUNCTUATION.has(char)) {\n flushPlain(index)\n tokens.push({ text: char, kind: 'punctuation' })\n index += 1\n plainStart = index\n continue\n }\n\n if (char === '-' || (char >= '0' && char <= '9')) {\n flushPlain(index)\n const end = readNumber(source, index)\n tokens.push({ text: source.slice(index, end), kind: 'number' })\n index = end\n plainStart = index\n continue\n }\n\n const literal = LITERALS.find(candidate => source.startsWith(candidate, index))\n if (literal) {\n flushPlain(index)\n tokens.push({ text: literal, kind: 'literal' })\n index += literal.length\n plainStart = index\n continue\n }\n\n index += 1\n }\n\n flushPlain(source.length)\n\n return tokens\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { serializeCode } from './serializeCode'\nimport { tokenizeJson, type GrCodeToken } from './tokenizeJson'\nimport {\n codeBlockCopyClass,\n codeBlockGutterClass,\n codeBlockHookClass,\n codeBlockNowrapClass,\n codeBlockNumberedClass as numberedClass,\n codeBlockPaddings,\n codeBlockRootClass,\n codeBlockScrollClass,\n codeBlockSurfaceClass,\n codeBlockTextSizes,\n codeBlockWrapClass,\n codeTokenClass,\n} from './grCodeBlockStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrCodeToken, GrCodeTokenKind } from './tokenizeJson'\n\n/** Язык подсветки. Кроме JSON компонент ничего не разбирает — и не должен. */\nexport type GrCodeBlockLanguage = 'json' | 'text'\n\n/**\n * Блок сырого текста или JSON: моноширинный, с подсветкой, номерами строк и\n * копированием в буфер.\n *\n * Показывает, а не редактирует и не сворачивает узлы. Свёртка по узлам — задача\n * `GrJsonViewer`, дерево выбора — `GrTree`.\n */\nexport interface GrCodeBlockProps {\n /** Строка или значение: объект сериализуется с отступом. */\n code: unknown\n language?: GrCodeBlockLanguage\n /** Высота, после которой включается вертикальный скролл. */\n maxHeight?: string | number\n /** Кнопка копирования. Без защищённого контекста не рисуется вовсе. */\n copyable?: boolean\n /** Переносить длинные строки вместо горизонтальной прокрутки. */\n wrap?: boolean\n lineNumbers?: boolean\n /** Имя области прокрутки. Безымянную скринридер объявляет просто «регион». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrCodeBlockEmits {\n /** Текст скопирован. Тост показывает потребитель: у блока своего места для него нет. */\n (e: 'copy', code: string): void\n}\n\nconst props = withDefaults(defineProps<GrCodeBlockProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем компонент\n // заглянет в `GrConfigProvider`.\n language: 'json',\n maxHeight: undefined,\n copyable: undefined,\n wrap: undefined,\n lineNumbers: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrCodeBlockEmits>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrCodeBlock' })\nconst resolvedCopyable = useGrComponentProp('GrCodeBlock', 'copyable', () => props.copyable, true)\nconst resolvedWrap = useGrComponentProp('GrCodeBlock', 'wrap', () => props.wrap, false)\nconst resolvedLineNumbers = useGrComponentProp('GrCodeBlock', 'lineNumbers', () => props.lineNumbers, false)\n\n/** Единственный источник текста: он же рендерится, он же уходит в буфер. */\nconst source = computed(() => serializeCode(props.code))\n\n/**\n * Разбор идёт по строкам, а не по всему тексту: номер строки — CSS-счётчик, а\n * счётчику нужен элемент на строку. JSON-строка перенос не содержит (он в ней\n * экранирован), поэтому построчная токенизация ничего не рвёт.\n */\nconst lines = computed<GrCodeToken[][]>(() => {\n const rows = source.value.split('\\n')\n\n if (props.language !== 'json') return rows.map(row => [{ text: row, kind: 'plain' as const }])\n\n return rows.map(row => tokenizeJson(row))\n})\n\n/**\n * Блок — скроллер по конструкции, а не по замеру. Замер здесь означал бы\n * мигающую остановку `Tab` на каждой смене данных и загрузке шрифта; ни один\n * компонент пакета так не делает.\n */\nconst isScroller = computed(() => props.maxHeight !== undefined || !resolvedWrap.value)\n\nconst scrollStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n return { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight }\n})\n\nconst preClass = computed(() => [\n codeBlockHookClass,\n codeBlockSurfaceClass,\n codeBlockPaddings[resolvedSize.value],\n codeBlockTextSizes[resolvedSize.value],\n resolvedWrap.value ? codeBlockWrapClass : codeBlockNowrapClass,\n isScroller.value ? codeBlockScrollClass : '',\n].filter(Boolean).join(' '))\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copied = ref(false)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\nasync function copy(): Promise<void> {\n try {\n // Копируется исходный текст, а не разметка: номера строк — CSS-счётчик,\n // а мягкие переносы принадлежат показу, и вставить их обратно нельзя.\n await navigator.clipboard.writeText(source.value)\n }\n catch {\n announce(t('gr.codeBlock.copyFailed', 'Could not copy code'))\n return\n }\n\n copied.value = true\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copied.value = false\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.codeBlock.copied', 'Code copied'))\n emit('copy', source.value)\n}\n\nconst copyLabel = computed(() => (\n copied.value ? t('gr.codeBlock.copied', 'Code copied') : t('gr.codeBlock.copy', 'Copy code')\n))\n</script>\n\n<template>\n <div data-gr-code-block :class=\"[codeBlockRootClass, showCopy ? codeBlockGutterClass : '']\">\n <GrButton\n v-if=\"showCopy\"\n data-gr-code-block-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"codeBlockCopyClass\"\n :aria-label=\"copyLabel\"\n :title=\"copyLabel\"\n @click=\"copy\"\n >\n <component :is=\"copied ? IconCheck : IconCopy\" class=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </GrButton>\n\n <!--\n Внутри `<pre>` пробелы значимы, и компилятор Vue их здесь не схлопывает:\n любой перенос между тегами стал бы пустой строкой в коде. Поэтому\n содержимое блока записано без переносов — строки разделяет `display: block`\n у `.gr-code-block__line`, а не текстовый `\\n`.\n -->\n <pre\n data-gr-code-block-scroll\n :class=\"[preClass, resolvedLineNumbers ? numberedClass : '']\"\n :style=\"scrollStyle\"\n :tabindex=\"isScroller ? 0 : undefined\"\n :role=\"ariaLabel ? 'region' : undefined\"\n :aria-label=\"ariaLabel\"\n ><code data-gr-code-block-code><span v-for=\"(tokens, index) in lines\" :key=\"index\" data-gr-code-block-line class=\"gr-code-block__line\"><span v-for=\"(token, tokenIndex) in tokens\" :key=\"tokenIndex\" :class=\"codeTokenClass[token.kind]\">{{ token.text }}</span></span></code></pre>\n </div>\n</template>\n\n<style>\n/*\n * Номер строки — CSS-счётчик, а не текст в разметке. Разница не косметическая:\n * узел со `<span>2</span>` попадает в выделение мышью, и в буфер уезжает то,\n * что нельзя вставить обратно как код. У `::before` содержимого в DOM нет.\n *\n * Стиль глобальный, а не scoped: правило адресует псевдоэлемент строки, и\n * scoped-атрибут пришлось бы вешать на каждый из них.\n */\n:where(.gr-code-block__line) {\n display: block;\n}\n\n:where(.gr-code-block--numbered) {\n counter-reset: gr-code-line;\n}\n\n/*\n * Висячий отступ: строка сдвинута на ширину колонки номеров, а первая строка\n * возвращена назад отрицательным `text-indent` — туда и встаёт номер. Без этого\n * перенесённая при `wrap` строка начиналась бы под номером, а не под кодом.\n */\n:where(.gr-code-block--numbered .gr-code-block__line) {\n padding-left: calc(var(--gr-code-block-line-number-width, 2.5em) + 1em);\n text-indent: calc(-1 * (var(--gr-code-block-line-number-width, 2.5em) + 1em));\n}\n\n:where(.gr-code-block--numbered .gr-code-block__line)::before {\n counter-increment: gr-code-line;\n content: counter(gr-code-line);\n display: inline-block;\n width: var(--gr-code-block-line-number-width, 2.5em);\n margin-right: 1em;\n text-align: right;\n text-indent: 0;\n color: var(--gr-code-block-line-number, var(--gr-muted-fg));\n user-select: none;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;AASA,IAAa,kBAAkB;;AAG/B,IAAa,wBAAwB;;;;;;;;;;;AAYrC,SAAS,qBAA+D;CACtE,MAAM,uBAAO,IAAI,QAAgB;CAEjC,OAAO,SAAS,aAAa,MAAc,OAAyB;EAElE,IAAI,OAAO,UAAU,UAAU,OAAO,GAAG,MAAM;EAE/C,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;EAExD,IAAI,KAAK,IAAI,KAAK,GAAG,OAAO;EAE5B,KAAK,IAAI,KAAK;EACd,OAAO;CACT;AACF;;;;;;;;AASA,SAAgB,cAAc,OAAgB,SAAS,GAAW;CAChE,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,UAAU,KAAA,GAAW,OAAO;CAEhC,IAAI;EAIF,OAHmB,KAAK,UAAU,OAAO,mBAAmB,GAAG,MAGxD,KAAc;CACvB,QACM;EACJ,OAAO;CACT;AACF;;;AChCA,IAAM,8BAAc,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG,CAAC;AAC1D,IAAM,WAAW;CAAC;CAAQ;CAAS;AAAM;;AAGzC,SAAS,WAAW,QAAgB,OAAuB;CACzD,IAAI,QAAQ,QAAQ;CAEpB,OAAO,QAAQ,OAAO,QAAQ;EAC5B,MAAM,OAAO,OAAO;EAIpB,IAAI,SAAS,MAAM;GACjB,SAAS;GACT;EACF;EAEA,SAAS;EACT,IAAI,SAAS,MAAK;CACpB;CAEA,OAAO;AACT;;AAGA,SAAS,WAAW,QAAgB,OAAuB;CACzD,IAAI,QAAQ;CAEZ,IAAI,OAAO,WAAW,KAAK,SAAS;CACpC,OAAO,QAAQ,OAAO,UAAU,YAAY,KAAK,OAAO,MAAM,GAAG,SAAS;CAE1E,OAAO;AACT;;AAGA,SAAS,eAAe,QAAgB,MAAkC;CACxE,KAAK,IAAI,QAAQ,MAAM,QAAQ,OAAO,QAAQ,SAAS,GACrD,IAAI,CAAC,KAAK,KAAK,OAAO,MAAM,GAAG,OAAO,OAAO;AAIjD;AAEA,SAAgB,aAAa,QAA+B;CAC1D,MAAM,SAAwB,CAAC;CAC/B,IAAI,QAAQ;CACZ,IAAI,aAAa;CAEjB,MAAM,cAAc,UAAwB;EAC1C,IAAI,QAAQ,YAAY,OAAO,KAAK;GAAE,MAAM,OAAO,MAAM,YAAY,KAAK;GAAG,MAAM;EAAQ,CAAC;CAC9F;CAEA,OAAO,QAAQ,OAAO,QAAQ;EAC5B,MAAM,OAAO,OAAO;EAEpB,IAAI,SAAS,MAAK;GAChB,WAAW,KAAK;GAChB,MAAM,MAAM,WAAW,QAAQ,KAAK;GAEpC,MAAM,OAAwB,eAAe,QAAQ,GAAG,MAAM,MAAM,QAAQ;GAC5E,OAAO,KAAK;IAAE,MAAM,OAAO,MAAM,OAAO,GAAG;IAAG;GAAK,CAAC;GACpD,QAAQ;GACR,aAAa;GACb;EACF;EAEA,IAAI,YAAY,IAAI,IAAI,GAAG;GACzB,WAAW,KAAK;GAChB,OAAO,KAAK;IAAE,MAAM;IAAM,MAAM;GAAc,CAAC;GAC/C,SAAS;GACT,aAAa;GACb;EACF;EAEA,IAAI,SAAS,OAAQ,QAAQ,OAAO,QAAQ,KAAM;GAChD,WAAW,KAAK;GAChB,MAAM,MAAM,WAAW,QAAQ,KAAK;GACpC,OAAO,KAAK;IAAE,MAAM,OAAO,MAAM,OAAO,GAAG;IAAG,MAAM;GAAS,CAAC;GAC9D,QAAQ;GACR,aAAa;GACb;EACF;EAEA,MAAM,UAAU,SAAS,MAAK,cAAa,OAAO,WAAW,WAAW,KAAK,CAAC;EAC9E,IAAI,SAAS;GACX,WAAW,KAAK;GAChB,OAAO,KAAK;IAAE,MAAM;IAAS,MAAM;GAAU,CAAC;GAC9C,SAAS,QAAQ;GACjB,aAAa;GACb;EACF;EAEA,SAAS;CACX;CAEA,WAAW,OAAO,MAAM;CAExB,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC/DA,MAAM,QAAQ;EAYd,MAAM,OAAO;EAEb,MAAM,EAAE,MAAM,2BAA2B;EACzC,MAAM,EAAE,aAAa,aAAa;EAElC,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,cAAc,CAAC;EACtF,MAAM,mBAAmB,mBAAmB,eAAe,kBAAkB,MAAM,UAAU,IAAI;EACjG,MAAM,eAAe,mBAAmB,eAAe,cAAc,MAAM,MAAM,KAAK;EACtF,MAAM,sBAAsB,mBAAmB,eAAe,qBAAqB,MAAM,aAAa,KAAK;;EAG3G,MAAM,SAAS,eAAe,cAAc,MAAM,IAAI,CAAC;;;;;;EAOvD,MAAM,QAAQ,eAAgC;GAC5C,MAAM,OAAO,OAAO,MAAM,MAAM,IAAI;GAEpC,IAAI,MAAM,aAAa,QAAQ,OAAO,KAAK,KAAI,QAAO,CAAC;IAAE,MAAM;IAAK,MAAM;GAAiB,CAAC,CAAC;GAE7F,OAAO,KAAK,KAAI,QAAO,aAAa,GAAG,CAAC;EAC1C,CAAC;;;;;;EAOD,MAAM,aAAa,eAAe,MAAM,cAAc,KAAA,KAAa,CAAC,aAAa,KAAK;EAEtF,MAAM,cAAc,eAAe;GACjC,IAAI,MAAM,cAAc,KAAA,GAAW,OAAO,KAAA;GAE1C,OAAO,EAAE,WAAW,OAAO,MAAM,cAAc,WAAW,GAAG,MAAM,UAAU,MAAM,MAAM,UAAU;EACrG,CAAC;EAED,MAAM,WAAW,eAAe;GAC9B;;GAEA,kBAAkB,aAAa;GAC/B,mBAAmB,aAAa;GAChC,aAAa,QAAQ,qBAAqB;GAC1C,WAAW,QAAQ,uBAAuB;EAC5C,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC;;;;;;EAO3B,MAAM,UAAU,IAAI,KAAK;EAEzB,gBAAgB;GACd,QAAQ,QAAQ,OAAO,cAAc,eAAe,OAAO,UAAU,WAAW,cAAc;EAChG,CAAC;EAED,MAAM,WAAW,eAAe,iBAAiB,SAAS,QAAQ,KAAK;EAEvE,MAAM,SAAS,IAAI,KAAK;EACxB,IAAI,cAA6B;EAEjC,SAAS,mBAAyB;GAChC,IAAI,gBAAgB,MAAM;IACxB,aAAa,WAAW;IACxB,cAAc;GAChB;EACF;EAEA,gBAAgB,gBAAgB;EAEhC,eAAe,OAAsB;GACnC,IAAI;IAGF,MAAM,UAAU,UAAU,UAAU,OAAO,KAAK;GAClD,QACM;IACJ,SAAS,EAAE,2BAA2B,qBAAqB,CAAC;IAC5D;GACF;GAEA,OAAO,QAAQ;GACf,iBAAiB;GACjB,cAAc,OAAO,iBAAiB;IACpC,OAAO,QAAQ;IACf,cAAc;GAChB,GAAG,IAAI;GAEP,SAAS,EAAE,uBAAuB,aAAa,CAAC;GAChD,KAAK,QAAQ,OAAO,KAAK;EAC3B;EAEA,MAAM,YAAY,eAChB,OAAO,QAAQ,EAAE,uBAAuB,aAAa,IAAI,EAAE,qBAAqB,WAAW,CAC5F;;uBAIC,mBA6BM,OAAA;IA7BD,sBAAA;IAAoB,OAAK,eAAA,CAAG,MAAA,kBAAA,GAAoB,SAAA,QAAW,MAAA,oBAAA,IAAoB,EAAA,CAAA;OAE1E,SAAA,SAAA,UAAA,GADR,YAYW,kBAAA;;IAVT,2BAAA;IACA,SAAQ;IACR,MAAK;IACL,QAAA;IACC,OAAK,eAAE,MAAA,kBAAA,CAAkB;IACzB,cAAY,UAAA;IACZ,OAAO,UAAA;IACP,SAAO;;2BAEgF,EAAA,UAAA,GAAxF,YAAwF,wBAAxE,OAAA,QAAS,MAAA,aAAA,IAAY,MAAA,YAAA,CAAQ,GAAA;KAAE,OAAM;KAAc,eAAY;;;;;;;uCASjF,mBAOoR,OAAA;IANlR,6BAAA;IACC,OAAK,eAAA,CAAG,SAAA,OAAU,MAAA,mBAAA,IAAsB,MAAA,sBAAA,IAAa,EAAA,CAAA;IACrD,OAAK,eAAE,YAAA,KAAW;IAClB,UAAU,WAAA,QAAU,IAAO,KAAA;IAC3B,MAAM,QAAA,YAAS,WAAc,KAAA;IAC7B,cAAY,QAAA;OACd,mBAA6Q,QAA7Q,YAA6Q,EAAA,UAAA,IAAA,GAA/O,mBAAwO,UAAA,MAAA,WAAxM,MAAA,QAAlB,QAAQ,UAAK;wBAA3B,mBAAwO,QAAA;KAAhM,KAAK;KAAO,2BAAA;KAAwB,OAAM;0BAAsB,mBAAyH,UAAA,MAAA,WAArF,SAAtB,OAAO,eAAU;yBAA/B,mBAAyH,QAAA;MAA5E,KAAK;MAAa,OAAK,eAAE,MAAA,cAAA,CAAc,CAAC,MAAM,KAAI;wBAAM,MAAM,IAAI,GAAA,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrCodeBlock_default } from "./chunks/GrCodeBlock-
|
|
2
|
-
import { n as grCodeBlockSafelist, t as grCodeBlockConfig } from "../../chunks/config-
|
|
1
|
+
import { t as GrCodeBlock_default } from "./chunks/GrCodeBlock-BmXCLgLS.js";
|
|
2
|
+
import { n as grCodeBlockSafelist, t as grCodeBlockConfig } from "../../chunks/config-B_qi4Fma.js";
|
|
3
3
|
export { GrCodeBlock_default as GrCodeBlock, GrCodeBlock_default as default, grCodeBlockConfig, grCodeBlockSafelist };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as check_default } from "../../../chunks/check-BZI24Qs4.js";
|
|
2
2
|
import { t as GrDropdown_default } from "../../GrDropdown/chunks/GrDropdown-BSfFOcDx.js";
|
|
3
|
-
import { a as columnBaseClass, c as dividerInsetClass, d as grDropdownMenuListClass, f as headerClass, g as textAlignClass, h as listBaseClass, i as colsClass, l as dividersClass, m as itemShortcutClass, o as columnsBaseClass, p as itemIndicatorClass, r as alignClass, s as dividerClass, u as grDropdownMenuItemClass } from "../../../chunks/config-
|
|
3
|
+
import { a as columnBaseClass, c as dividerInsetClass, d as grDropdownMenuListClass, f as headerClass, g as textAlignClass, h as listBaseClass, i as colsClass, l as dividersClass, m as itemShortcutClass, o as columnsBaseClass, p as itemIndicatorClass, r as alignClass, s as dividerClass, u as grDropdownMenuItemClass } from "../../../chunks/config-C5mO5DYZ.js";
|
|
4
4
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, guardReactiveProps, mergeProps, normalizeClass, normalizeProps, openBlock, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useAttrs, useId, useSlots, withCtx } from "vue";
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/components/GrDropdownMenu/GrDropdownMenuDivider.vue
|
|
@@ -279,7 +279,7 @@ var GrDropdownMenu_default = /* @__PURE__ */ defineComponent({
|
|
|
279
279
|
type: Boolean,
|
|
280
280
|
default: true
|
|
281
281
|
},
|
|
282
|
-
contentClass: { default: "
|
|
282
|
+
contentClass: { default: "" },
|
|
283
283
|
items: { default: void 0 },
|
|
284
284
|
dividers: {
|
|
285
285
|
type: Boolean,
|