adata-ui 2.1.40-beta.11 → 2.1.40-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/assets/styles/index.scss +67 -5
  2. package/components/elements/a-select-row/ASelectRowV2.vue +8 -8
  3. package/components/elements/a-select-row/index.vue +3 -5
  4. package/components/elements/accordion/AAccordion.vue +5 -3
  5. package/components/elements/accordion/AAccordionGroup/AAccordionGroup.vue +3 -3
  6. package/components/elements/alert/AAlert.vue +34 -47
  7. package/components/elements/bg-block/ABgBlock.vue +1 -1
  8. package/components/elements/button/AButton.vue +3 -3
  9. package/components/elements/button/AButtonV2.vue +7 -5
  10. package/components/elements/button-login/index.vue +1 -1
  11. package/components/elements/chip-wrapper/AChipWrapper.vue +1 -1
  12. package/components/elements/chips/AChips.vue +2 -2
  13. package/components/elements/digit-badge/ADigitBadge.vue +21 -19
  14. package/components/elements/feature-description/AFeatureDescription.vue +1 -1
  15. package/components/elements/leave-note/ALeaveNote.vue +1 -1
  16. package/components/elements/pagination/APagination.vue +7 -7
  17. package/components/elements/segmented/ASegmentedV2.vue +103 -10
  18. package/components/elements/select/ASelect.vue +4 -4
  19. package/components/elements/select/ASelectMobile.vue +2 -2
  20. package/components/elements/select/ASelectV2.vue +10 -11
  21. package/components/elements/select/ui/select-button.vue +1 -1
  22. package/components/elements/status-badge/AStatusBadge.vue +22 -19
  23. package/components/elements/status-badge-v2/StatusBadgeV2.vue +1 -3
  24. package/components/elements/table/ATable.vue +1 -1
  25. package/components/elements/table/table.config.ts +2 -2
  26. package/components/elements/tree-select/ATreeSelect.vue +97 -113
  27. package/components/elements/tree-select/TreeService.ts +142 -207
  28. package/components/elements/tree-select/components/ATreeSelectNode.vue +104 -0
  29. package/components/elements/tree-select/types.ts +40 -11
  30. package/components/elements/tree-select/utils.ts +30 -0
  31. package/components/elements/tree-select-modal/ATreeSelectModal.vue +217 -0
  32. package/components/features/color-mode/AColorMode.client.vue +1 -1
  33. package/components/features/dropdown/ADropdownV2.vue +1 -2
  34. package/components/features/lang-switcher/lang-switcher.vue +9 -4
  35. package/components/features/payment/banner/BasicPlusLimitBanner.vue +3 -3
  36. package/components/features/payment/banner/PaymentBanner.vue +3 -3
  37. package/components/features/payment/banner/UpSellBanner.vue +3 -3
  38. package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +3 -3
  39. package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +1 -1
  40. package/components/features/payment/process/PaymentMethodSidePanel.vue +3 -3
  41. package/components/features/payment/process/PaymentTopUpSidePanel.vue +2 -2
  42. package/components/features/pk-mobile-services/APkMobileServices.vue +1 -1
  43. package/components/features//321/201hange-version/AChangeVersion.vue +2 -2
  44. package/components/forms/checkbox/ACheckbox.vue +1 -1
  45. package/components/forms/checkbox/ACheckboxV2.vue +5 -5
  46. package/components/forms/input/AInputV2.vue +4 -4
  47. package/components/forms/input/date/AInputDate.vue +2 -2
  48. package/components/forms/input/password/AInputPassword.vue +8 -2
  49. package/components/forms/input/standard/AInputStandard.vue +3 -3
  50. package/components/forms/input/textarea/ATextarea.vue +2 -2
  51. package/components/forms/toggle/AToggle.vue +2 -3
  52. package/components/forms/toggle/AToggleV2.vue +3 -3
  53. package/components/modals/AnotherDeviceModal.vue +1 -1
  54. package/components/modals/ContactsMobileModel.vue +1 -1
  55. package/components/modals/ContentNavigationModal.vue +3 -3
  56. package/components/modals/Insufficient-funds-modal.vue +1 -1
  57. package/components/modals/PaymentMethodModal.vue +1 -1
  58. package/components/modals/id/IdBanner.vue +3 -3
  59. package/components/modals/id/IdRecoveryModal.vue +1 -1
  60. package/components/navigation/breadcrumbs/ABreadcrumbs.vue +106 -45
  61. package/components/navigation/breadcrumbs/types.ts +3 -0
  62. package/components/navigation/footer/AFooter.vue +39 -40
  63. package/components/navigation/footer/ui/a-footer-accordion.vue +9 -9
  64. package/components/navigation/header/AHeader.vue +2 -4
  65. package/components/navigation/header/AlmatyContacts.vue +78 -78
  66. package/components/navigation/header/CardGallery.vue +4 -4
  67. package/components/navigation/header/HeaderUsage.vue +29 -15
  68. package/components/navigation/header/ListItem.vue +2 -2
  69. package/components/navigation/header/NavCard.vue +1 -1
  70. package/components/navigation/header/NavList.vue +2 -2
  71. package/components/navigation/header/NotificationsMenu.vue +2 -3
  72. package/components/navigation/header/ProductMenu.vue +1 -1
  73. package/components/navigation/header/ProfileMenu.vue +26 -35
  74. package/components/navigation/header/SystemNotification.vue +1 -3
  75. package/components/navigation/pill-tabs/APillTabs.vue +2 -2
  76. package/components/navigation/pill-tabs/APillTabsV2.vue +5 -5
  77. package/components/overlays/drawer/ADrawerV2.vue +335 -0
  78. package/components/overlays/modal/AModal.vue +4 -4
  79. package/components/overlays/modal/AModalV2.vue +10 -4
  80. package/components/overlays/side-panel/ASidePanel.vue +6 -6
  81. package/components/overlays/sideover/ASlideover.vue +6 -6
  82. package/components/overlays/tooltip/ATooltip.vue +1 -1
  83. package/components/overlays/tooltip/ATooltipV2.vue +2 -12
  84. package/components/transitions/ATransitionHeight.vue +1 -1
  85. package/composables/useHeaderNavigationLinks.ts +100 -94
  86. package/icons/archive.vue +16 -0
  87. package/icons/arrow/arrows-in.vue +11 -0
  88. package/icons/arrow/arrows-out.vue +11 -0
  89. package/icons/arrow/arrows-resize-in.vue +38 -0
  90. package/icons/arrow/arrows-resize-out.vue +17 -0
  91. package/icons/bot.vue +52 -0
  92. package/icons/clock-time.vue +24 -0
  93. package/icons/compare-outlined.vue +8 -0
  94. package/icons/dots-horizontal.vue +31 -0
  95. package/icons/download-line.vue +13 -0
  96. package/icons/egov.vue +41 -0
  97. package/icons/elicense.vue +38 -0
  98. package/icons/favourite-bookmark-outline.vue +17 -0
  99. package/icons/file-pen.vue +31 -0
  100. package/icons/file-stack.vue +31 -0
  101. package/icons/file-types/excel.vue +131 -0
  102. package/icons/file-types/html.vue +13 -0
  103. package/icons/file-types/jpeg.vue +16 -0
  104. package/icons/file-types/pdf.vue +11 -0
  105. package/icons/file-types/png.vue +11 -0
  106. package/icons/file-types/rar.vue +11 -0
  107. package/icons/file-types/word.vue +50 -0
  108. package/icons/file-types/zip.vue +11 -0
  109. package/icons/git-compare.vue +38 -0
  110. package/icons/graduation-cap.vue +31 -0
  111. package/icons/graph-loading.vue +34 -0
  112. package/icons/help-question-circle.vue +26 -0
  113. package/icons/in-row-type.vue +46 -0
  114. package/icons/in-table-type.vue +29 -0
  115. package/icons/message-dotted.vue +30 -0
  116. package/icons/notification-error.vue +17 -0
  117. package/icons/person.vue +22 -0
  118. package/icons/settings.vue +26 -0
  119. package/icons/shield-alert.vue +31 -0
  120. package/icons/socials/facebook-color.vue +16 -0
  121. package/icons/socials/globe-color.vue +16 -0
  122. package/icons/socials/instagram-color.vue +46 -0
  123. package/icons/socials/linkedin-color.vue +14 -0
  124. package/icons/socials/telegram-color.vue +16 -0
  125. package/icons/socials/tiktok-color.vue +14 -0
  126. package/icons/socials/vk-color.vue +16 -0
  127. package/icons/socials/youtube-color.vue +16 -0
  128. package/icons/view-eye-open.vue +13 -0
  129. package/package.json +1 -1
  130. package/plugins/toast.client.ts +38 -55
  131. package/tailwind.config.ts +147 -116
  132. package/utils/twMerge.ts +34 -0
  133. package/components/elements/tree-select/components/tree-select-nodes.vue +0 -91
@@ -1,30 +1,7 @@
1
- <template>
2
- <div>
3
- <ul>
4
- <template
5
- v-for="node in nestedTree"
6
- :key="node.id"
7
- >
8
- <TreeSelectNodes
9
- v-if="!node?.hidden"
10
- :node="node"
11
- :toggle-expand="toggleExpand"
12
- :toggle-checkbox="toggleCheckbox"
13
- :type="type"
14
- :disabled="isDisabled(node.id)"
15
- />
16
- </template>
17
- </ul>
18
- </div>
19
- </template>
20
-
21
1
  <script lang="ts" setup>
22
- import { useTreeService } from '#adata-ui/components/elements/tree-select/TreeService.ts'
23
- import TreeSelectNodes from './components/tree-select-nodes.vue'
24
- import type { TreeData } from '#adata-ui/components/elements/tree-select/types'
25
- import type { TreeNestedNode } from '#adata-ui/components/elements/tree-select/types'
26
- import highlight from '#adata-ui/composables/highlight'
27
2
  import { useDebounceFn } from '@vueuse/core'
3
+ import { useTreeService } from '#adata-ui/components/elements/tree-select/TreeService'
4
+ import type { TreeData, TreeViewNode } from '#adata-ui/components/elements/tree-select/types'
28
5
 
29
6
  interface Props {
30
7
  options: TreeData[]
@@ -34,140 +11,147 @@ interface Props {
34
11
  disabled?: boolean
35
12
  onlyOne?: boolean
36
13
  searchText?: string
37
- // eslint-disable-next-line vue/require-default-prop
38
- asyncSearch?: (keyword: string) => TreeData[]
14
+ asyncSearch?: (keyword: string) => TreeData[] | Promise<TreeData[]>
39
15
  withoutId?: boolean
16
+ idKey?: string
17
+ nameKey?: string
18
+ childrenKey?: string
19
+ hasChildrenKey?: string
20
+ lazy?: boolean
21
+ loadChildren?: (node: TreeViewNode) => TreeData[] | Promise<TreeData[]>
40
22
  }
41
23
 
42
24
  interface Emits {
43
25
  (e: 'update:modelValue', item: (string | number)[]): void
44
26
  }
45
27
 
46
- const emits = defineEmits<Emits>()
47
28
  const props = withDefaults(defineProps<Props>(), {
48
29
  type: 'checkbox',
49
30
  searchText: '',
50
31
  withoutId: false,
51
32
  disabled: false,
52
33
  onlyOne: false,
34
+ idKey: 'id',
35
+ nameKey: 'name',
36
+ childrenKey: 'children',
37
+ lazy: false,
53
38
  })
39
+
40
+ const emits = defineEmits<Emits>()
41
+
54
42
  const {
55
- nestedTree,
56
- tree,
43
+ nodes,
44
+ build,
57
45
  setState,
58
- setExpandableClosed,
59
- initTree,
46
+ applyModel,
60
47
  collectSelectedIds,
61
- updateNestedTree,
62
- searchedData,
63
- toggleHiddenNodes,
64
48
  reset,
65
- toggleExpandableNodes,
66
- } = useTreeService( props.withoutId )
67
- const selected = computed({
68
- get() {
69
- return props.modelValue
70
- },
71
- set(val) {
72
- emits('update:modelValue', val)
73
- }
49
+ toggleExpand,
50
+ applySearch,
51
+ clearSearch,
52
+ } = useTreeService({
53
+ withoutId: props.withoutId,
54
+ idKey: props.idKey,
55
+ nameKey: props.nameKey,
56
+ childrenKey: props.childrenKey,
57
+ hasChildrenKey: props.hasChildrenKey,
58
+ lazy: props.lazy,
59
+ loadChildren: props.loadChildren,
74
60
  })
75
61
 
76
- initTree(props.options)
77
- const toggleExpand = (node) => {
78
- if (node.expandable) {
79
- setExpandableClosed(node.id, !node.expandableClosed)
80
- }
62
+ function emitSelection() {
63
+ emits('update:modelValue', collectSelectedIds())
81
64
  }
82
65
 
83
- const toggleCheckbox = (id, checked) => {
66
+ function toggleCheckbox(id: string | number, checked: boolean) {
84
67
  if (props.onlyOne && !checked) {
85
68
  reset()
86
- } else {
69
+ }
70
+ else {
87
71
  setState(id, checked ? 'checked' : 'unchecked')
88
72
  }
73
+ emitSelection()
89
74
  }
90
75
 
91
- function openFounded(nodes: TreeNestedNode[]) {
92
- nodes.forEach((node) => {
93
- node.expandableClosed = false
94
- node.text = highlight(node.text, props.searchText)
95
- if (node.child.length) {
96
- openFounded(node.child)
97
- }
98
- })
76
+ const selected = computed(() => props.modelValue ?? [])
77
+ function isDisabled(id: string | number) {
78
+ return props.onlyOne ? selected.value.length > 0 && !selected.value.includes(id) : false
99
79
  }
100
80
 
101
- function filterOptions(options: any[], search: string) {
81
+ function filterOptions(options: TreeData[], search: string): TreeData[] {
82
+ const needle = search.toLowerCase()
102
83
  return options
103
84
  .map((option) => {
104
- const matches = option.name.toLowerCase().includes(search.toLowerCase())
105
- const matchesWithId = option.id?.toString().toLowerCase().includes(search.toLowerCase())
106
- let filteredChildren = []
107
-
108
- if (option.children) {
109
- filteredChildren = filterOptions(option.children, search)
110
- }
111
-
112
- if (matches || matchesWithId || filteredChildren.length > 0) {
113
- return {
114
- ...option,
115
- children: filteredChildren
116
- }
85
+ const name = String(option[props.nameKey] ?? '').toLowerCase()
86
+ const id = option[props.idKey] as string | number | undefined
87
+ const matches = name.includes(needle)
88
+ const matchesId = id?.toString().toLowerCase().includes(needle)
89
+ const children = (option[props.childrenKey] as TreeData[] | undefined) ?? []
90
+ const filteredChildren = children.length ? filterOptions(children, search) : []
91
+
92
+ if (matches || matchesId || filteredChildren.length > 0) {
93
+ return { ...option, [props.childrenKey]: filteredChildren }
117
94
  }
118
-
119
95
  return null
120
96
  })
121
- .filter(Boolean)
97
+ .filter(Boolean) as TreeData[]
122
98
  }
123
99
 
124
100
  const debouncedSearch = useDebounceFn(async () => {
125
101
  if (props.searchText !== '') {
126
- if (props?.asyncSearch) {
127
- const searchData = await props.asyncSearch(props.searchText)
128
- searchedData(searchData, props.searchText)
129
- } else {
130
- const searchData = filterOptions(props.options, props.searchText)
131
- searchedData(searchData, props.searchText)
132
- }
133
- } else {
134
- toggleHiddenNodes(false)
135
- toggleExpandableNodes(true)
136
- updateNestedTree()
102
+ const data = props.asyncSearch
103
+ ? await props.asyncSearch(props.searchText)
104
+ : filterOptions(props.options, props.searchText)
105
+ applySearch(data, props.searchText)
106
+ }
107
+ else {
108
+ clearSearch()
137
109
  }
138
110
  }, 300)
139
111
 
140
- watch(
141
- () => props.searchText,
142
- (newVal) => debouncedSearch()
143
- )
112
+ watch(() => props.searchText, () => debouncedSearch())
144
113
 
114
+ function initFromOptions() {
115
+ build(props.options)
116
+ applyModel(props.modelValue)
117
+ emitSelection()
118
+ }
145
119
 
120
+ build(props.options)
146
121
  onMounted(() => {
147
- if (props.modelValue && props.modelValue.length) {
148
- props.modelValue.forEach((id) => {
149
- setState(id, 'checked')
150
- })
151
- }
152
-
153
- if (props.isSearch) {
154
- // openFounded(nestedTree.value)
155
- }
122
+ applyModel(props.modelValue)
123
+ emitSelection()
156
124
  })
125
+ watch(() => props.options, initFromOptions)
157
126
 
158
- const isDisabled = (id: string | number) => {
159
- return props.onlyOne ? selected.value.length > 0 && !selected.value.includes(id) : false
127
+ function resetSelection() {
128
+ reset()
129
+ emitSelection()
160
130
  }
161
- watch(
162
- tree,
163
- () => {
164
- if (tree.value) {
165
- selected.value = collectSelectedIds(tree.value)
166
- }
167
- },
168
- { deep: true }
169
- )
170
- defineExpose({
171
- reset
172
- })
131
+
132
+ defineExpose({ reset: resetSelection })
173
133
  </script>
134
+
135
+ <template>
136
+ <div>
137
+ <ul>
138
+ <template
139
+ v-for="node in nodes"
140
+ :key="node.id"
141
+ >
142
+ <a-tree-select-node
143
+ v-if="!node.hidden"
144
+ :node="node"
145
+ :toggle-expand="toggleExpand"
146
+ :toggle-checkbox="toggleCheckbox"
147
+ :type="type"
148
+ :disabled="isDisabled(node.id)"
149
+ >
150
+ <template v-if="$slots.label" #label="slotProps">
151
+ <slot name="label" v-bind="slotProps" />
152
+ </template>
153
+ </a-tree-select-node>
154
+ </template>
155
+ </ul>
156
+ </div>
157
+ </template>
@@ -1,249 +1,184 @@
1
1
  import type {
2
2
  TreeData,
3
- TreeNestedNode,
4
- TreeNode,
5
- TreeNodeStateEditable
3
+ TreeNodeState,
4
+ TreeServiceOptions,
5
+ TreeViewNode,
6
6
  } from '#adata-ui/components/elements/tree-select/types'
7
7
  import highlight from '#adata-ui/composables/highlight'
8
8
 
9
- export const useTreeService = ( withoutId?: boolean ) => {
10
- const tree = ref<Map<string | number, TreeNode> | null>(null)
11
- const nestedTree = ref<TreeNestedNode[]>([])
12
- const createdTreeBool = ref(false)
13
-
14
- const initTree = (data: TreeData[]): void => {
15
- if (!createdTreeBool.value) {
16
- tree.value = generateTree(data, 0)
17
- createdTreeBool.value = true
18
- updateNestedTree()
19
- }
20
- }
21
-
22
- const generateTree = (
23
- data: TreeData[],
9
+ export const useTreeService = (options: TreeServiceOptions = {}) => {
10
+ const {
11
+ withoutId = false,
12
+ idKey = 'id',
13
+ nameKey = 'name',
14
+ childrenKey = 'children',
15
+ hasChildrenKey,
16
+ lazy = false,
17
+ loadChildren,
18
+ } = options
19
+
20
+ const nodes = ref<TreeViewNode[]>([])
21
+ const nodeMap = new Map<string | number, TreeViewNode>()
22
+
23
+ const readId = (item: TreeData) => item[idKey] as string | number
24
+ const readName = (item: TreeData) => String(item[nameKey] ?? '')
25
+ const readChildren = (item: TreeData) => (item[childrenKey] as TreeData[] | undefined) ?? []
26
+
27
+ const createNode = (
28
+ item: TreeData,
24
29
  level: number,
25
- parent: string | number | null = null
26
- ): Map<string | number, TreeNode> => {
27
- const treeMap = new Map<string | number, TreeNode>()
28
- data.forEach((item) => {
29
- const id = item.id
30
- const child: (string | number)[] = item.children ? item.children.map((child) => child.id) : []
31
- treeMap.set(id, {
32
- id,
33
- name: item.name,
34
- level,
35
- expandable: child.length > 0,
36
- expandableClosed: true,
37
- child,
38
- parent,
39
- state: 'unchecked',
40
- hidden: false
41
- })
42
- if (item.children && item.children.length > 0) {
43
- const childTreeMap = generateTree(item.children, level + 1, id)
44
- childTreeMap.forEach((value, key) => treeMap.set(key, value))
45
- }
30
+ parent: TreeViewNode | null,
31
+ ): TreeViewNode => {
32
+ const id = readId(item)
33
+ const name = readName(item)
34
+ const rawChildren = readChildren(item)
35
+ const baseText = withoutId ? name : `${id} - ${name}`
36
+ const hasLazyChildren = lazy && !!hasChildrenKey && !!item[hasChildrenKey]
37
+
38
+ const node: TreeViewNode = reactive({
39
+ id,
40
+ name,
41
+ baseText,
42
+ text: baseText,
43
+ level,
44
+ state: 'unchecked' as TreeNodeState,
45
+ expandable: rawChildren.length > 0 || hasLazyChildren,
46
+ expanded: false,
47
+ hidden: false,
48
+ loaded: !(hasLazyChildren && rawChildren.length === 0),
49
+ loading: false,
50
+ parent,
51
+ children: [],
52
+ raw: markRaw(item),
53
+ }) as TreeViewNode
54
+
55
+ nodeMap.set(id, node)
56
+ node.children = rawChildren.map(child => createNode(child, level + 1, node))
57
+ return node
58
+ }
59
+
60
+ const build = (data: TreeData[]): void => {
61
+ nodeMap.clear()
62
+ nodes.value = (data ?? []).map(item => createNode(item, 0, null))
63
+ }
64
+
65
+ const getNode = (id: string | number): TreeViewNode | undefined => nodeMap.get(id)
66
+
67
+ const setSubtreeState = (node: TreeViewNode, state: TreeNodeState): void => {
68
+ node.children.forEach((child) => {
69
+ child.state = state
70
+ setSubtreeState(child, state)
46
71
  })
47
- return treeMap
48
72
  }
49
73
 
50
- const updateNestedTree = (searchText: string = '') => {
51
- if (!tree.value) return
52
- const nodesFromLevel0: TreeNode[] = getNodesByLevel(0)
53
- nestedTree.value = nodesFromLevel0.map((flatNode) => createNestedNode(flatNode))
54
- highlightTree(nestedTree.value, searchText)
74
+ const recomputeAncestors = (node: TreeViewNode): void => {
75
+ let parent = node.parent
76
+ while (parent) {
77
+ const kids = parent.children
78
+ const checked = kids.filter(k => k.state === 'checked').length
79
+ const indeterminate = kids.filter(k => k.state === 'indeterminate').length
55
80
 
56
- }
57
- function highlightTree(nodes, searchText) {
58
- nodes.forEach((node) => {
59
- node.text = highlight(node.text, searchText);
60
- if (node.child.length) {
61
- highlightTree(node.child, searchText);
62
- }
63
- });
64
- }
65
- const createNestedNode = (node: TreeNode): TreeNestedNode => {
66
- return {
67
- id: node.id,
68
- name: node.name,
69
- text: withoutId ? `${node.name}` : `${node.id} - ${node.name}`,
70
- state: node.state,
71
- level: node.level,
72
- expandable: node.expandable,
73
- expandableClosed: node.expandableClosed,
74
- hidden: node?.hidden ?? false,
75
- child: node.child
76
- .map((id) => {
77
- const childNode = getNodeById(id)
78
- return childNode ? createNestedNode(childNode) : null
79
- })
80
- .filter((n) => n) as TreeNestedNode[]
81
- }
82
- }
83
-
84
- const getNodeById = (idNode: string | number): TreeNode | null => {
85
- if (!tree.value) return null
86
- return tree.value.get(idNode) || null
87
- }
81
+ if (kids.length > 0 && checked === kids.length) parent.state = 'checked'
82
+ else if (checked > 0 || indeterminate > 0) parent.state = 'indeterminate'
83
+ else parent.state = 'unchecked'
88
84
 
89
- const getChildNodesFromNode = (idNode: string | number): TreeNode[] => {
90
- if (!tree.value) return []
91
- const node = getNodeById(idNode)
92
- if (!node) return []
93
- return node.child.map((id) => getNodeById(id)).filter((n) => n) as TreeNode[]
94
- }
95
-
96
- const getParentNode = (idNode: string | number): TreeNode | null => {
97
- if (!tree.value) return null
98
- const node = getNodeById(idNode)
99
- if (!node || !node.parent) return null
100
- return getNodeById(node.parent)
101
- }
102
-
103
- const getNodesByLevel = (level: number): TreeNode[] => {
104
- if (!tree.value) return []
105
- const nodes: TreeNode[] = []
106
- tree.value.forEach((node) => {
107
- if (node.level === level) {
108
- nodes.push(node)
109
- }
110
- })
111
- return nodes
85
+ parent = parent.parent
86
+ }
112
87
  }
113
88
 
114
- const setState = (idNode: string | number, state: TreeNodeStateEditable): void => {
115
- if (!tree.value) return
116
- const node = getNodeById(idNode)
89
+ const setState = (id: string | number, state: TreeNodeState): void => {
90
+ const node = getNode(id)
117
91
  if (!node) return
118
92
  node.state = state
119
- tree.value.set(idNode, node)
120
- setStateChildNodes(node, state)
121
- setStateParentNodes(node)
122
- updateNestedTree()
123
- }
124
-
125
- const setStateChildNodes = (node: TreeNode, state: TreeNodeStateEditable): void => {
126
- if (!tree.value) return
127
- node.child.forEach((id) => {
128
- const childNode = getNodeById(id)
129
- if (childNode) {
130
- childNode.state = state
131
- tree.value.set(id, childNode)
132
- setStateChildNodes(childNode, state)
133
- }
134
- })
93
+ setSubtreeState(node, state)
94
+ recomputeAncestors(node)
135
95
  }
136
96
 
137
- const setStateParentNodes = (node: TreeNode): void => {
138
- if (!tree.value) return
139
- const parentNode = getParentNode(node.id)
140
- if (!parentNode) return
141
- const childNodes = getChildNodesFromNode(parentNode.id)
142
- const checkedCount = childNodes.filter((n) => n.state === 'checked').length
143
- const indeterminateCount = childNodes.filter((n) => n.state === 'indeterminate').length
144
-
145
- if (checkedCount === childNodes.length) {
146
- parentNode.state = 'checked'
147
- } else if (checkedCount > 0 || indeterminateCount > 0) {
148
- parentNode.state = 'indeterminate'
149
- } else {
150
- parentNode.state = 'unchecked'
151
- }
152
- tree.value.set(parentNode.id, parentNode)
153
- setStateParentNodes(parentNode)
97
+ const applyModel = (ids: (string | number)[] = []): void => {
98
+ ids.forEach(id => setState(id, 'checked'))
154
99
  }
155
100
 
156
- const setExpandableClosed = (idNode: string | number, expandableClosed: boolean): void => {
157
- if (!tree.value) return
158
- const node = getNodeById(idNode)
159
- if (!node) return
160
-
161
- if (!expandableClosed && node.expandable && node.child.length === 0) {
162
- // Здесь можно добавить логику для ленивой загрузки дочерних узлов, если они не были загружены ранее.
163
- }
164
-
165
- node.expandableClosed = expandableClosed
166
- tree.value.set(node.id, node)
167
- updateNestedTree()
101
+ const collectSelectedIds = (): (string | number)[] => {
102
+ const ids: (string | number)[] = []
103
+ nodeMap.forEach((node) => {
104
+ if (node.state === 'checked' && node.parent?.state !== 'checked') {
105
+ ids.push(node.id)
106
+ }
107
+ })
108
+ return ids
168
109
  }
169
110
 
170
111
  const reset = (): void => {
171
- if (!tree.value) return
172
- tree.value.forEach((node) => {
112
+ nodeMap.forEach((node) => {
173
113
  node.state = 'unchecked'
174
114
  })
175
- updateNestedTree()
176
115
  }
177
116
 
178
- const collectSelectedIds = (tree: Map<string | number, TreeNode>): (string | number)[] => {
179
- const selectedIds: Set<string | number> = new Set()
180
-
181
- // Функция для проверки, выбраны ли все дочерние узлы
182
- const areAllChildrenChecked = (node: TreeNode): boolean => {
183
- const childNodes = getChildNodesFromNode(node.id)
184
- return childNodes.length > 0 && childNodes.every((child) => child.state === 'checked')
185
- }
186
-
187
- tree.forEach((node) => {
188
- if (node.state === 'checked') {
189
- if (node.parent && getNodeById(node.parent)?.state === 'checked') {
190
- // Пропустить дочерний узел, если родительский узел уже выбран
191
- return
192
- } else if (areAllChildrenChecked(node)) {
193
- // Если все дочерние узлы выбраны, то включить только родительский узел
194
- selectedIds.add(node.id)
195
- } else {
196
- selectedIds.add(node.id)
117
+ const toggleExpand = async (node: TreeViewNode): Promise<void> => {
118
+ if (!node.expandable) return
119
+ const willExpand = !node.expanded
120
+ node.expanded = willExpand
121
+
122
+ if (willExpand && lazy && !node.loaded && !node.loading && loadChildren) {
123
+ node.loading = true
124
+ try {
125
+ const data = await loadChildren(node)
126
+ const kids = (data ?? []).map(child => createNode(child, node.level + 1, node))
127
+ node.children = kids
128
+ node.loaded = true
129
+ if (node.state === 'checked') {
130
+ kids.forEach((k) => {
131
+ k.state = 'checked'
132
+ setSubtreeState(k, 'checked')
133
+ })
197
134
  }
198
135
  }
199
- })
200
-
201
- return Array.from(selectedIds)
136
+ finally {
137
+ node.loading = false
138
+ }
139
+ }
202
140
  }
203
141
 
204
- function hideSearchedData(data: TreeData[], searchText: string) {
205
- data.forEach((node) => {
206
- const existingNode = tree.value.get(node.id)
207
- if (existingNode) {
208
- tree.value.set(node.id, { ...existingNode, hidden: false, expandableClosed: false })
209
- }
210
- if (node.children?.length) {
211
- hideSearchedData(node.children)
212
- }
142
+ const applySearch = (matched: TreeData[], searchText: string): void => {
143
+ nodeMap.forEach((node) => {
144
+ node.hidden = true
145
+ node.text = node.baseText
213
146
  })
214
- }
215
147
 
216
- function searchedData(data: TreeData[], searchText: string) {
217
- if (!tree.value) return
218
- toggleHiddenNodes(true)
219
- hideSearchedData(data, searchText)
220
- updateNestedTree(searchText)
221
- }
148
+ const reveal = (items: TreeData[]): void => {
149
+ items.forEach((item) => {
150
+ const node = getNode(readId(item))
151
+ if (node) {
152
+ node.hidden = false
153
+ node.expanded = true
154
+ node.text = highlight(node.baseText, searchText)
155
+ }
156
+ const kids = readChildren(item)
157
+ if (kids.length) reveal(kids)
158
+ })
159
+ }
222
160
 
223
- const toggleHiddenNodes = (hidden: boolean) => {
224
- if (!tree.value) return
225
- tree.value.forEach((node, key) => {
226
- tree.value.set(key, { ...node, hidden })
227
- })
161
+ reveal(matched ?? [])
228
162
  }
229
- const toggleExpandableNodes = (expandableClosed: boolean) => {
230
- if (!tree.value) return
231
- tree.value.forEach((node, key) => {
232
- tree.value.set(key, { ...node, expandableClosed })
163
+
164
+ const clearSearch = (): void => {
165
+ nodeMap.forEach((node) => {
166
+ node.hidden = false
167
+ node.expanded = false
168
+ node.text = node.baseText
233
169
  })
234
170
  }
235
171
 
236
172
  return {
237
- tree,
238
- nestedTree,
239
- initTree,
173
+ nodes,
174
+ build,
175
+ getNode,
240
176
  setState,
241
- reset,
242
- setExpandableClosed,
177
+ applyModel,
243
178
  collectSelectedIds,
244
- updateNestedTree,
245
- searchedData,
246
- toggleHiddenNodes,
247
- toggleExpandableNodes,
179
+ reset,
180
+ toggleExpand,
181
+ applySearch,
182
+ clearSearch,
248
183
  }
249
184
  }