@feugene/granularity 0.44.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/chunks/config-bjDf8nBf.js +43 -0
- package/dist/chunks/config-bjDf8nBf.js.map +1 -0
- package/dist/chunks/{generated-Hxu_sWPs.js → generated-CQ4h6Ieq.js} +45 -3
- package/dist/chunks/{generated-Hxu_sWPs.js.map → generated-CQ4h6Ieq.js.map} +1 -1
- package/dist/chunks/{granular-provider-CCNbQa5U.js → granular-provider-CsN_R9dB.js} +2 -0
- package/dist/chunks/granular-provider-CsN_R9dB.js.map +1 -0
- package/dist/chunks/useTree-BebEiBcL.js +525 -0
- package/dist/chunks/useTree-BebEiBcL.js.map +1 -0
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-CwCBMh4h.js → GrJsonViewer-C6q8dEEZ.js} +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-CwCBMh4h.js.map → GrJsonViewer-C6q8dEEZ.js.map} +1 -1
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrTree/chunks/{GrTree-CkHzgYg1.js → GrTree-6yZrncJE.js} +137 -540
- package/dist/components/GrTree/chunks/GrTree-6yZrncJE.js.map +1 -0
- package/dist/components/GrTree/index.js +2 -2
- package/dist/components/GrTree/styles.css +1 -1
- package/dist/components/GrTreeSections/chunks/GrTreeSections-DcmDcLwj.js +149 -0
- package/dist/components/GrTreeSections/chunks/GrTreeSections-DcmDcLwj.js.map +1 -0
- package/dist/components/GrTreeSections/index.js +3 -0
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DUsv3_mF.js → GrTreeSelect-DBu5nMGA.js} +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DUsv3_mF.js.map → GrTreeSelect-DBu5nMGA.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/composables/useTree.js +2 -0
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/index.js +7 -4
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrTree/grTreeProps.d.ts +8 -1
- package/dist/types/components/GrTree/grTreeStyles.d.ts +12 -0
- package/dist/types/components/GrTree/index.d.ts +3 -2
- package/dist/types/components/GrTreeSections/GrTreeSections.vue.d.ts +71 -0
- package/dist/types/components/GrTreeSections/config.d.ts +1 -0
- package/dist/types/components/GrTreeSections/grTreeSectionsStyles.d.ts +23 -0
- package/dist/types/components/GrTreeSections/index.d.ts +8 -0
- package/dist/types/components/GrTreeSections/safelist.d.ts +1 -0
- package/dist/types/{components/GrTree/grTreeDataAdapter.d.ts → composables/internal/treeAdapter.d.ts} +3 -3
- package/dist/types/{components/GrTree/grTreeChecking.d.ts → composables/internal/treeChecking.d.ts} +1 -1
- package/dist/types/composables/useTree.d.ts +107 -0
- package/dist/types/granular-provider/shared.d.ts +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/web-types.json +143 -3
- package/package.json +9 -1
- package/dist/chunks/granular-provider-CCNbQa5U.js.map +0 -1
- package/dist/components/GrTree/chunks/GrTree-CkHzgYg1.js.map +0 -1
- package/dist/types/components/GrTree/grTreeStore.d.ts +0 -71
|
@@ -77,6 +77,7 @@ import { t as grToasterConfig } from "./config-B5SBUa6u.js";
|
|
|
77
77
|
import { t as grTooltipConfig } from "./config-iemgzS4x.js";
|
|
78
78
|
import { t as grTransferConfig } from "./config-DmED6aJV.js";
|
|
79
79
|
import { t as grTreeConfig } from "./config-CDat1wXf.js";
|
|
80
|
+
import { t as grTreeSectionsConfig } from "./config-bjDf8nBf.js";
|
|
80
81
|
import { t as grTreeSelectConfig } from "./config-BGCyyXiD.js";
|
|
81
82
|
import { defineGranularComponent, defineGranularProvider, resolvePackageBaseUrl } from "@feugene/unocss-preset-granular/contract";
|
|
82
83
|
//#region src/components/GrDialog/config.ts
|
|
@@ -193,6 +194,7 @@ var granularityComponentConfigs = {
|
|
|
193
194
|
GrTooltip: grTooltipConfig,
|
|
194
195
|
GrTransfer: grTransferConfig,
|
|
195
196
|
GrTree: grTreeConfig,
|
|
197
|
+
GrTreeSections: grTreeSectionsConfig,
|
|
196
198
|
GrTreeSelect: grTreeSelectConfig,
|
|
197
199
|
GrValue: grValueConfig
|
|
198
200
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"granular-provider-CsN_R9dB.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 { grAffixConfig } from '../components/GrAffix/config'\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 { grCarouselConfig } from '../components/GrCarousel/config'\nimport { grCheckboxConfig } from '../components/GrCheckbox/config'\nimport { grCheckboxGroupConfig } from '../components/GrCheckboxGroup/config'\nimport { grChipConfig } from '../components/GrChip/config'\nimport { grChipGroupConfig } from '../components/GrChipGroup/config'\nimport { grCollapseConfig } from '../components/GrCollapse/config'\nimport { grColorPickerConfig } from '../components/GrColorPicker/config'\nimport { grCommandPaletteConfig } from '../components/GrCommandPalette/config'\nimport { grConfigProviderConfig } from '../components/GrConfigProvider/config'\nimport { grConfirmDialogConfig } from '../components/GrConfirmDialog/config'\nimport { grContextMenuConfig } from '../components/GrContextMenu/config'\nimport { grDataTableConfig } from '../components/GrDataTable/config'\nimport { grDeltaConfig } from '../components/GrDelta/config'\nimport { grDescriptionListConfig } from '../components/GrDescriptionList/config'\nimport { grDialogConfig } from '../components/GrDialog/config'\nimport { grDialogServiceConfig } from '../components/GrDialogService/config'\nimport { grDividerConfig } from '../components/GrDivider/config'\nimport { grDrawerConfig } from '../components/GrDrawer/config'\nimport { grDropdownConfig } from '../components/GrDropdown/config'\nimport { grDropdownMenuConfig } from '../components/GrDropdownMenu/config'\nimport { grEmptyStateConfig } from '../components/GrEmptyState/config'\nimport { grFilePreviewConfig } from '../components/GrFilePreview/config'\nimport { grFileUploadConfig } from '../components/GrFileUpload/config'\nimport { grFormConfig } from '../components/GrForm/config'\nimport { grFormFieldConfig } from '../components/GrFormField/config'\nimport { grFormFileConfig } from '../components/GrFormFile/config'\nimport { grFormSectionConfig } from '../components/GrFormSection/config'\nimport { grIconConfig } from '../components/GrIcon/config'\nimport { grImageViewerConfig } from '../components/GrImageViewer/config'\nimport { grInputConfig } from '../components/GrInput/config'\nimport { grInputTagConfig } from '../components/GrInputTag/config'\nimport { grJsonViewerConfig } from '../components/GrJsonViewer/config'\nimport { grKbdConfig } from '../components/GrKbd/config'\nimport { grLinkConfig } from '../components/GrLink/config'\nimport { grListConfig } from '../components/GrList/config'\nimport { grLoadingConfig } from '../components/GrLoading/config'\nimport { grModalConfig } from '../components/GrModal/config'\nimport { grNavbarConfig } from '../components/GrNavbar/config'\nimport { grNumberInputConfig } from '../components/GrNumberInput/config'\nimport { grPaginationConfig } from '../components/GrPagination/config'\nimport { grPopoverConfig } from '../components/GrPopover/config'\nimport { grProgressBarConfig } from '../components/GrProgressBar/config'\nimport { grProgressCircleConfig } from '../components/GrProgressCircle/config'\nimport { grPromptDialogConfig } from '../components/GrPromptDialog/config'\nimport { grRadioConfig } from '../components/GrRadio/config'\nimport { grRadioGroupConfig } from '../components/GrRadioGroup/config'\nimport { grRatingConfig } from '../components/GrRating/config'\nimport { grResponseErrorBannerConfig } from '../components/GrResponseErrorBanner/config'\nimport { grScrollSpyConfig } from '../components/GrScrollSpy/config'\nimport { grSegmentedConfig } from '../components/GrSegmented/config'\nimport { grSelectConfig } from '../components/GrSelect/config'\nimport { grSidebarConfig } from '../components/GrSidebar/config'\nimport { grSkeletonConfig } from '../components/GrSkeleton/config'\nimport { grSliderConfig } from '../components/GrSlider/config'\nimport { grSortableListConfig } from '../components/GrSortableList/config'\nimport { grSplitterConfig } from '../components/GrSplitter/config'\nimport { grStatisticConfig } from '../components/GrStatistic/config'\nimport { grStepsConfig } from '../components/GrSteps/config'\nimport { grSwitchConfig } from '../components/GrSwitch/config'\nimport { grTableConfig } from '../components/GrTable/config'\nimport { grTabPanelsConfig } from '../components/GrTabPanels/config'\nimport { grTabsConfig } from '../components/GrTabs/config'\nimport { grTextareaConfig } from '../components/GrTextarea/config'\nimport { grTimelineConfig } from '../components/GrTimeline/config'\nimport { grToasterConfig } from '../components/GrToaster/config'\nimport { grTooltipConfig } from '../components/GrTooltip/config'\nimport { grTransferConfig } from '../components/GrTransfer/config'\nimport { grTreeConfig } from '../components/GrTree/config'\nimport { grTreeSectionsConfig } from '../components/GrTreeSections/config'\nimport { grTreeSelectConfig } from '../components/GrTreeSelect/config'\nimport { grValueConfig } from '../components/GrValue/config'\n// </granularity:components:imports>\n\n/** Идентификатор провайдера — совпадает с именем пакета. */\nexport const GRANULARITY_PROVIDER_ID = '@feugene/granularity'\n\n// Не заменять на `new URL('..', import.meta.url)`: Vite и rolldown распознают\n// этот литерал и подставляют `data:`-URL, после чего scan-директории пустеют.\nconst packageBaseUrl = resolvePackageBaseUrl(import.meta.url)\n\n/** Встроенные темы пакета. Единственный источник правды о списке тем. */\nexport const granularityThemeNames = ['light', 'dark'] as const\nexport type GranularityThemeName = (typeof granularityThemeNames)[number]\n\n/** Темы, активные по умолчанию, если окружение не переопределяет выбор. */\nexport const granularityDefaultThemes: readonly GranularityThemeName[] = ['light']\n\nconst theme = {\n baseCssUrl: new URL('../styles/base.css', import.meta.url).href,\n tokensCssUrl: new URL('../styles/tokens.css', import.meta.url).href,\n themes: {\n light: new URL('../styles/themes/light.css', import.meta.url).href,\n dark: new URL('../styles/themes/dark.css', import.meta.url).href,\n },\n defaultThemes: granularityDefaultThemes,\n} as const\n\n/**\n * Реестр публичных компонентов. Ключи совпадают с subpath-экспортом\n * `@feugene/granularity/components/<Name>`.\n */\nexport const granularityComponentConfigs = {\n // <granularity:components:registry> — блок генерируется `yarn generate:registry`\n GrAffix: grAffixConfig,\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 GrCarousel: grCarouselConfig,\n GrCheckbox: grCheckboxConfig,\n GrCheckboxGroup: grCheckboxGroupConfig,\n GrChip: grChipConfig,\n GrChipGroup: grChipGroupConfig,\n GrCollapse: grCollapseConfig,\n GrColorPicker: grColorPickerConfig,\n GrCommandPalette: grCommandPaletteConfig,\n GrConfigProvider: grConfigProviderConfig,\n GrConfirmDialog: grConfirmDialogConfig,\n GrContextMenu: grContextMenuConfig,\n GrDataTable: grDataTableConfig,\n GrDelta: grDeltaConfig,\n GrDescriptionList: grDescriptionListConfig,\n GrDialog: grDialogConfig,\n GrDialogService: grDialogServiceConfig,\n GrDivider: grDividerConfig,\n GrDrawer: grDrawerConfig,\n GrDropdown: grDropdownConfig,\n GrDropdownMenu: grDropdownMenuConfig,\n GrEmptyState: grEmptyStateConfig,\n GrFilePreview: grFilePreviewConfig,\n GrFileUpload: grFileUploadConfig,\n GrForm: grFormConfig,\n GrFormField: grFormFieldConfig,\n GrFormFile: grFormFileConfig,\n GrFormSection: grFormSectionConfig,\n GrIcon: grIconConfig,\n GrImageViewer: grImageViewerConfig,\n GrInput: grInputConfig,\n GrInputTag: grInputTagConfig,\n GrJsonViewer: grJsonViewerConfig,\n GrKbd: grKbdConfig,\n GrLink: grLinkConfig,\n GrList: grListConfig,\n GrLoading: grLoadingConfig,\n GrModal: grModalConfig,\n GrNavbar: grNavbarConfig,\n GrNumberInput: grNumberInputConfig,\n GrPagination: grPaginationConfig,\n GrPopover: grPopoverConfig,\n GrProgressBar: grProgressBarConfig,\n GrProgressCircle: grProgressCircleConfig,\n GrPromptDialog: grPromptDialogConfig,\n GrRadio: grRadioConfig,\n GrRadioGroup: grRadioGroupConfig,\n GrRating: grRatingConfig,\n GrResponseErrorBanner: grResponseErrorBannerConfig,\n GrScrollSpy: grScrollSpyConfig,\n GrSegmented: grSegmentedConfig,\n GrSelect: grSelectConfig,\n GrSidebar: grSidebarConfig,\n GrSkeleton: grSkeletonConfig,\n GrSlider: grSliderConfig,\n GrSortableList: grSortableListConfig,\n GrSplitter: grSplitterConfig,\n GrStatistic: grStatisticConfig,\n GrSteps: grStepsConfig,\n GrSwitch: grSwitchConfig,\n GrTable: grTableConfig,\n GrTabPanels: grTabPanelsConfig,\n GrTabs: grTabsConfig,\n GrTextarea: grTextareaConfig,\n GrTimeline: grTimelineConfig,\n GrToaster: grToasterConfig,\n GrTooltip: grTooltipConfig,\n GrTransfer: grTransferConfig,\n GrTree: grTreeConfig,\n GrTreeSections: grTreeSectionsConfig,\n GrTreeSelect: grTreeSelectConfig,\n GrValue: grValueConfig,\n // </granularity:components:registry>\n}\n\nexport type GranularityComponentName = keyof typeof granularityComponentConfigs\n\n/** Базовый набор компонентов в порядке реестра. */\nconst baseComponents: readonly GranularComponentDescriptor[] = Object.values(\n granularityComponentConfigs,\n)\n\n/**\n * Собирает granular-provider пакета.\n *\n * `overrides` — точка расширения для потребителя: дескриптор с именем из\n * базового реестра заменяет его, остальные дописываются в конец.\n */\nexport function createGranularityProvider(\n overrides: readonly GranularComponentDescriptor[] = [],\n): GranularProvider {\n const overrideByName = new Map(\n overrides.map(component => [component.name, component]),\n )\n const components: GranularComponentDescriptor[] = [\n ...baseComponents.map(component => overrideByName.get(component.name) ?? component),\n ...overrides.filter(component => !baseComponents.some(base => base.name === component.name)),\n ]\n\n return defineGranularProvider({\n id: GRANULARITY_PROVIDER_ID,\n contractVersion: 1,\n packageBaseUrl,\n components,\n theme,\n })\n}\n","import { createGranularityProvider } from './shared'\n\nexport * from './shared'\n\nexport const granularityProvider = createGranularityProvider()\n\nexport default granularityProvider\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAa,iBAAiB,wBAAwB,YAAY,KAAK;CACrE,MAAM;CACN,cAAc,CAAC,YAAY,SAAS;CACpC,UAAU;AACZ,CAAC;;;ACND,IAAa,8BAA8B,wBAAwB,YAAY,KAAK;CAClF,MAAM;CACN,cAAc,CAAC,WAAW,UAAU;AACtC,CAAC;;;;ACgGD,IAAa,0BAA0B;AAIvC,IAAM,iBAAiB,sBAAsB,YAAY,GAAG;;AAG5D,IAAa,wBAAwB,CAAC,SAAS,MAAM;;AAIrD,IAAa,2BAA4D,CAAC,OAAO;AAEjF,IAAM,QAAQ;CACZ,YAAY,IAAA,IAAA,q5KAAA,KAAA,YAAA,GAAA,CAA8C,CAAC;CAC3D,cAAc,IAAA,IAAA,65YAAA,KAAA,YAAA,GAAA,CAAgD,CAAC;CAC/D,QAAQ;EACN,OAAO,IAAA,IAAA,iyYAAA,KAAA,YAAA,GAAA,CAAsD,CAAC;EAC9D,MAAM,IAAA,IAAA,y/SAAA,KAAA,YAAA,GAAA,CAAqD,CAAC;CAC9D;CACA,eAAe;AACjB;;;;;AAMA,IAAa,8BAA8B;CAEzC,SAAS;CACT,SAAS;CACT,gBAAgB;CAChB,UAAU;CACV,SAAS;CACT,aAAa;CACb,aAAa;CACb,eAAe;CACf,UAAU;CACV,eAAe;CACf,QAAQ;CACR,YAAY;CACZ,YAAY;CACZ,iBAAiB;CACjB,QAAQ;CACR,aAAa;CACb,YAAY;CACZ,eAAe;CACf,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,aAAa;CACb,SAAS;CACT,mBAAmB;CACnB,UAAU;CACV,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,YAAY;CACZ,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,cAAc;CACd,QAAQ;CACR,aAAa;CACb,YAAY;CACZ,eAAe;CACf,QAAQ;CACR,eAAe;CACf,SAAS;CACT,YAAY;CACZ,cAAc;CACd,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,WAAW;CACX,SAAS;CACT,UAAU;CACV,eAAe;CACf,cAAc;CACd,WAAW;CACX,eAAe;CACf,kBAAkB;CAClB,gBAAgB;CAChB,SAAS;CACT,cAAc;CACd,UAAU;CACV,uBAAuB;CACvB,aAAa;CACb,aAAa;CACb,UAAU;CACV,WAAW;CACX,YAAY;CACZ,UAAU;CACV,gBAAgB;CAChB,YAAY;CACZ,aAAa;CACb,SAAS;CACT,UAAU;CACV,SAAS;CACT,aAAa;CACb,QAAQ;CACR,YAAY;CACZ,YAAY;CACZ,WAAW;CACX,WAAW;CACX,YAAY;CACZ,QAAQ;CACR,gBAAgB;CAChB,cAAc;CACd,SAAS;AAEX;;AAKA,IAAM,iBAAyD,OAAO,OACpE,2BACF;;;;;;;AAQA,SAAgB,0BACd,YAAoD,CAAC,GACnC;CAClB,MAAM,iBAAiB,IAAI,IACzB,UAAU,KAAI,cAAa,CAAC,UAAU,MAAM,SAAS,CAAC,CACxD;CACA,MAAM,aAA4C,CAChD,GAAG,eAAe,KAAI,cAAa,eAAe,IAAI,UAAU,IAAI,KAAK,SAAS,GAClF,GAAG,UAAU,QAAO,cAAa,CAAC,eAAe,MAAK,SAAQ,KAAK,SAAS,UAAU,IAAI,CAAC,CAC7F;CAEA,OAAO,uBAAuB;EAC5B,IAAI;EACJ,iBAAiB;EACjB;EACA;EACA;CACF,CAAC;AACH;;;AClPA,IAAa,sBAAsB,0BAA0B"}
|
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { computed, ref, shallowRef, toValue, watch } from "vue";
|
|
2
|
+
//#region src/composables/internal/treeAdapter.ts
|
|
3
|
+
var DEFAULT_CHILDREN_KEY = "children";
|
|
4
|
+
var DEFAULT_LABEL_KEY = "label";
|
|
5
|
+
var DEFAULT_IS_LEAF_KEY = "isLeaf";
|
|
6
|
+
function isTreeKey(value) {
|
|
7
|
+
return typeof value === "string" || typeof value === "number";
|
|
8
|
+
}
|
|
9
|
+
function resolveMapValue(map, key, fallback) {
|
|
10
|
+
return map?.[key] ?? fallback;
|
|
11
|
+
}
|
|
12
|
+
function createGrTreeDataAdapter(options) {
|
|
13
|
+
function hasNodeKey() {
|
|
14
|
+
return options.nodeKey != null;
|
|
15
|
+
}
|
|
16
|
+
function getChildrenKey() {
|
|
17
|
+
return resolveMapValue(options.props, "children", DEFAULT_CHILDREN_KEY);
|
|
18
|
+
}
|
|
19
|
+
function getLabelKey() {
|
|
20
|
+
return resolveMapValue(options.props, "label", DEFAULT_LABEL_KEY);
|
|
21
|
+
}
|
|
22
|
+
function getLabel(data) {
|
|
23
|
+
const value = data[getLabelKey()];
|
|
24
|
+
return value == null ? "" : String(value);
|
|
25
|
+
}
|
|
26
|
+
function getIsLeaf(data) {
|
|
27
|
+
const value = data[resolveMapValue(options.props, "isLeaf", DEFAULT_IS_LEAF_KEY)];
|
|
28
|
+
return typeof value === "boolean" ? value : void 0;
|
|
29
|
+
}
|
|
30
|
+
function getChildren(data) {
|
|
31
|
+
const value = data[getChildrenKey()];
|
|
32
|
+
return Array.isArray(value) ? value : [];
|
|
33
|
+
}
|
|
34
|
+
function ensureChildren(data) {
|
|
35
|
+
const key = getChildrenKey();
|
|
36
|
+
const record = data;
|
|
37
|
+
if (Array.isArray(record[key])) return record[key];
|
|
38
|
+
record[key] = [];
|
|
39
|
+
return record[key];
|
|
40
|
+
}
|
|
41
|
+
function getExplicitNodeKey(data) {
|
|
42
|
+
if (options.nodeKey == null) return void 0;
|
|
43
|
+
const value = data[options.nodeKey];
|
|
44
|
+
return isTreeKey(value) ? value : void 0;
|
|
45
|
+
}
|
|
46
|
+
function getNodeKey(data, index, parentKey) {
|
|
47
|
+
const explicitKey = getExplicitNodeKey(data);
|
|
48
|
+
if (explicitKey != null) return explicitKey;
|
|
49
|
+
return parentKey == null ? index : `${String(parentKey)}:${index}`;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
hasNodeKey,
|
|
53
|
+
getChildrenKey,
|
|
54
|
+
getLabelKey,
|
|
55
|
+
getLabel,
|
|
56
|
+
getIsLeaf,
|
|
57
|
+
getChildren,
|
|
58
|
+
ensureChildren,
|
|
59
|
+
getNodeKey,
|
|
60
|
+
getExplicitNodeKey
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/composables/internal/treeChecking.ts
|
|
65
|
+
/**
|
|
66
|
+
* Раскладывает отмеченные ключи по всему дереву: родитель отмечен, когда
|
|
67
|
+
* отмечены все дети, и наполовину — когда часть.
|
|
68
|
+
*
|
|
69
|
+
* Считается от листьев вверх, поэтому `checkedKeys` снаружи может содержать
|
|
70
|
+
* только листья (типовой формат ответа сервера) — родители посчитаются сами.
|
|
71
|
+
* При `strict` дерево не связывает узлы вовсе: каждый отвечает сам за себя.
|
|
72
|
+
*/
|
|
73
|
+
function resolveCheckStates(roots, checkedKeys, strict) {
|
|
74
|
+
const states = /* @__PURE__ */ new Map();
|
|
75
|
+
const walk = (node) => {
|
|
76
|
+
if (strict || node.childNodes.length === 0) {
|
|
77
|
+
const state = checkedKeys.has(node.key) ? "checked" : "unchecked";
|
|
78
|
+
if (strict) node.childNodes.forEach(walk);
|
|
79
|
+
states.set(node.key, state);
|
|
80
|
+
return state;
|
|
81
|
+
}
|
|
82
|
+
const childStates = node.childNodes.map(walk);
|
|
83
|
+
const allChecked = childStates.every((s) => s === "checked");
|
|
84
|
+
const someMarked = childStates.some((s) => s !== "unchecked");
|
|
85
|
+
const state = allChecked ? "checked" : someMarked || checkedKeys.has(node.key) ? "half" : "unchecked";
|
|
86
|
+
states.set(node.key, state);
|
|
87
|
+
return state;
|
|
88
|
+
};
|
|
89
|
+
roots.forEach(walk);
|
|
90
|
+
return states;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Новый набор отмеченных ключей после переключения узла.
|
|
94
|
+
*
|
|
95
|
+
* Каскад идёт вниз (все потомки повторяют решение) и вверх (родитель отмечен,
|
|
96
|
+
* только если отмечены все его дети). Возвращается набор с родителями —
|
|
97
|
+
* `getCheckedKeys({ leafOnly: true })` при необходимости отбросит их обратно.
|
|
98
|
+
*/
|
|
99
|
+
function toggleCheckedKeys(roots, checkedKeys, node, checked, strict) {
|
|
100
|
+
const next = new Set(checkedKeys);
|
|
101
|
+
if (strict) {
|
|
102
|
+
if (checked) next.add(node.key);
|
|
103
|
+
else next.delete(node.key);
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
106
|
+
const applyDown = (target) => {
|
|
107
|
+
if (checked) next.add(target.key);
|
|
108
|
+
else next.delete(target.key);
|
|
109
|
+
target.childNodes.forEach(applyDown);
|
|
110
|
+
};
|
|
111
|
+
applyDown(node);
|
|
112
|
+
for (let parent = node.parent; parent; parent = parent.parent) if (parent.childNodes.every((child) => next.has(child.key))) next.add(parent.key);
|
|
113
|
+
else next.delete(parent.key);
|
|
114
|
+
return pruneToTree(roots, next);
|
|
115
|
+
}
|
|
116
|
+
/** Отбрасывает ключи, которых нет в текущей модели (данные могли смениться). */
|
|
117
|
+
function pruneToTree(roots, keys) {
|
|
118
|
+
const known = /* @__PURE__ */ new Set();
|
|
119
|
+
const walk = (node) => {
|
|
120
|
+
known.add(node.key);
|
|
121
|
+
node.childNodes.forEach(walk);
|
|
122
|
+
};
|
|
123
|
+
roots.forEach(walk);
|
|
124
|
+
const next = /* @__PURE__ */ new Set();
|
|
125
|
+
for (const key of keys) if (known.has(key)) next.add(key);
|
|
126
|
+
return next;
|
|
127
|
+
}
|
|
128
|
+
/** Ключи в состоянии `checked`; `leafOnly` оставляет только листья. */
|
|
129
|
+
function collectCheckedKeys(roots, states, leafOnly) {
|
|
130
|
+
const out = [];
|
|
131
|
+
const walk = (node) => {
|
|
132
|
+
const isLeaf = node.childNodes.length === 0;
|
|
133
|
+
if (states.get(node.key) === "checked" && (!leafOnly || isLeaf)) out.push(node.key);
|
|
134
|
+
node.childNodes.forEach(walk);
|
|
135
|
+
};
|
|
136
|
+
roots.forEach(walk);
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/** Ключи в состоянии `half` — родители с частично отмеченными потомками. */
|
|
140
|
+
function collectHalfCheckedKeys(states) {
|
|
141
|
+
const out = [];
|
|
142
|
+
for (const [key, state] of states) if (state === "half") out.push(key);
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/composables/useTree.ts
|
|
147
|
+
function useTree(options) {
|
|
148
|
+
const adapter = options.adapter ?? createGrTreeDataAdapter({
|
|
149
|
+
props: options.props,
|
|
150
|
+
nodeKey: options.nodeKey
|
|
151
|
+
});
|
|
152
|
+
const filterValue = ref("");
|
|
153
|
+
const expandedKeys = shallowRef(/* @__PURE__ */ new Set());
|
|
154
|
+
const checkedKeys = shallowRef(new Set(toValue(options.defaultCheckedKeys) ?? []));
|
|
155
|
+
const loadingKeys = shallowRef(/* @__PURE__ */ new Set());
|
|
156
|
+
const loadedKeys = shallowRef(/* @__PURE__ */ new Set());
|
|
157
|
+
const loadedChildren = shallowRef(/* @__PURE__ */ new Map());
|
|
158
|
+
const currentKey = ref(void 0);
|
|
159
|
+
const getData = () => toValue(options.data) ?? [];
|
|
160
|
+
const getFilterNodeMethod = () => options.filterNodeMethod;
|
|
161
|
+
watch(() => toValue(options.defaultExpandedKeys), (keys) => {
|
|
162
|
+
expandedKeys.value = new Set(keys ?? []);
|
|
163
|
+
}, { immediate: true });
|
|
164
|
+
watch(() => toValue(options.checkedKeys), (keys) => {
|
|
165
|
+
if (keys) checkedKeys.value = new Set(keys);
|
|
166
|
+
}, { immediate: true });
|
|
167
|
+
const treeModel = computed(() => {
|
|
168
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
169
|
+
const byData = /* @__PURE__ */ new WeakMap();
|
|
170
|
+
const build = (items, parent, level) => {
|
|
171
|
+
return items.map((item, index) => {
|
|
172
|
+
const node = {
|
|
173
|
+
key: adapter.getNodeKey(item, index, parent?.key),
|
|
174
|
+
label: adapter.getLabel(item),
|
|
175
|
+
data: item,
|
|
176
|
+
level,
|
|
177
|
+
parent,
|
|
178
|
+
childNodes: []
|
|
179
|
+
};
|
|
180
|
+
byKey.set(node.key, node);
|
|
181
|
+
byData.set(item, node);
|
|
182
|
+
const ownChildren = adapter.getChildren(item);
|
|
183
|
+
const lazyChildren = loadedChildren.value.get(node.key) ?? [];
|
|
184
|
+
node.childNodes = build([...ownChildren, ...lazyChildren], node, level + 1);
|
|
185
|
+
return node;
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
return {
|
|
189
|
+
roots: build(getData(), void 0, 1),
|
|
190
|
+
byKey,
|
|
191
|
+
byData
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
const autoExpandedKeys = /* @__PURE__ */ new Set();
|
|
195
|
+
watch(treeModel, (model) => {
|
|
196
|
+
if (!toValue(options.defaultExpandAll)) return;
|
|
197
|
+
const next = new Set(expandedKeys.value);
|
|
198
|
+
let changed = false;
|
|
199
|
+
for (const key of model.byKey.keys()) {
|
|
200
|
+
if (autoExpandedKeys.has(key)) continue;
|
|
201
|
+
autoExpandedKeys.add(key);
|
|
202
|
+
next.add(key);
|
|
203
|
+
changed = true;
|
|
204
|
+
}
|
|
205
|
+
if (changed) expandedKeys.value = next;
|
|
206
|
+
}, { immediate: true });
|
|
207
|
+
const filterInfo = computed(() => {
|
|
208
|
+
const value = filterValue.value;
|
|
209
|
+
const isActive = value.trim().length > 0;
|
|
210
|
+
const valueLower = value.toLowerCase();
|
|
211
|
+
const subtreeHasMatch = /* @__PURE__ */ new Map();
|
|
212
|
+
const matchedKeys = /* @__PURE__ */ new Set();
|
|
213
|
+
const autoExpandKeys = /* @__PURE__ */ new Set();
|
|
214
|
+
if (!isActive) return {
|
|
215
|
+
isActive,
|
|
216
|
+
subtreeHasMatch,
|
|
217
|
+
matchedKeys,
|
|
218
|
+
autoExpandKeys
|
|
219
|
+
};
|
|
220
|
+
const match = (node) => {
|
|
221
|
+
const custom = getFilterNodeMethod()?.(value, node.data, node);
|
|
222
|
+
if (custom !== void 0) return custom;
|
|
223
|
+
return node.label.toLowerCase().includes(valueLower);
|
|
224
|
+
};
|
|
225
|
+
const walk = (nodes) => {
|
|
226
|
+
let any = false;
|
|
227
|
+
for (const node of nodes) {
|
|
228
|
+
const self = match(node);
|
|
229
|
+
const childAny = walk(node.childNodes);
|
|
230
|
+
const has = self || childAny;
|
|
231
|
+
subtreeHasMatch.set(node.key, has);
|
|
232
|
+
if (self) matchedKeys.add(node.key);
|
|
233
|
+
if (childAny) autoExpandKeys.add(node.key);
|
|
234
|
+
any ||= has;
|
|
235
|
+
}
|
|
236
|
+
return any;
|
|
237
|
+
};
|
|
238
|
+
walk(treeModel.value.roots);
|
|
239
|
+
return {
|
|
240
|
+
isActive,
|
|
241
|
+
subtreeHasMatch,
|
|
242
|
+
matchedKeys,
|
|
243
|
+
autoExpandKeys
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
function getNode(key) {
|
|
247
|
+
return treeModel.value.byKey.get(key);
|
|
248
|
+
}
|
|
249
|
+
function findNodeByData(data) {
|
|
250
|
+
return treeModel.value.byData.get(data);
|
|
251
|
+
}
|
|
252
|
+
function isTreeNode(target) {
|
|
253
|
+
return typeof target === "object" && target !== null && "key" in target && "data" in target && "childNodes" in target;
|
|
254
|
+
}
|
|
255
|
+
function resolveNodeTarget(target) {
|
|
256
|
+
if (target == null) return void 0;
|
|
257
|
+
if (typeof target === "string" || typeof target === "number") return getNode(target);
|
|
258
|
+
if (isTreeNode(target)) return getNode(target.key) ?? target;
|
|
259
|
+
const key = adapter.getExplicitNodeKey(target);
|
|
260
|
+
if (key != null) return getNode(key);
|
|
261
|
+
return findNodeByData(target);
|
|
262
|
+
}
|
|
263
|
+
function getNodeContainer(node) {
|
|
264
|
+
const items = node.parent ? adapter.ensureChildren(node.parent.data) : getData();
|
|
265
|
+
let index = items.findIndex((item) => item === node.data);
|
|
266
|
+
if (index >= 0) return {
|
|
267
|
+
items,
|
|
268
|
+
index
|
|
269
|
+
};
|
|
270
|
+
const key = adapter.getExplicitNodeKey(node.data);
|
|
271
|
+
if (key == null) return void 0;
|
|
272
|
+
index = items.findIndex((item) => adapter.getExplicitNodeKey(item) === key);
|
|
273
|
+
if (index < 0) return void 0;
|
|
274
|
+
return {
|
|
275
|
+
items,
|
|
276
|
+
index
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function isDescendantNode(ancestor, node) {
|
|
280
|
+
let current = node.parent;
|
|
281
|
+
while (current) {
|
|
282
|
+
if (current.key === ancestor.key) return true;
|
|
283
|
+
current = current.parent;
|
|
284
|
+
}
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
function canMoveNode(node, referenceNode, dropType) {
|
|
288
|
+
const sourceNode = resolveNodeTarget(node);
|
|
289
|
+
const targetNode = resolveNodeTarget(referenceNode);
|
|
290
|
+
if (!sourceNode || !targetNode) return false;
|
|
291
|
+
if (sourceNode.key === targetNode.key) return false;
|
|
292
|
+
if (isDescendantNode(sourceNode, targetNode)) return false;
|
|
293
|
+
if (!getNodeContainer(sourceNode)) return false;
|
|
294
|
+
if (dropType === "inner") return true;
|
|
295
|
+
return Boolean(getNodeContainer(targetNode));
|
|
296
|
+
}
|
|
297
|
+
function sanitizeTreeState() {
|
|
298
|
+
const byKey = treeModel.value.byKey;
|
|
299
|
+
if (currentKey.value != null && !byKey.has(currentKey.value)) currentKey.value = void 0;
|
|
300
|
+
const nextExpandedKeys = new Set(Array.from(expandedKeys.value).filter((key) => byKey.has(key)));
|
|
301
|
+
if (nextExpandedKeys.size !== expandedKeys.value.size) expandedKeys.value = nextExpandedKeys;
|
|
302
|
+
const nextCheckedKeys = new Set(Array.from(checkedKeys.value).filter((key) => byKey.has(key)));
|
|
303
|
+
if (nextCheckedKeys.size !== checkedKeys.value.size) checkedKeys.value = nextCheckedKeys;
|
|
304
|
+
}
|
|
305
|
+
function isExpandedKey(key) {
|
|
306
|
+
return expandedKeys.value.has(key);
|
|
307
|
+
}
|
|
308
|
+
function setExpandedKey(key, expanded) {
|
|
309
|
+
const next = new Set(expandedKeys.value);
|
|
310
|
+
if (expanded) next.add(key);
|
|
311
|
+
else next.delete(key);
|
|
312
|
+
expandedKeys.value = next;
|
|
313
|
+
}
|
|
314
|
+
function toggleExpand(node) {
|
|
315
|
+
setExpandedKey(node.key, !isExpandedKey(node.key));
|
|
316
|
+
}
|
|
317
|
+
function setCurrentKey(key) {
|
|
318
|
+
currentKey.value = key;
|
|
319
|
+
}
|
|
320
|
+
function getCurrentKey() {
|
|
321
|
+
return currentKey.value;
|
|
322
|
+
}
|
|
323
|
+
function getCurrentNode() {
|
|
324
|
+
const key = currentKey.value;
|
|
325
|
+
return key == null ? void 0 : getNode(key);
|
|
326
|
+
}
|
|
327
|
+
function setCurrentNode(node) {
|
|
328
|
+
if (node == null) {
|
|
329
|
+
currentKey.value = void 0;
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
const targetNode = resolveNodeTarget(node);
|
|
333
|
+
if (!targetNode) return false;
|
|
334
|
+
currentKey.value = targetNode.key;
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
function resolveInsertedNode(data) {
|
|
338
|
+
const key = adapter.getExplicitNodeKey(data);
|
|
339
|
+
if (key != null) return getNode(key);
|
|
340
|
+
return findNodeByData(data);
|
|
341
|
+
}
|
|
342
|
+
function moveNode(node, referenceNode, dropType) {
|
|
343
|
+
const sourceNode = resolveNodeTarget(node);
|
|
344
|
+
const targetNode = resolveNodeTarget(referenceNode);
|
|
345
|
+
if (!sourceNode || !targetNode) return void 0;
|
|
346
|
+
if (!canMoveNode(sourceNode, targetNode, dropType)) return void 0;
|
|
347
|
+
const sourceContainer = getNodeContainer(sourceNode);
|
|
348
|
+
if (!sourceContainer) return void 0;
|
|
349
|
+
let destinationItems;
|
|
350
|
+
let destinationIndex;
|
|
351
|
+
if (dropType === "inner") {
|
|
352
|
+
destinationItems = adapter.ensureChildren(targetNode.data);
|
|
353
|
+
destinationIndex = destinationItems.length;
|
|
354
|
+
} else {
|
|
355
|
+
const targetContainer = getNodeContainer(targetNode);
|
|
356
|
+
if (!targetContainer) return void 0;
|
|
357
|
+
destinationItems = targetContainer.items;
|
|
358
|
+
destinationIndex = dropType === "prev" ? targetContainer.index : targetContainer.index + 1;
|
|
359
|
+
}
|
|
360
|
+
if (destinationItems === sourceContainer.items && sourceContainer.index < destinationIndex) destinationIndex -= 1;
|
|
361
|
+
const [movedData] = sourceContainer.items.splice(sourceContainer.index, 1);
|
|
362
|
+
destinationItems.splice(destinationIndex, 0, movedData);
|
|
363
|
+
if (dropType === "inner") setExpandedKey(targetNode.key, true);
|
|
364
|
+
sanitizeTreeState();
|
|
365
|
+
return resolveInsertedNode(movedData);
|
|
366
|
+
}
|
|
367
|
+
function appendNode(data, parent) {
|
|
368
|
+
const parentNode = resolveNodeTarget(parent);
|
|
369
|
+
if (!parentNode) return void 0;
|
|
370
|
+
adapter.ensureChildren(parentNode.data).push(data);
|
|
371
|
+
setExpandedKey(parentNode.key, true);
|
|
372
|
+
sanitizeTreeState();
|
|
373
|
+
return resolveInsertedNode(data);
|
|
374
|
+
}
|
|
375
|
+
function removeNode(node) {
|
|
376
|
+
if (!adapter.hasNodeKey()) return false;
|
|
377
|
+
const targetNode = resolveNodeTarget(node);
|
|
378
|
+
if (!targetNode) return false;
|
|
379
|
+
const container = getNodeContainer(targetNode);
|
|
380
|
+
if (!container) return false;
|
|
381
|
+
container.items.splice(container.index, 1);
|
|
382
|
+
sanitizeTreeState();
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
function insertNodeBefore(data, referenceNode) {
|
|
386
|
+
const targetNode = resolveNodeTarget(referenceNode);
|
|
387
|
+
if (!targetNode) return void 0;
|
|
388
|
+
const container = getNodeContainer(targetNode);
|
|
389
|
+
if (!container) return void 0;
|
|
390
|
+
container.items.splice(container.index, 0, data);
|
|
391
|
+
sanitizeTreeState();
|
|
392
|
+
return resolveInsertedNode(data);
|
|
393
|
+
}
|
|
394
|
+
function insertNodeAfter(data, referenceNode) {
|
|
395
|
+
const targetNode = resolveNodeTarget(referenceNode);
|
|
396
|
+
if (!targetNode) return void 0;
|
|
397
|
+
const container = getNodeContainer(targetNode);
|
|
398
|
+
if (!container) return void 0;
|
|
399
|
+
container.items.splice(container.index + 1, 0, data);
|
|
400
|
+
sanitizeTreeState();
|
|
401
|
+
return resolveInsertedNode(data);
|
|
402
|
+
}
|
|
403
|
+
const checkStates = computed(() => resolveCheckStates(treeModel.value.roots, checkedKeys.value, Boolean(toValue(options.checkStrictly))));
|
|
404
|
+
function getCheckState(key) {
|
|
405
|
+
return checkStates.value.get(key) ?? "unchecked";
|
|
406
|
+
}
|
|
407
|
+
function setChecked(node, checked) {
|
|
408
|
+
const targetNode = resolveNodeTarget(node);
|
|
409
|
+
if (!targetNode) return void 0;
|
|
410
|
+
checkedKeys.value = toggleCheckedKeys(treeModel.value.roots, checkedKeys.value, targetNode, checked, Boolean(toValue(options.checkStrictly)));
|
|
411
|
+
return targetNode;
|
|
412
|
+
}
|
|
413
|
+
function toggleChecked(node) {
|
|
414
|
+
return setChecked(node, getCheckState(node.key) !== "checked");
|
|
415
|
+
}
|
|
416
|
+
function getCheckedKeys(opts) {
|
|
417
|
+
return collectCheckedKeys(treeModel.value.roots, checkStates.value, Boolean(opts?.leafOnly));
|
|
418
|
+
}
|
|
419
|
+
function setCheckedKeys(keys) {
|
|
420
|
+
checkedKeys.value = pruneToTree(treeModel.value.roots, new Set(keys));
|
|
421
|
+
}
|
|
422
|
+
function getHalfCheckedKeys() {
|
|
423
|
+
return collectHalfCheckedKeys(checkStates.value);
|
|
424
|
+
}
|
|
425
|
+
function isLeafNode(node) {
|
|
426
|
+
if (node.childNodes.length > 0) return false;
|
|
427
|
+
if (!toValue(options.lazy)) return true;
|
|
428
|
+
const declared = adapter.getIsLeaf(node.data);
|
|
429
|
+
if (declared !== void 0) return declared;
|
|
430
|
+
return loadedKeys.value.has(node.key);
|
|
431
|
+
}
|
|
432
|
+
function isLoadingKey(key) {
|
|
433
|
+
return loadingKeys.value.has(key);
|
|
434
|
+
}
|
|
435
|
+
function markLoading(key, loading) {
|
|
436
|
+
const next = new Set(loadingKeys.value);
|
|
437
|
+
if (loading) next.add(key);
|
|
438
|
+
else next.delete(key);
|
|
439
|
+
loadingKeys.value = next;
|
|
440
|
+
}
|
|
441
|
+
function ensureLoaded(node) {
|
|
442
|
+
const load = options.load;
|
|
443
|
+
if (!toValue(options.lazy) || !load) return;
|
|
444
|
+
if (loadedKeys.value.has(node.key) || loadingKeys.value.has(node.key) || isLeafNode(node)) return;
|
|
445
|
+
markLoading(node.key, true);
|
|
446
|
+
let settled = false;
|
|
447
|
+
load(node, (children) => {
|
|
448
|
+
if (settled) return;
|
|
449
|
+
settled = true;
|
|
450
|
+
const next = new Map(loadedChildren.value);
|
|
451
|
+
next.set(node.key, [...children ?? []]);
|
|
452
|
+
loadedChildren.value = next;
|
|
453
|
+
loadedKeys.value = new Set(loadedKeys.value).add(node.key);
|
|
454
|
+
markLoading(node.key, false);
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
function filter(value) {
|
|
458
|
+
filterValue.value = value;
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
visibleRows: computed(() => {
|
|
462
|
+
const { roots } = treeModel.value;
|
|
463
|
+
const { isActive, subtreeHasMatch, matchedKeys, autoExpandKeys } = filterInfo.value;
|
|
464
|
+
const rows = [];
|
|
465
|
+
const walk = (nodes, ancestorColors) => {
|
|
466
|
+
const siblings = isActive ? nodes.filter((node) => subtreeHasMatch.get(node.key)) : nodes;
|
|
467
|
+
siblings.forEach((node, index) => {
|
|
468
|
+
const isLeaf = isLeafNode(node);
|
|
469
|
+
const isExpanded = isExpandedKey(node.key) || isActive && autoExpandKeys.has(node.key);
|
|
470
|
+
rows.push({
|
|
471
|
+
node,
|
|
472
|
+
isExpanded,
|
|
473
|
+
isLeaf,
|
|
474
|
+
isMatched: matchedKeys.has(node.key),
|
|
475
|
+
posInSet: index + 1,
|
|
476
|
+
setSize: siblings.length,
|
|
477
|
+
isLoading: isLoadingKey(node.key),
|
|
478
|
+
branchColors: ancestorColors
|
|
479
|
+
});
|
|
480
|
+
if (!isExpanded || node.childNodes.length === 0) return;
|
|
481
|
+
const color = options.branchColorFor?.(node);
|
|
482
|
+
walk(node.childNodes, color ? [...ancestorColors, color] : ancestorColors);
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
walk(roots, []);
|
|
486
|
+
return rows;
|
|
487
|
+
}),
|
|
488
|
+
filterValue,
|
|
489
|
+
expandedKeys,
|
|
490
|
+
loadedChildren,
|
|
491
|
+
checkedKeys,
|
|
492
|
+
checkStates,
|
|
493
|
+
loadingKeys,
|
|
494
|
+
loadedKeys,
|
|
495
|
+
currentKey,
|
|
496
|
+
treeModel,
|
|
497
|
+
filterInfo,
|
|
498
|
+
filter,
|
|
499
|
+
isExpandedKey,
|
|
500
|
+
setExpandedKey,
|
|
501
|
+
toggleExpand,
|
|
502
|
+
setCurrentKey,
|
|
503
|
+
getCurrentKey,
|
|
504
|
+
getCurrentNode,
|
|
505
|
+
setCurrentNode,
|
|
506
|
+
getNode,
|
|
507
|
+
canMoveNode,
|
|
508
|
+
moveNode,
|
|
509
|
+
appendNode,
|
|
510
|
+
removeNode,
|
|
511
|
+
insertNodeBefore,
|
|
512
|
+
insertNodeAfter,
|
|
513
|
+
getCheckState,
|
|
514
|
+
setChecked,
|
|
515
|
+
toggleChecked,
|
|
516
|
+
getCheckedKeys,
|
|
517
|
+
setCheckedKeys,
|
|
518
|
+
getHalfCheckedKeys,
|
|
519
|
+
isLeafNode,
|
|
520
|
+
isLoadingKey,
|
|
521
|
+
ensureLoaded
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
//#endregion
|
|
525
|
+
export { createGrTreeDataAdapter as n, useTree as t };
|