@veltra/desktop 1.0.8 → 1.0.10

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 (220) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action.vue.d.ts +3 -3
  3. package/dist/components/auto-complete/auto-complete.vue.d.ts +1 -1
  4. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  5. package/dist/components/batch-edit/batch-edit.vue.d.ts +2 -2
  6. package/dist/components/batch-edit/use-edit.js.map +1 -1
  7. package/dist/components/button/button.js.map +1 -1
  8. package/dist/components/button/button.vue.d.ts +1 -1
  9. package/dist/components/cascade/cascade.js +15 -13
  10. package/dist/components/cascade/cascade.js.map +1 -1
  11. package/dist/components/cascade/cascade.vue.d.ts +7 -7
  12. package/dist/components/cascade/node.js.map +1 -1
  13. package/dist/components/cascade/use-check.js +3 -2
  14. package/dist/components/cascade/use-check.js.map +1 -1
  15. package/dist/components/cascade/use-select.js +3 -3
  16. package/dist/components/cascade/use-select.js.map +1 -1
  17. package/dist/components/checkbox-group/checkbox-group.js.map +1 -1
  18. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  19. package/dist/components/date-panel/date-panel.js.map +1 -1
  20. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  21. package/dist/components/date-picker/date-picker.js +9 -9
  22. package/dist/components/date-picker/date-picker.js.map +1 -1
  23. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  24. package/dist/components/date-range-picker/date-range-picker.js +9 -9
  25. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  26. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  27. package/dist/components/drawer/drawer.js.map +1 -1
  28. package/dist/components/empty/empty.vue.d.ts +1 -1
  29. package/dist/components/expression-editor/expression-editor.js.map +1 -1
  30. package/dist/components/expression-editor/internal/editor-runtime/create-runtime.js.map +1 -1
  31. package/dist/components/expression-editor/internal/features/commands/register-command-packs.js.map +1 -1
  32. package/dist/components/expression-editor/internal/features/insertion/insertion-service.js.map +1 -1
  33. package/dist/components/expression-editor/use-decorators.js.map +1 -1
  34. package/dist/components/expression-editor/use-expression-drag-drop.js.map +1 -1
  35. package/dist/components/file-picker/file-picker.js.map +1 -1
  36. package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
  37. package/dist/components/file-picker/helper.js +19 -7
  38. package/dist/components/file-picker/helper.js.map +1 -1
  39. package/dist/components/file-viewer/file-viewer.js +294 -0
  40. package/dist/components/file-viewer/file-viewer.js.map +1 -0
  41. package/dist/components/file-viewer/file-viewer.vue.d.ts +42 -0
  42. package/dist/components/file-viewer/helper.js +185 -0
  43. package/dist/components/file-viewer/helper.js.map +1 -0
  44. package/dist/components/file-viewer/index.d.ts +1 -0
  45. package/dist/components/file-viewer/previewers/docx-previewer.js +76 -0
  46. package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -0
  47. package/dist/components/file-viewer/previewers/image-previewer.js +64 -0
  48. package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -0
  49. package/dist/components/file-viewer/previewers/pdf-previewer.js +90 -0
  50. package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -0
  51. package/dist/components/file-viewer/previewers/sheet-previewer.js +175 -0
  52. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -0
  53. package/dist/components/file-viewer/previewers/text-previewer.js +74 -0
  54. package/dist/components/file-viewer/previewers/text-previewer.js.map +1 -0
  55. package/dist/components/file-viewer/previewers/video-previewer.js +62 -0
  56. package/dist/components/file-viewer/previewers/video-previewer.js.map +1 -0
  57. package/dist/components/file-viewer/style.d.ts +1 -0
  58. package/dist/components/file-viewer/style.js +4 -0
  59. package/dist/components/file-viewer/style2.css +445 -0
  60. package/dist/components/form/form-model.js +2 -1
  61. package/dist/components/form/form-model.js.map +1 -1
  62. package/dist/components/grid/grid.js.map +1 -1
  63. package/dist/components/grid/use-responsive.js.map +1 -1
  64. package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
  65. package/dist/components/index.d.ts +12 -9
  66. package/dist/components/input/input.vue.d.ts +3 -3
  67. package/dist/components/list/list.js.map +1 -1
  68. package/dist/components/loading/directive.js.map +1 -1
  69. package/dist/components/menu/menu-item.js.map +1 -1
  70. package/dist/components/message/message2.js.map +1 -1
  71. package/dist/components/multi-select/multi-select-option.js +14 -10
  72. package/dist/components/multi-select/multi-select-option.js.map +1 -1
  73. package/dist/components/multi-select/multi-select.js +16 -19
  74. package/dist/components/multi-select/multi-select.js.map +1 -1
  75. package/dist/components/multi-select/multi-select.vue.d.ts +4 -4
  76. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +7 -7
  77. package/dist/components/notification/index.js.map +1 -1
  78. package/dist/components/notification/notification.vue.d.ts +1 -1
  79. package/dist/components/number-input/number-input.js.map +1 -1
  80. package/dist/components/number-input/number-input.vue.d.ts +1 -1
  81. package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
  82. package/dist/components/palette/color-transform.js.map +1 -1
  83. package/dist/components/palette/di.js.map +1 -1
  84. package/dist/components/palette/palette-alpha.js +7 -10
  85. package/dist/components/palette/palette-alpha.js.map +1 -1
  86. package/dist/components/palette/palette-hue.js +5 -7
  87. package/dist/components/palette/palette-hue.js.map +1 -1
  88. package/dist/components/palette/palette-sv.js +8 -10
  89. package/dist/components/palette/palette-sv.js.map +1 -1
  90. package/dist/components/palette/palette.js +22 -24
  91. package/dist/components/palette/palette.js.map +1 -1
  92. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  93. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +2 -2
  94. package/dist/components/progress/progress.js +1 -1
  95. package/dist/components/progress/progress.js.map +1 -1
  96. package/dist/components/progress-nodes/progress-nodes.js.map +1 -1
  97. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  98. package/dist/components/rich-text-editor/rich-text-editor.js.map +1 -1
  99. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
  100. package/dist/components/rich-text-editor/toolbar.js.map +1 -1
  101. package/dist/components/scroll/scroll.js.map +1 -1
  102. package/dist/components/select/select.js +14 -12
  103. package/dist/components/select/select.js.map +1 -1
  104. package/dist/components/select/select.vue.d.ts +3 -3
  105. package/dist/components/slider/slider-thumb.js +10 -13
  106. package/dist/components/slider/slider-thumb.js.map +1 -1
  107. package/dist/components/slider/slider.js +12 -15
  108. package/dist/components/slider/slider.js.map +1 -1
  109. package/dist/components/table/di.js.map +1 -1
  110. package/dist/components/table/node/row.d.ts +10 -1
  111. package/dist/components/table/node/row.js +9 -0
  112. package/dist/components/table/node/row.js.map +1 -1
  113. package/dist/components/table/table-body.js +25 -26
  114. package/dist/components/table/table-body.js.map +1 -1
  115. package/dist/components/table/table-cell.js +16 -9
  116. package/dist/components/table/table-cell.js.map +1 -1
  117. package/dist/components/table/table-row.js +77 -19
  118. package/dist/components/table/table-row.js.map +1 -1
  119. package/dist/components/table/table.js +75 -34
  120. package/dist/components/table/table.js.map +1 -1
  121. package/dist/components/table/table.vue.d.ts +2 -2
  122. package/dist/components/table/use-columns.js +4 -0
  123. package/dist/components/table/use-columns.js.map +1 -1
  124. package/dist/components/table/use-fixed-columns.js +27 -8
  125. package/dist/components/table/use-fixed-columns.js.map +1 -1
  126. package/dist/components/table/use-rows.js +2 -2
  127. package/dist/components/table/use-rows.js.map +1 -1
  128. package/dist/components/table/use-table.js +50 -3
  129. package/dist/components/table/use-table.js.map +1 -1
  130. package/dist/components/tabs/index.d.ts +3 -1
  131. package/dist/components/tabs/style2.css +170 -107
  132. package/dist/components/tabs/tabs-horizontal.js +133 -0
  133. package/dist/components/tabs/tabs-horizontal.js.map +1 -0
  134. package/dist/components/tabs/tabs-horizontal.vue.d.ts +40 -0
  135. package/dist/components/tabs/tabs-vertical.js +89 -0
  136. package/dist/components/tabs/tabs-vertical.js.map +1 -0
  137. package/dist/components/tabs/tabs-vertical.vue.d.ts +38 -0
  138. package/dist/components/tabs/tabs.js +75 -105
  139. package/dist/components/tabs/tabs.js.map +1 -1
  140. package/dist/components/tabs/tabs.vue.d.ts +9 -23
  141. package/dist/components/tabs/use-tabs-bar.js +105 -0
  142. package/dist/components/tabs/use-tabs-bar.js.map +1 -0
  143. package/dist/components/text/text.js.map +1 -1
  144. package/dist/components/textarea/textarea.js.map +1 -1
  145. package/dist/components/textarea/textarea.vue.d.ts +4 -4
  146. package/dist/components/theme/theme.js.map +1 -1
  147. package/dist/components/tip/tip.vue.d.ts +1 -1
  148. package/dist/components/tree/style2.css +45 -23
  149. package/dist/components/tree/tree-node.js +50 -35
  150. package/dist/components/tree/tree-node.js.map +1 -1
  151. package/dist/components/tree/tree-node2.js.map +1 -1
  152. package/dist/components/tree/tree.js +27 -20
  153. package/dist/components/tree/tree.js.map +1 -1
  154. package/dist/components/tree/tree.vue.d.ts +5 -5
  155. package/dist/components/tree/use-check.js +52 -14
  156. package/dist/components/tree/use-check.js.map +1 -1
  157. package/dist/components/tree/use-tree-nodes.js +51 -24
  158. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  159. package/dist/components/tree-select/tree-select.vue.d.ts +7 -7
  160. package/dist/index.d.ts +57 -53
  161. package/dist/index.js +51 -48
  162. package/dist/types/file-viewer.d.ts +71 -0
  163. package/dist/types/index.d.ts +3 -2
  164. package/dist/types/tabs.d.ts +106 -14
  165. package/dist/types/tree.d.ts +6 -1
  166. package/package.json +32 -31
  167. package/src/components/cascade/cascade.vue +18 -18
  168. package/src/components/cascade/node.ts +4 -1
  169. package/src/components/cascade/use-check.ts +5 -4
  170. package/src/components/cascade/use-select.ts +4 -5
  171. package/src/components/date-picker/date-picker.vue +10 -9
  172. package/src/components/date-range-picker/date-range-picker.vue +12 -12
  173. package/src/components/file-picker/helper.ts +21 -10
  174. package/src/components/file-viewer/file-viewer.vue +311 -0
  175. package/src/components/file-viewer/helper.ts +193 -0
  176. package/src/components/file-viewer/index.ts +1 -0
  177. package/src/components/file-viewer/previewers/docx-previewer.vue +78 -0
  178. package/src/components/file-viewer/previewers/image-previewer.vue +64 -0
  179. package/src/components/file-viewer/previewers/pdf-previewer.vue +90 -0
  180. package/src/components/file-viewer/previewers/sheet-previewer.vue +210 -0
  181. package/src/components/file-viewer/previewers/text-previewer.vue +76 -0
  182. package/src/components/file-viewer/previewers/video-previewer.vue +72 -0
  183. package/src/components/file-viewer/style.scss +504 -0
  184. package/src/components/file-viewer/style.ts +4 -0
  185. package/src/components/index.ts +2 -0
  186. package/src/components/multi-select/multi-select-option.vue +16 -3
  187. package/src/components/multi-select/multi-select.vue +17 -9
  188. package/src/components/palette/di.ts +5 -3
  189. package/src/components/palette/palette-alpha.vue +7 -10
  190. package/src/components/palette/palette-hue.vue +5 -7
  191. package/src/components/palette/palette-sv.vue +12 -14
  192. package/src/components/palette/palette.vue +19 -21
  193. package/src/components/progress/progress.vue +1 -1
  194. package/src/components/select/select.vue +21 -19
  195. package/src/components/slider/slider-thumb.vue +11 -14
  196. package/src/components/slider/slider.vue +15 -15
  197. package/src/components/table/di.ts +58 -43
  198. package/src/components/table/node/row.ts +10 -1
  199. package/src/components/table/table-body.vue +28 -33
  200. package/src/components/table/table-cell.vue +17 -9
  201. package/src/components/table/table-row.tsx +83 -17
  202. package/src/components/table/table.vue +72 -45
  203. package/src/components/table/use-fixed-columns.ts +46 -12
  204. package/src/components/table/use-rows.ts +14 -3
  205. package/src/components/table/use-table.ts +27 -3
  206. package/src/components/tabs/index.ts +2 -0
  207. package/src/components/tabs/style.scss +219 -133
  208. package/src/components/tabs/tabs-horizontal.vue +128 -0
  209. package/src/components/tabs/tabs-vertical.vue +77 -0
  210. package/src/components/tabs/tabs.vue +56 -127
  211. package/src/components/tabs/use-tabs-bar.ts +119 -0
  212. package/src/components/tree/style.scss +74 -29
  213. package/src/components/tree/tree-node.vue +10 -4
  214. package/src/components/tree/tree.vue +39 -12
  215. package/src/components/tree/use-check.ts +79 -23
  216. package/src/components/tree/use-tree-nodes.ts +85 -34
  217. package/src/types/file-viewer.ts +69 -0
  218. package/src/types/index.ts +1 -0
  219. package/src/types/tabs.ts +113 -19
  220. package/src/types/tree.ts +6 -1
@@ -1 +1 @@
1
- {"version":3,"file":"use-rows.js","names":["useModel"],"sources":["../../../src/components/table/use-rows.ts"],"sourcesContent":["import { Forest, o } from '@cat-kit/core'\nimport { useModel } from '@veltra/compositions'\nimport { shallowRef, watch, type ShallowRef } from 'vue'\n\nimport type { TableColumn, TableEmits, TableProps, TableRow } from '../../types'\nimport { TableRowNode } from './node/row'\n\ninterface Options {\n props: TableProps\n emit: TableEmits\n}\n\ninterface UseRowsReturned {\n /** 所有的行森林(树形数据) */\n rowForest: ShallowRef<Forest<Record<string, unknown>, any> | undefined>\n /** 当前可见的展平行列表 */\n rows: ShallowRef<TableRowNode[]>\n /** 切换树形行节点的展开/折叠 */\n toggleTreeRowExpand: (node: TableRow) => void\n /** 是否全部展开 */\n allExpanded: ShallowRef<boolean>\n /** 切换全部展开/折叠 */\n toggleAllTreeRowExpand: () => void\n /** 行点击处理 */\n handleRowClick: (row: TableRow, e: MouseEvent) => void\n /** 单元格点击处理 */\n handleCellClick: (row: TableRow, column: TableColumn, e: MouseEvent) => void\n /** 根据原始数据对象获取对应的 TableRow */\n getRowByData: (data: Record<string, any>) => TableRow | undefined\n}\n\nexport function useRows(options: Options): UseRowsReturned {\n const { props, emit } = options\n\n // --- 状态定义 ---\n\n /** 所有可见的行 */\n const rows = shallowRef<TableRowNode[]>([])\n /** 数据行, 不包括展开和折叠的行 */\n const dataRows = shallowRef<TableRowNode[]>([])\n /** 行森林(数据结构树) */\n const rowForest = shallowRef<Forest<Record<string, unknown>, any>>()\n /** 树形节点是否全部展开 */\n const allExpanded = shallowRef(props.defaultExpandAll ?? false)\n\n /** 当前高亮的行 */\n const currentRow = useModel({\n props,\n emit,\n propName: 'current',\n shallow: true,\n local: () => !!props.highlightCurrent\n })\n\n // --- 行字典维护 (性能优化) ---\n\n /**\n * 用于优化增删改时的性能。\n * 原理:Vue 的组件在属性发生改变时会重新渲染,通过 WeakMap 缓存 data -> TableRow 的映射,\n * 可以在数据更新时复用已有的 TableRowNode 实例,避免重复创建和 DOM 抖动。\n */\n let rowDicts = new WeakMap<Record<string, any>, TableRowNode>()\n let tempRowDicts: null | WeakMap<Record<string, any>, TableRowNode> = null\n let uidSeed = 0\n\n /** 获取行唯一标识 */\n const getRowUID = props.rowKey\n ? (rowData: Record<string, any>) => rowData && o(rowData).get(props.rowKey!)\n : () => uidSeed++\n\n /** 创建或复用 TableRow 实例 */\n const createRow = (\n data: Record<string, any>,\n index: number,\n depth = 0,\n parent?: TableRowNode\n ) => {\n const existRow = data ? rowDicts.get(data) : undefined\n if (existRow) {\n existRow.index = index\n existRow.depth = depth\n existRow.parent = parent\n return existRow\n }\n return new TableRowNode({ data, index, depth, parent, uid: getRowUID(data) })\n }\n\n // --- 行处理逻辑 ---\n\n /** 处理常规扁平列表 */\n function getDataRows(data: Record<string, any>[]): TableRowNode[] {\n let i = data.length\n const result = Array.from({ length: i }) as TableRowNode[]\n tempRowDicts = new WeakMap()\n\n while (i--) {\n const dataItem = data[i]!\n const row = createRow(dataItem, i)\n result[i] = row\n tempRowDicts.set(dataItem, row)\n }\n\n rowDicts = tempRowDicts\n tempRowDicts = null\n return result\n }\n\n function expandDataRows(): void {\n const result: TableRowNode[] = []\n let i = -1\n while (++i < dataRows.value.length) {\n const row = dataRows.value[i]!\n result.push(row)\n if (row.expanded) {\n result.push(row.copy())\n }\n }\n rows.value = result\n }\n\n /** 处理树形森林结构 */\n function getRowForest(data: Record<string, any>[]): Forest<Record<string, unknown>, any> {\n tempRowDicts = new WeakMap()\n const childrenKey = typeof props.tree === 'string' ? props.tree : 'children'\n const ret = new Forest<Record<string, unknown>, any>({\n data,\n childrenKey,\n createNode(val, index, depth, _forest, parent) {\n const row = createRow(val, index, depth, parent)\n if (props.defaultExpandAll) {\n row.expanded = true\n }\n val && tempRowDicts!.set(val, row)\n return row\n }\n })\n\n rowDicts = tempRowDicts\n tempRowDicts = null\n return ret\n }\n\n /** 展平树形结构为可见行列表 */\n function updateFlattedRows(): void {\n if (!rowForest.value) return\n rows.value = rowForest.value.flattenVisible((n) => n.expanded)\n }\n\n // --- 副作用监听 ---\n\n // 监听当前行切换,维护 isCurrent 状态\n watch(\n () => currentRow.value,\n (row, oldRow) => {\n if (oldRow) oldRow.isCurrent = false\n if (row) row.isCurrent = true\n }\n )\n\n // 同步状态给外部\n watch(rows, (rows) => emit('update:rows', rows))\n watch(rowForest, (forest) => emit('update:forest', forest))\n\n // 核心:监听原始数据变化,重新构建行列表或树\n watch(\n [() => props.data, () => props.tree, () => props.defaultExpandAll],\n ([data, tree]) => {\n if (!data?.length) {\n rows.value = []\n rowDicts = new WeakMap()\n rowForest.value = undefined\n return\n }\n\n if (!tree) {\n rowForest.value = undefined\n dataRows.value = getDataRows(data)\n rows.value = dataRows.value\n } else {\n dataRows.value = []\n rowForest.value = getRowForest(data)\n updateFlattedRows()\n }\n },\n { immediate: true }\n )\n\n // --- 交互方法 ---\n\n function toggleTreeRowExpand(node: TableRow): void {\n node.expanded = !node.expanded\n\n if (props.tree) {\n return updateFlattedRows()\n }\n if (props.expandable) {\n expandDataRows()\n }\n }\n\n function toggleAllTreeRowExpand(): void {\n allExpanded.value = !allExpanded.value\n if (props.tree) {\n rowForest.value?.dfs((node) => {\n node.expanded = allExpanded.value\n })\n updateFlattedRows()\n return\n }\n if (props.expandable) {\n dataRows.value.forEach((row) => {\n row.expanded = allExpanded.value\n })\n expandDataRows()\n return\n }\n }\n\n function handleRowClick(row: TableRow, e: MouseEvent): void {\n currentRow.value = currentRow.value === row ? undefined : row\n emit('row-click', row, e)\n }\n\n function handleCellClick(row: TableRow, column: TableColumn, e: MouseEvent): void {\n emit('cell-click', row, column, e)\n }\n\n function getRowByData(data: Record<string, any>): TableRow | undefined {\n return rowDicts.get(data)\n }\n\n return {\n rowForest,\n rows,\n allExpanded,\n toggleTreeRowExpand,\n toggleAllTreeRowExpand,\n handleRowClick,\n handleCellClick,\n getRowByData\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-rows.js","names":["useModel"],"sources":["../../../src/components/table/use-rows.ts"],"sourcesContent":["import { Forest, o } from '@cat-kit/core'\nimport { useModel } from '@veltra/compositions'\nimport { shallowRef, watch, type Ref, type ShallowRef } from 'vue'\n\nimport type { TableColumn, TableEmits, TableProps, TableRow } from '../../types'\nimport { TableRowNode } from './node/row'\n\ninterface Options {\n props: TableProps\n emit: TableEmits\n /**\n * D2:滚动中 (`true`) 跳过 `currentRow` 高亮写入。\n *\n * 行点击处理会通过 `currentRow` watcher 把 `isCurrent` 切换到新行 / 旧行上,\n * 触发对应 `<tr>` 的 class 重绘。在滚动期间 (移动端/触控板惯性滚动常见)\n * 意外点击不应改变高亮,因此直接跳过;滚动结束后的正常点击仍会生效。\n */\n isScrolling?: Ref<boolean>\n}\n\ninterface UseRowsReturned {\n /** 所有的行森林(树形数据) */\n rowForest: ShallowRef<Forest<Record<string, unknown>, any> | undefined>\n /** 当前可见的展平行列表 */\n rows: ShallowRef<TableRowNode[]>\n /** 切换树形行节点的展开/折叠 */\n toggleTreeRowExpand: (node: TableRow) => void\n /** 是否全部展开 */\n allExpanded: ShallowRef<boolean>\n /** 切换全部展开/折叠 */\n toggleAllTreeRowExpand: () => void\n /** 行点击处理 */\n handleRowClick: (row: TableRow, e: MouseEvent) => void\n /** 单元格点击处理 */\n handleCellClick: (row: TableRow, column: TableColumn, e: MouseEvent) => void\n /** 根据原始数据对象获取对应的 TableRow */\n getRowByData: (data: Record<string, any>) => TableRow | undefined\n}\n\nexport function useRows(options: Options): UseRowsReturned {\n const { props, emit, isScrolling } = options\n\n // --- 状态定义 ---\n\n /** 所有可见的行 */\n const rows = shallowRef<TableRowNode[]>([])\n /** 数据行, 不包括展开和折叠的行 */\n const dataRows = shallowRef<TableRowNode[]>([])\n /** 行森林(数据结构树) */\n const rowForest = shallowRef<Forest<Record<string, unknown>, any>>()\n /** 树形节点是否全部展开 */\n const allExpanded = shallowRef(props.defaultExpandAll ?? false)\n\n /** 当前高亮的行 */\n const currentRow = useModel({\n props,\n emit,\n propName: 'current',\n shallow: true,\n local: () => !!props.highlightCurrent\n })\n\n // --- 行字典维护 (性能优化) ---\n\n /**\n * 用于优化增删改时的性能。\n * 原理:Vue 的组件在属性发生改变时会重新渲染,通过 WeakMap 缓存 data -> TableRow 的映射,\n * 可以在数据更新时复用已有的 TableRowNode 实例,避免重复创建和 DOM 抖动。\n */\n let rowDicts = new WeakMap<Record<string, any>, TableRowNode>()\n let tempRowDicts: null | WeakMap<Record<string, any>, TableRowNode> = null\n let uidSeed = 0\n\n /** 获取行唯一标识 */\n const getRowUID = props.rowKey\n ? (rowData: Record<string, any>) => rowData && o(rowData).get(props.rowKey!)\n : () => uidSeed++\n\n /** 创建或复用 TableRow 实例 */\n const createRow = (\n data: Record<string, any>,\n index: number,\n depth = 0,\n parent?: TableRowNode\n ) => {\n const existRow = data ? rowDicts.get(data) : undefined\n if (existRow) {\n existRow.index = index\n existRow.depth = depth\n existRow.parent = parent\n return existRow\n }\n return new TableRowNode({ data, index, depth, parent, uid: getRowUID(data) })\n }\n\n // --- 行处理逻辑 ---\n\n /** 处理常规扁平列表 */\n function getDataRows(data: Record<string, any>[]): TableRowNode[] {\n let i = data.length\n const result = Array.from({ length: i }) as TableRowNode[]\n tempRowDicts = new WeakMap()\n\n while (i--) {\n const dataItem = data[i]!\n const row = createRow(dataItem, i)\n result[i] = row\n tempRowDicts.set(dataItem, row)\n }\n\n rowDicts = tempRowDicts\n tempRowDicts = null\n return result\n }\n\n function expandDataRows(): void {\n const result: TableRowNode[] = []\n let i = -1\n while (++i < dataRows.value.length) {\n const row = dataRows.value[i]!\n result.push(row)\n if (row.expanded) {\n result.push(row.copy())\n }\n }\n rows.value = result\n }\n\n /** 处理树形森林结构 */\n function getRowForest(data: Record<string, any>[]): Forest<Record<string, unknown>, any> {\n tempRowDicts = new WeakMap()\n const childrenKey = typeof props.tree === 'string' ? props.tree : 'children'\n const ret = new Forest<Record<string, unknown>, any>({\n data,\n childrenKey,\n createNode(val, index, depth, _forest, parent) {\n const row = createRow(val, index, depth, parent)\n if (props.defaultExpandAll) {\n row.expanded = true\n }\n val && tempRowDicts!.set(val, row)\n return row\n }\n })\n\n rowDicts = tempRowDicts\n tempRowDicts = null\n return ret\n }\n\n /** 展平树形结构为可见行列表 */\n function updateFlattedRows(): void {\n if (!rowForest.value) return\n rows.value = rowForest.value.flattenVisible((n) => n.expanded)\n }\n\n // --- 副作用监听 ---\n\n // 监听当前行切换,维护 isCurrent 状态\n watch(\n () => currentRow.value,\n (row, oldRow) => {\n if (oldRow) oldRow.isCurrent = false\n if (row) row.isCurrent = true\n }\n )\n\n // 同步状态给外部\n watch(rows, (rows) => emit('update:rows', rows))\n watch(rowForest, (forest) => emit('update:forest', forest))\n\n // 核心:监听原始数据变化,重新构建行列表或树\n watch(\n [() => props.data, () => props.tree, () => props.defaultExpandAll],\n ([data, tree]) => {\n if (!data?.length) {\n rows.value = []\n rowDicts = new WeakMap()\n rowForest.value = undefined\n return\n }\n\n if (!tree) {\n rowForest.value = undefined\n dataRows.value = getDataRows(data)\n rows.value = dataRows.value\n } else {\n dataRows.value = []\n rowForest.value = getRowForest(data)\n updateFlattedRows()\n }\n },\n { immediate: true }\n )\n\n // --- 交互方法 ---\n\n function toggleTreeRowExpand(node: TableRow): void {\n node.expanded = !node.expanded\n\n if (props.tree) {\n return updateFlattedRows()\n }\n if (props.expandable) {\n expandDataRows()\n }\n }\n\n function toggleAllTreeRowExpand(): void {\n allExpanded.value = !allExpanded.value\n if (props.tree) {\n rowForest.value?.dfs((node) => {\n node.expanded = allExpanded.value\n })\n updateFlattedRows()\n return\n }\n if (props.expandable) {\n dataRows.value.forEach((row) => {\n row.expanded = allExpanded.value\n })\n expandDataRows()\n return\n }\n }\n\n function handleRowClick(row: TableRow, e: MouseEvent): void {\n // D2:滚动中的点击(多为惯性滚动误触)不应改变 currentRow 高亮。\n if (!isScrolling?.value) {\n currentRow.value = currentRow.value === row ? undefined : row\n }\n emit('row-click', row, e)\n }\n\n function handleCellClick(row: TableRow, column: TableColumn, e: MouseEvent): void {\n emit('cell-click', row, column, e)\n }\n\n function getRowByData(data: Record<string, any>): TableRow | undefined {\n return rowDicts.get(data)\n }\n\n return {\n rowForest,\n rows,\n allExpanded,\n toggleTreeRowExpand,\n toggleAllTreeRowExpand,\n handleRowClick,\n handleCellClick,\n getRowByData\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { useSlots } from "vue";
1
+ import { useSlots, watch } from "vue";
2
2
  import { o } from "@cat-kit/core";
3
3
  import { bem } from "@veltra/utils";
4
4
  //#region src/components/table/use-table.ts
@@ -33,11 +33,33 @@ function useTable(options) {
33
33
  if (column.fixed) classList.push(bem.is("fixed-" + column.fixed));
34
34
  return classList.join(" ");
35
35
  };
36
+ /**
37
+ * B1: 单元格类名按 ColumnNode 身份缓存。
38
+ *
39
+ * 对同一列,类名只取决于 (column.fixed, column.isLastFixed, column.isFirstFixed,
40
+ * column.align, leftFixed, rightFixed)。其中前四个绑定在 ColumnNode 实例上,
41
+ * 后两个在 leftFixed/rightFixed 变化时失效缓存即可。用 WeakMap 以 ColumnNode
42
+ * 为键可避免字符串 key 的拼接/哈希开销,且列树重建时节点随 GC 自动释放。
43
+ */
44
+ let cellClassCache = /* @__PURE__ */ new WeakMap();
45
+ let headerCellClassCache = /* @__PURE__ */ new WeakMap();
46
+ watch([leftFixed, rightFixed], () => {
47
+ cellClassCache = /* @__PURE__ */ new WeakMap();
48
+ headerCellClassCache = /* @__PURE__ */ new WeakMap();
49
+ });
36
50
  const getCellClass = (column) => {
37
- return getCommonClassName(column) + ` ${bem.is(column.align)}`;
51
+ const cached = cellClassCache.get(column);
52
+ if (cached !== void 0) return cached;
53
+ const result = getCommonClassName(column) + ` ${bem.is(column.align)}`;
54
+ cellClassCache.set(column, result);
55
+ return result;
38
56
  };
39
57
  const getHeaderCellClass = (column) => {
40
- return getCommonClassName(column) + ` ${bem.is(column.headerAlign)}`;
58
+ const cached = headerCellClassCache.get(column);
59
+ if (cached !== void 0) return cached;
60
+ const result = getCommonClassName(column) + ` ${bem.is(column.headerAlign)}`;
61
+ headerCellClassCache.set(column, result);
62
+ return result;
41
63
  };
42
64
  const getCellCtx = (row, column) => {
43
65
  const rowData = row.data;
@@ -56,11 +78,36 @@ function useTable(options) {
56
78
  };
57
79
  };
58
80
  return {
81
+ /**
82
+ * 获取列插槽VNode
83
+ * @param ctx 列渲染上下文
84
+ */
59
85
  getColumnSlotsNode,
86
+ /**
87
+ * 获取表头插槽VNode
88
+ * @param ctx 表头渲染上下文
89
+ */
60
90
  getHeaderSlotsNode,
91
+ /**
92
+ * 获取展开行插槽VNode
93
+ * @param ctx 展开行渲染上下文
94
+ */
61
95
  getExpandRowSlotsNode,
96
+ /**
97
+ * 获取单元格类名
98
+ * @param column 列
99
+ */
62
100
  getCellClass,
101
+ /**
102
+ * 获取表头单元格类名
103
+ * @param column 列
104
+ */
63
105
  getHeaderCellClass,
106
+ /**
107
+ * 获取单元格上下文
108
+ * @param row 行
109
+ * @param column 列
110
+ */
64
111
  getCellCtx
65
112
  };
66
113
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-table.js","names":[],"sources":["../../../src/components/table/use-table.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { bem, type BEM } from '@veltra/utils'\nimport { useSlots, type Ref } from 'vue'\n\nimport type {\n TableColumnRenderContext,\n TableColumnSlotsScope,\n TableProps,\n TableRow,\n RenderReturn,\n TableRowSlotsScope\n} from '../../types'\nimport type { ColumnNode } from './node/col'\n\ninterface Options {\n props: TableProps\n cls: BEM<'table'>\n leftFixed: Ref<boolean>\n rightFixed: Ref<boolean>\n}\n\ninterface UseTableReturn {\n getColumnSlotsNode: (ctx: TableColumnSlotsScope | TableColumnRenderContext) => RenderReturn\n getHeaderSlotsNode: (ctx: { column: ColumnNode }) => RenderReturn\n getExpandRowSlotsNode: (ctx: TableRowSlotsScope) => RenderReturn\n getCellClass: (column: ColumnNode) => string\n getHeaderCellClass: (column: ColumnNode) => string\n getCellCtx: (\n row: TableRow,\n column: ColumnNode\n ) => TableColumnSlotsScope | TableColumnRenderContext\n}\n\nexport function useTable(options: Options): UseTableReturn {\n const { props, cls, leftFixed, rightFixed } = options\n\n const slots = useSlots()\n\n const getColumnSlotsNode = (\n ctx: TableColumnSlotsScope | TableColumnRenderContext\n ): RenderReturn => {\n const column = ctx.column.data\n\n const { render, key } = column\n\n if (render) return render(ctx)\n\n const slotsRender = props.slots?.[`column:${key}`] ?? slots[`column:${key}`]\n\n if (slotsRender) return slotsRender(ctx)\n\n return ctx.val\n }\n\n const getHeaderSlotsNode = (ctx: { column: ColumnNode }): RenderReturn => {\n const column = ctx.column.data\n const { nameRender, key } = column\n\n if (nameRender) return nameRender(ctx)\n\n const slotsRender = props.slots?.[`header:${key}`] ?? slots[`header:${key}`]\n\n if (slotsRender) return slotsRender(ctx)\n\n return column.name\n }\n\n const getExpandRowSlotsNode = (ctx: TableRowSlotsScope): RenderReturn => {\n const slotsRender = props.slots?.['row:expand'] ?? slots['row:expand']\n\n if (slotsRender) return slotsRender(ctx)\n return null\n }\n\n const cellCls = cls.e('cell')\n\n const getCommonClassName = (column: ColumnNode): string => {\n const classList: string[] = [cellCls]\n\n leftFixed.value && column.isLastFixed && classList.push(bem.is('last-fixed'))\n\n rightFixed.value && column.isFirstFixed && classList.push(bem.is('first-fixed'))\n\n if (column.fixed) {\n classList.push(bem.is('fixed-' + column.fixed))\n }\n return classList.join(' ')\n }\n\n const getCellClass = (column: ColumnNode): string => {\n return getCommonClassName(column) + ` ${bem.is(column.align)}`\n }\n\n const getHeaderCellClass = (column: ColumnNode): string => {\n return getCommonClassName(column) + ` ${bem.is(column.headerAlign)}`\n }\n\n const getCellCtx = (\n row: TableRow,\n column: ColumnNode\n ): TableColumnSlotsScope | TableColumnRenderContext => {\n const rowData = row.data\n const val = column.key ? o(rowData).get(column.key) : undefined\n\n const ctx = {\n row,\n column,\n rowData,\n val,\n model: {\n modelValue: val,\n 'onUpdate:modelValue': (val: any) => {\n rowData[column.key] = val\n }\n }\n }\n\n return ctx\n }\n\n return {\n /**\n * 获取列插槽VNode\n * @param ctx 列渲染上下文\n */\n getColumnSlotsNode,\n /**\n * 获取表头插槽VNode\n * @param ctx 表头渲染上下文\n */\n getHeaderSlotsNode,\n /**\n * 获取展开行插槽VNode\n * @param ctx 展开行渲染上下文\n */\n getExpandRowSlotsNode,\n /**\n * 获取单元格类名\n * @param column 列\n */\n getCellClass,\n /**\n * 获取表头单元格类名\n * @param column 列\n */\n getHeaderCellClass,\n /**\n * 获取单元格上下文\n * @param row 行\n * @param column 列\n */\n getCellCtx\n }\n}\n"],"mappings":";;;;AAiCA,SAAgB,SAAS,SAAkC;CACzD,MAAM,EAAE,OAAO,KAAK,WAAW,eAAe;CAE9C,MAAM,QAAQ,UAAU;CAExB,MAAM,sBACJ,QACiB;EAGjB,MAAM,EAAE,QAAQ,QAFD,IAAI,OAAO;AAI1B,MAAI,OAAQ,QAAO,OAAO,IAAI;EAE9B,MAAM,cAAc,MAAM,QAAQ,UAAU,UAAU,MAAM,UAAU;AAEtE,MAAI,YAAa,QAAO,YAAY,IAAI;AAExC,SAAO,IAAI;;CAGb,MAAM,sBAAsB,QAA8C;EACxE,MAAM,SAAS,IAAI,OAAO;EAC1B,MAAM,EAAE,YAAY,QAAQ;AAE5B,MAAI,WAAY,QAAO,WAAW,IAAI;EAEtC,MAAM,cAAc,MAAM,QAAQ,UAAU,UAAU,MAAM,UAAU;AAEtE,MAAI,YAAa,QAAO,YAAY,IAAI;AAExC,SAAO,OAAO;;CAGhB,MAAM,yBAAyB,QAA0C;EACvE,MAAM,cAAc,MAAM,QAAQ,iBAAiB,MAAM;AAEzD,MAAI,YAAa,QAAO,YAAY,IAAI;AACxC,SAAO;;CAGT,MAAM,UAAU,IAAI,EAAE,OAAO;CAE7B,MAAM,sBAAsB,WAA+B;EACzD,MAAM,YAAsB,CAAC,QAAQ;AAErC,YAAU,SAAS,OAAO,eAAe,UAAU,KAAK,IAAI,GAAG,aAAa,CAAC;AAE7E,aAAW,SAAS,OAAO,gBAAgB,UAAU,KAAK,IAAI,GAAG,cAAc,CAAC;AAEhF,MAAI,OAAO,MACT,WAAU,KAAK,IAAI,GAAG,WAAW,OAAO,MAAM,CAAC;AAEjD,SAAO,UAAU,KAAK,IAAI;;CAG5B,MAAM,gBAAgB,WAA+B;AACnD,SAAO,mBAAmB,OAAO,GAAG,IAAI,IAAI,GAAG,OAAO,MAAM;;CAG9D,MAAM,sBAAsB,WAA+B;AACzD,SAAO,mBAAmB,OAAO,GAAG,IAAI,IAAI,GAAG,OAAO,YAAY;;CAGpE,MAAM,cACJ,KACA,WACqD;EACrD,MAAM,UAAU,IAAI;EACpB,MAAM,MAAM,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO,IAAI,GAAG,KAAA;AAetD,SAbY;GACV;GACA;GACA;GACA;GACA,OAAO;IACL,YAAY;IACZ,wBAAwB,QAAa;AACnC,aAAQ,OAAO,OAAO;;IAEzB;GACF;;AAKH,QAAO;EAKL;EAKA;EAKA;EAKA;EAKA;EAMA;EACD"}
1
+ {"version":3,"file":"use-table.js","names":[],"sources":["../../../src/components/table/use-table.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { bem, type BEM } from '@veltra/utils'\nimport { useSlots, watch, type Ref } from 'vue'\n\nimport type {\n TableColumnRenderContext,\n TableColumnSlotsScope,\n TableProps,\n TableRow,\n RenderReturn,\n TableRowSlotsScope\n} from '../../types'\nimport type { ColumnNode } from './node/col'\n\ninterface Options {\n props: TableProps\n cls: BEM<'table'>\n leftFixed: Ref<boolean>\n rightFixed: Ref<boolean>\n}\n\ninterface UseTableReturn {\n getColumnSlotsNode: (ctx: TableColumnSlotsScope | TableColumnRenderContext) => RenderReturn\n getHeaderSlotsNode: (ctx: { column: ColumnNode }) => RenderReturn\n getExpandRowSlotsNode: (ctx: TableRowSlotsScope) => RenderReturn\n getCellClass: (column: ColumnNode) => string\n getHeaderCellClass: (column: ColumnNode) => string\n getCellCtx: (\n row: TableRow,\n column: ColumnNode\n ) => TableColumnSlotsScope | TableColumnRenderContext\n}\n\nexport function useTable(options: Options): UseTableReturn {\n const { props, cls, leftFixed, rightFixed } = options\n\n const slots = useSlots()\n\n const getColumnSlotsNode = (\n ctx: TableColumnSlotsScope | TableColumnRenderContext\n ): RenderReturn => {\n const column = ctx.column.data\n\n const { render, key } = column\n\n if (render) return render(ctx)\n\n const slotsRender = props.slots?.[`column:${key}`] ?? slots[`column:${key}`]\n\n if (slotsRender) return slotsRender(ctx)\n\n return ctx.val\n }\n\n const getHeaderSlotsNode = (ctx: { column: ColumnNode }): RenderReturn => {\n const column = ctx.column.data\n const { nameRender, key } = column\n\n if (nameRender) return nameRender(ctx)\n\n const slotsRender = props.slots?.[`header:${key}`] ?? slots[`header:${key}`]\n\n if (slotsRender) return slotsRender(ctx)\n\n return column.name\n }\n\n const getExpandRowSlotsNode = (ctx: TableRowSlotsScope): RenderReturn => {\n const slotsRender = props.slots?.['row:expand'] ?? slots['row:expand']\n\n if (slotsRender) return slotsRender(ctx)\n return null\n }\n\n const cellCls = cls.e('cell')\n\n const getCommonClassName = (column: ColumnNode): string => {\n const classList: string[] = [cellCls]\n\n leftFixed.value && column.isLastFixed && classList.push(bem.is('last-fixed'))\n\n rightFixed.value && column.isFirstFixed && classList.push(bem.is('first-fixed'))\n\n if (column.fixed) {\n classList.push(bem.is('fixed-' + column.fixed))\n }\n return classList.join(' ')\n }\n\n /**\n * B1: 单元格类名按 ColumnNode 身份缓存。\n *\n * 对同一列,类名只取决于 (column.fixed, column.isLastFixed, column.isFirstFixed,\n * column.align, leftFixed, rightFixed)。其中前四个绑定在 ColumnNode 实例上,\n * 后两个在 leftFixed/rightFixed 变化时失效缓存即可。用 WeakMap 以 ColumnNode\n * 为键可避免字符串 key 的拼接/哈希开销,且列树重建时节点随 GC 自动释放。\n */\n let cellClassCache = new WeakMap<ColumnNode, string>()\n let headerCellClassCache = new WeakMap<ColumnNode, string>()\n\n watch([leftFixed, rightFixed], () => {\n cellClassCache = new WeakMap()\n headerCellClassCache = new WeakMap()\n })\n\n const getCellClass = (column: ColumnNode): string => {\n const cached = cellClassCache.get(column)\n if (cached !== undefined) return cached\n const result = getCommonClassName(column) + ` ${bem.is(column.align)}`\n cellClassCache.set(column, result)\n return result\n }\n\n const getHeaderCellClass = (column: ColumnNode): string => {\n const cached = headerCellClassCache.get(column)\n if (cached !== undefined) return cached\n const result = getCommonClassName(column) + ` ${bem.is(column.headerAlign)}`\n headerCellClassCache.set(column, result)\n return result\n }\n\n const getCellCtx = (\n row: TableRow,\n column: ColumnNode\n ): TableColumnSlotsScope | TableColumnRenderContext => {\n const rowData = row.data\n const val = column.key ? o(rowData).get(column.key) : undefined\n\n const ctx = {\n row,\n column,\n rowData,\n val,\n model: {\n modelValue: val,\n 'onUpdate:modelValue': (val: any) => {\n rowData[column.key] = val\n }\n }\n }\n\n return ctx\n }\n\n return {\n /**\n * 获取列插槽VNode\n * @param ctx 列渲染上下文\n */\n getColumnSlotsNode,\n /**\n * 获取表头插槽VNode\n * @param ctx 表头渲染上下文\n */\n getHeaderSlotsNode,\n /**\n * 获取展开行插槽VNode\n * @param ctx 展开行渲染上下文\n */\n getExpandRowSlotsNode,\n /**\n * 获取单元格类名\n * @param column 列\n */\n getCellClass,\n /**\n * 获取表头单元格类名\n * @param column 列\n */\n getHeaderCellClass,\n /**\n * 获取单元格上下文\n * @param row 行\n * @param column 列\n */\n getCellCtx\n }\n}\n"],"mappings":";;;;AAiCA,SAAgB,SAAS,SAAkC;CACzD,MAAM,EAAE,OAAO,KAAK,WAAW,eAAe;CAE9C,MAAM,QAAQ,UAAU;CAExB,MAAM,sBACJ,QACiB;EAGjB,MAAM,EAAE,QAAQ,QAFD,IAAI,OAAO;AAI1B,MAAI,OAAQ,QAAO,OAAO,IAAI;EAE9B,MAAM,cAAc,MAAM,QAAQ,UAAU,UAAU,MAAM,UAAU;AAEtE,MAAI,YAAa,QAAO,YAAY,IAAI;AAExC,SAAO,IAAI;;CAGb,MAAM,sBAAsB,QAA8C;EACxE,MAAM,SAAS,IAAI,OAAO;EAC1B,MAAM,EAAE,YAAY,QAAQ;AAE5B,MAAI,WAAY,QAAO,WAAW,IAAI;EAEtC,MAAM,cAAc,MAAM,QAAQ,UAAU,UAAU,MAAM,UAAU;AAEtE,MAAI,YAAa,QAAO,YAAY,IAAI;AAExC,SAAO,OAAO;;CAGhB,MAAM,yBAAyB,QAA0C;EACvE,MAAM,cAAc,MAAM,QAAQ,iBAAiB,MAAM;AAEzD,MAAI,YAAa,QAAO,YAAY,IAAI;AACxC,SAAO;;CAGT,MAAM,UAAU,IAAI,EAAE,OAAO;CAE7B,MAAM,sBAAsB,WAA+B;EACzD,MAAM,YAAsB,CAAC,QAAQ;AAErC,YAAU,SAAS,OAAO,eAAe,UAAU,KAAK,IAAI,GAAG,aAAa,CAAC;AAE7E,aAAW,SAAS,OAAO,gBAAgB,UAAU,KAAK,IAAI,GAAG,cAAc,CAAC;AAEhF,MAAI,OAAO,MACT,WAAU,KAAK,IAAI,GAAG,WAAW,OAAO,MAAM,CAAC;AAEjD,SAAO,UAAU,KAAK,IAAI;;;;;;;;;;CAW5B,IAAI,iCAAiB,IAAI,SAA6B;CACtD,IAAI,uCAAuB,IAAI,SAA6B;AAE5D,OAAM,CAAC,WAAW,WAAW,QAAQ;AACnC,mCAAiB,IAAI,SAAS;AAC9B,yCAAuB,IAAI,SAAS;GACpC;CAEF,MAAM,gBAAgB,WAA+B;EACnD,MAAM,SAAS,eAAe,IAAI,OAAO;AACzC,MAAI,WAAW,KAAA,EAAW,QAAO;EACjC,MAAM,SAAS,mBAAmB,OAAO,GAAG,IAAI,IAAI,GAAG,OAAO,MAAM;AACpE,iBAAe,IAAI,QAAQ,OAAO;AAClC,SAAO;;CAGT,MAAM,sBAAsB,WAA+B;EACzD,MAAM,SAAS,qBAAqB,IAAI,OAAO;AAC/C,MAAI,WAAW,KAAA,EAAW,QAAO;EACjC,MAAM,SAAS,mBAAmB,OAAO,GAAG,IAAI,IAAI,GAAG,OAAO,YAAY;AAC1E,uBAAqB,IAAI,QAAQ,OAAO;AACxC,SAAO;;CAGT,MAAM,cACJ,KACA,WACqD;EACrD,MAAM,UAAU,IAAI;EACpB,MAAM,MAAM,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO,IAAI,GAAG,KAAA;AAetD,SAAO;GAZL;GACA;GACA;GACA;GACA,OAAO;IACL,YAAY;IACZ,wBAAwB,QAAa;AACnC,aAAQ,OAAO,OAAO;;IAEzB;GAGO;;AAGZ,QAAO;;;;;EAKL;;;;;EAKA;;;;;EAKA;;;;;EAKA;;;;;EAKA;;;;;;EAMA;EACD"}
@@ -1 +1,3 @@
1
- import { _default } from "./tabs.vue.js";
1
+ import { _default } from "./tabs.vue.js";
2
+ import { _default as _default$1 } from "./tabs-horizontal.vue.js";
3
+ import { _default as _default$2 } from "./tabs-vertical.vue.js";
@@ -15,152 +15,215 @@
15
15
  * --u-height-small: 24px;
16
16
  */
17
17
  /** 暗色:data-theme 与系统偏好,与 UITheme.injectBuiltInThemes 选择器策略一致 */
18
+ /** shadcn 风格 TabsList:hover 背景底色容器 + 白底活动态;rounded=true 时水平为胶囊状。 */
19
+ /* ===== 复合标签页:bar + content 布局容器 ===== */
18
20
  .u-tabs {
19
21
  display: flex;
20
- }
21
- .u-tabs__header {
22
- flex-shrink: 0;
22
+ gap: var(--u-gap-small);
23
23
  }
24
24
  .u-tabs__content {
25
- flex-grow: 1;
25
+ flex: 1 1 auto;
26
+ width: 100%;
27
+ min-height: 0;
28
+ border-radius: var(--u-radius-default);
26
29
  }
27
- .u-tabs__header-item {
28
- cursor: pointer;
29
- font-weight: 600;
30
- letter-spacing: 1px;
31
- color: var(--u-text-color-main);
32
- user-select: none;
33
- position: relative;
30
+ .u-tabs--top {
31
+ flex-direction: column;
34
32
  }
35
- .u-tabs__header-item::after {
36
- content: "";
37
- position: absolute;
38
- background-color: var(--u-color-primary);
39
- transition-duration: 0.25s;
40
- transition-property: transform;
41
- transition-timing-function: cubic-bezier(0.35, 0, 0.25, 1);
33
+ .u-tabs--bottom {
34
+ flex-direction: column-reverse;
42
35
  }
43
- .u-tabs__header-item {
44
- display: flex;
45
- justify-content: flex-start;
46
- align-items: center;
47
- flex-wrap: nowrap;
36
+ .u-tabs--left {
37
+ flex-direction: row;
48
38
  }
49
- .u-tabs__header-item.is-disabled {
50
- color: var(--u-text-color-disabled);
51
- cursor: not-allowed;
39
+ .u-tabs--left .u-tabs__content {
40
+ height: 100%;
52
41
  }
53
- .u-tabs__header-item.is-active {
54
- color: var(--u-color-primary);
42
+ .u-tabs--right {
43
+ flex-direction: row-reverse;
55
44
  }
56
- .u-tabs__close {
57
- width: 0;
58
- transition-property: width, margin-left;
59
- transition-duration: 0.25s;
60
- margin-left: 0;
45
+ .u-tabs--right .u-tabs__content {
46
+ height: 100%;
61
47
  }
62
- .u-tabs__close-placeholder {
63
- height: 1em;
64
- width: 1em;
65
- margin-left: var(--u-gap-small);
48
+
49
+ /* ===== 标签栏(独立组件):水平 / 垂直 ===== */
50
+ .u-tabs-bar {
51
+ display: inline-flex;
52
+ align-self: flex-start;
53
+ max-width: 100%;
54
+ min-width: 0;
55
+ padding: 0 4px;
56
+ background-color: var(--u-bg-color-hover);
57
+ border-radius: var(--u-radius-default);
58
+ gap: 2px;
66
59
  }
67
- .u-tabs--top .u-tabs__header, .u-tabs--bottom .u-tabs__header {
68
- display: flex;
69
- justify-content: flex-start;
60
+ .u-tabs-bar--horizontal {
70
61
  align-items: center;
71
- flex-wrap: nowrap;
72
- }
73
- .u-tabs--top .u-tabs__header-item::after, .u-tabs--bottom .u-tabs__header-item::after {
74
- bottom: 0;
75
- left: 0;
76
- height: 2px;
77
- width: 100%;
78
- transform: scaleX(0);
79
- }
80
- .u-tabs--top .u-tabs__header-item.is-active::after, .u-tabs--bottom .u-tabs__header-item.is-active::after {
81
- transform: scaleX(1);
62
+ position: relative;
63
+ flex-shrink: 0;
82
64
  }
83
- .u-tabs--top {
65
+ .u-tabs-bar--vertical {
84
66
  flex-direction: column;
67
+ padding-block: 4px;
85
68
  }
86
- .u-tabs--top .u-tabs__header {
87
- border-bottom: var(--u-border);
69
+ .u-tabs-bar__viewport {
70
+ flex: 1 1 auto;
71
+ min-width: 0;
72
+ padding: 4px 0;
73
+ overflow-x: auto;
74
+ overflow-y: hidden;
75
+ scrollbar-width: none;
88
76
  }
89
- .u-tabs--bottom {
90
- flex-direction: column-reverse;
77
+ .u-tabs-bar__viewport::-webkit-scrollbar {
78
+ display: none;
91
79
  }
92
- .u-tabs--bottom .u-tabs__header {
93
- border-top: var(--u-border);
80
+ .u-tabs-bar__list {
81
+ display: inline-flex;
82
+ flex-wrap: nowrap;
83
+ flex-shrink: 0;
84
+ width: max-content;
85
+ margin: 0;
86
+ padding: 0;
87
+ gap: 2px;
94
88
  }
95
- .u-tabs--bottom .u-tabs__header-item {
96
- border-radius: 0 0 var(--u-radius-large) var(--u-radius-large);
89
+ .u-tabs-bar__item {
90
+ cursor: pointer;
91
+ font-weight: 500;
92
+ letter-spacing: 0.25px;
93
+ color: var(--u-text-color-main);
94
+ user-select: none;
95
+ position: relative;
96
+ border-radius: calc(var(--u-radius-default) - 2px);
97
+ background-color: transparent;
98
+ box-shadow: none;
99
+ border: none;
100
+ outline: none;
101
+ white-space: nowrap;
102
+ display: inline-flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ gap: 0;
106
+ transition: color 0.22s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.22s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.22s cubic-bezier(0.4, 0, 0.2, 1);
97
107
  }
98
- .u-tabs--left .u-tabs__content, .u-tabs--right .u-tabs__content {
99
- height: 100%;
108
+ .u-tabs-bar__item:not(.is-active):not(.is-disabled):hover {
109
+ color: var(--u-text-color-title);
100
110
  }
101
- .u-tabs--left .u-tabs__header-item::after, .u-tabs--right .u-tabs__header-item::after {
102
- top: 0;
103
- width: 2px;
104
- height: 100%;
105
- transform: scaleY(0);
111
+ .u-tabs-bar__item.is-disabled {
112
+ color: var(--u-text-color-disabled);
113
+ cursor: not-allowed;
106
114
  }
107
- .u-tabs--left .u-tabs__header-item.is-active::after, .u-tabs--right .u-tabs__header-item.is-active::after {
108
- transform: scaleY(1);
115
+ .u-tabs-bar__item.is-active {
116
+ color: var(--u-color-primary);
117
+ background-color: var(--u-bg-color-top);
118
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08), 0 1px 3px rgba(15, 23, 42, 0.06);
109
119
  }
110
- .u-tabs--left .u-tabs__header {
111
- border-right: var(--u-border);
120
+ .u-tabs-bar__item-label {
121
+ display: inline-flex;
122
+ align-items: center;
123
+ min-width: 0;
112
124
  }
113
- .u-tabs--left .u-tabs__header-item {
114
- border-radius: var(--u-radius-large) 0 0 var(--u-radius-large);
115
- justify-content: flex-end;
125
+ .u-tabs-bar__close {
126
+ flex: 0 0 auto;
127
+ display: inline-flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ width: 16px;
131
+ height: 16px;
132
+ margin-left: 6px;
133
+ overflow: hidden;
134
+ padding: 0;
135
+ border: none;
136
+ background: transparent;
137
+ color: var(--u-text-color-secondary);
138
+ cursor: pointer;
139
+ border-radius: var(--u-radius-default);
140
+ font-size: 12px;
141
+ line-height: 1;
142
+ transition: width 0.18s ease, margin-left 0.18s ease, opacity 0.18s ease, transform 0.18s ease, color 0.15s ease, background-color 0.15s ease;
116
143
  }
117
- .u-tabs--left .u-tabs__header-item::after {
118
- right: 0;
144
+ .u-tabs-bar__close:hover {
145
+ color: var(--u-text-color-title);
146
+ background-color: rgba(15, 23, 42, 0.08);
119
147
  }
120
- .u-tabs--right {
121
- flex-direction: row-reverse;
148
+ .u-tabs-bar__close:active {
149
+ background-color: rgba(15, 23, 42, 0.14);
150
+ }
151
+ .u-tabs-bar--horizontal .u-tabs-bar__item .u-tabs-bar__close {
152
+ width: 0;
153
+ margin-left: 0;
154
+ opacity: 0;
155
+ transform: scale(0.6);
156
+ pointer-events: none;
157
+ }
158
+ .u-tabs-bar--horizontal .u-tabs-bar__item:hover .u-tabs-bar__close {
159
+ width: 16px;
160
+ margin-left: 6px;
161
+ opacity: 1;
162
+ transform: scale(1);
163
+ pointer-events: auto;
164
+ }
165
+ .u-tabs-bar--vertical .u-tabs-bar__item {
166
+ justify-content: space-between;
167
+ }
168
+ .u-tabs-bar__nav {
169
+ flex: 0 0 auto;
170
+ display: inline-flex;
171
+ align-items: center;
172
+ justify-content: center;
173
+ padding: 0;
174
+ border: none;
175
+ background: transparent;
176
+ color: var(--u-text-color-main);
177
+ cursor: pointer;
178
+ user-select: none;
179
+ border-radius: calc(var(--u-radius-default) - 2px);
180
+ transition: color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
122
181
  }
123
- .u-tabs--right .u-tabs__header {
124
- border-left: var(--u-border);
182
+ .u-tabs-bar__nav:hover:not(:disabled) {
183
+ color: var(--u-text-color-title);
184
+ background-color: var(--u-bg-color-top);
185
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08), 0 1px 3px rgba(15, 23, 42, 0.06);
125
186
  }
126
- .u-tabs--right .u-tabs__header-item {
127
- border-radius: 0 var(--u-radius-large) var(--u-radius-large) 0;
187
+ .u-tabs-bar__nav:disabled {
188
+ cursor: not-allowed;
189
+ opacity: 0.4;
128
190
  }
129
- .u-tabs--right .u-tabs__header-item::after {
130
- left: 0;
191
+ .u-tabs-bar.is-block {
192
+ display: flex;
193
+ width: 100%;
194
+ max-width: 100%;
195
+ align-self: stretch;
131
196
  }
132
- .u-tabs--small .u-tabs__header-item {
133
- height: var(--u-form-component-height-small);
197
+ .u-tabs-bar--small .u-tabs-bar__item {
198
+ height: calc(var(--u-form-component-height-small) - 4px);
134
199
  padding: 0 var(--u-gap-small);
135
200
  }
136
- .u-tabs--small .u-tabs__header-item:hover .u-tabs__close {
137
- width: var(--u-font-size-main-small);
138
- margin-left: var(--u-gap-small);
201
+ .u-tabs-bar--small .u-tabs-bar__nav {
202
+ width: calc(var(--u-form-component-height-small) - 4px);
203
+ height: calc(var(--u-form-component-height-small) - 4px);
139
204
  }
140
- .u-tabs--default .u-tabs__header-item {
141
- height: var(--u-form-component-height-default);
205
+ .u-tabs-bar--default .u-tabs-bar__item {
206
+ height: calc(var(--u-form-component-height-default) - 4px);
142
207
  padding: 0 var(--u-gap-default);
143
208
  }
144
- .u-tabs--default .u-tabs__header-item:hover .u-tabs__close {
145
- width: var(--u-font-size-main-default);
146
- margin-left: var(--u-gap-small);
209
+ .u-tabs-bar--default .u-tabs-bar__nav {
210
+ width: calc(var(--u-form-component-height-default) - 4px);
211
+ height: calc(var(--u-form-component-height-default) - 4px);
147
212
  }
148
- .u-tabs--large .u-tabs__header-item {
149
- height: var(--u-form-component-height-large);
213
+ .u-tabs-bar--large .u-tabs-bar__item {
214
+ height: calc(var(--u-form-component-height-large) - 4px);
150
215
  padding: 0 var(--u-gap-large);
151
216
  }
152
- .u-tabs--large .u-tabs__header-item:hover .u-tabs__close {
153
- width: var(--u-font-size-main-large);
154
- margin-left: var(--u-gap-small);
217
+ .u-tabs-bar--large .u-tabs-bar__nav {
218
+ width: calc(var(--u-form-component-height-large) - 4px);
219
+ height: calc(var(--u-form-component-height-large) - 4px);
155
220
  }
156
- .u-tabs--left .u-tabs__close, .u-tabs--right .u-tabs__close {
157
- width: auto;
221
+ .u-tabs-bar.is-rounded .u-tabs-bar__item {
222
+ border-radius: 9999px;
158
223
  }
159
- .u-tabs__content {
160
- width: 100%;
161
- border-radius: var(--u-radius-default);
224
+ .u-tabs-bar.is-rounded .u-tabs-bar__nav {
225
+ border-radius: 9999px;
162
226
  }
163
- .u-tabs__ripple {
164
- background-color: var(--u-color-primary);
165
- opacity: 0.1;
227
+ .u-tabs-bar--horizontal.is-rounded {
228
+ border-radius: 9999px;
166
229
  }
@@ -0,0 +1,133 @@
1
+ import _sfc_main$1 from "../icon/icon.js";
2
+ import { useTabsBar } from "./use-tabs-bar.js";
3
+ import { Fragment, computed, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeModels, normalizeClass, openBlock, renderList, renderSlot, shallowRef, toDisplayString, unref, useModel, vShow, withCtx, withDirectives, withModifiers } from "vue";
4
+ import { bem } from "@veltra/utils";
5
+ import { useFallbackProps } from "@veltra/compositions";
6
+ import { ArrowLeft, ArrowRight, Close } from "@veltra/icons/normal";
7
+ //#region src/components/tabs/tabs-horizontal.vue
8
+ const _hoisted_1 = ["disabled"];
9
+ const _hoisted_2 = ["onClick"];
10
+ const _hoisted_3 = ["aria-label", "onClick"];
11
+ const _hoisted_4 = ["disabled"];
12
+ const _sfc_main = /* @__PURE__ */ defineComponent({
13
+ name: "TabsHorizontal",
14
+ __name: "tabs-horizontal",
15
+ props: /* @__PURE__ */ mergeModels({
16
+ size: {},
17
+ modelValue: {},
18
+ items: {},
19
+ closable: {
20
+ type: Boolean,
21
+ default: false
22
+ },
23
+ block: {
24
+ type: Boolean,
25
+ default: false
26
+ },
27
+ rounded: {
28
+ type: Boolean,
29
+ default: false
30
+ },
31
+ position: { default: "top" }
32
+ }, {
33
+ "modelValue": {},
34
+ "modelModifiers": {}
35
+ }),
36
+ emits: /* @__PURE__ */ mergeModels([
37
+ "update:modelValue",
38
+ "click",
39
+ "close"
40
+ ], ["update:modelValue"]),
41
+ setup(__props, { emit: __emit }) {
42
+ const props = __props;
43
+ const emit = __emit;
44
+ const { size } = useFallbackProps([props], { size: "default" });
45
+ const cls = bem("tabs-bar");
46
+ const model = useModel(__props, "modelValue");
47
+ const viewportRef = shallowRef();
48
+ const listRef = shallowRef();
49
+ const { showNav, canPrev, canNext, scrollByStep } = useTabsBar({
50
+ viewportRef,
51
+ listRef,
52
+ items: computed(() => props.items),
53
+ model
54
+ });
55
+ const isItemClosable = (item) => {
56
+ if (item.disabled) return false;
57
+ return item.closable ?? props.closable;
58
+ };
59
+ const handleClick = (item, index) => {
60
+ if (item.disabled) return;
61
+ model.value = item.key;
62
+ emit("click", item, index);
63
+ };
64
+ const handleClose = (item, index) => {
65
+ if (item.disabled) return;
66
+ emit("close", item, index);
67
+ };
68
+ return (_ctx, _cache) => {
69
+ return openBlock(), createElementBlock("div", { class: normalizeClass([
70
+ unref(cls).b,
71
+ unref(cls).m("horizontal"),
72
+ unref(cls).m(__props.position),
73
+ unref(cls).m(unref(size)),
74
+ unref(bem).is("block", __props.block),
75
+ unref(bem).is("rounded", __props.rounded)
76
+ ]) }, [
77
+ withDirectives(createElementVNode("button", {
78
+ type: "button",
79
+ class: normalizeClass([unref(cls).e("nav"), unref(cls).em("nav", "prev")]),
80
+ disabled: !unref(canPrev),
81
+ onClick: _cache[0] || (_cache[0] = ($event) => unref(scrollByStep)(-1))
82
+ }, [createVNode(unref(_sfc_main$1), null, {
83
+ default: withCtx(() => [createVNode(unref(ArrowLeft))]),
84
+ _: 1
85
+ })], 10, _hoisted_1), [[vShow, unref(showNav)]]),
86
+ createElementVNode("div", {
87
+ ref_key: "viewportRef",
88
+ ref: viewportRef,
89
+ class: normalizeClass(unref(cls).e("viewport"))
90
+ }, [createElementVNode("div", {
91
+ ref_key: "listRef",
92
+ ref: listRef,
93
+ class: normalizeClass(unref(cls).e("list"))
94
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item, index) => {
95
+ return openBlock(), createElementBlock("button", {
96
+ type: "button",
97
+ role: "tab",
98
+ key: item.key,
99
+ class: normalizeClass([
100
+ unref(cls).e("item"),
101
+ unref(bem).is("active", model.value === item.key),
102
+ unref(bem).is("disabled", item.disabled === true)
103
+ ]),
104
+ onClick: withModifiers(($event) => handleClick(item, index), ["stop"])
105
+ }, [renderSlot(_ctx.$slots, `name:${item.key}`, { item }, () => [createElementVNode("span", { class: normalizeClass(unref(cls).e("item-label")) }, toDisplayString(item.name ?? item.key), 3)]), isItemClosable(item) ? (openBlock(), createElementBlock("span", {
106
+ key: 0,
107
+ role: "button",
108
+ tabindex: "0",
109
+ class: normalizeClass(unref(cls).e("close")),
110
+ "aria-label": `close ${item.name ?? item.key}`,
111
+ onClick: withModifiers(($event) => handleClose(item, index), ["stop"])
112
+ }, [createVNode(unref(_sfc_main$1), null, {
113
+ default: withCtx(() => [createVNode(unref(Close))]),
114
+ _: 1
115
+ })], 10, _hoisted_3)) : createCommentVNode("v-if", true)], 10, _hoisted_2);
116
+ }), 128))], 2)], 2),
117
+ withDirectives(createElementVNode("button", {
118
+ type: "button",
119
+ class: normalizeClass([unref(cls).e("nav"), unref(cls).em("nav", "next")]),
120
+ disabled: !unref(canNext),
121
+ onClick: _cache[1] || (_cache[1] = ($event) => unref(scrollByStep)(1))
122
+ }, [createVNode(unref(_sfc_main$1), null, {
123
+ default: withCtx(() => [createVNode(unref(ArrowRight))]),
124
+ _: 1
125
+ })], 10, _hoisted_4), [[vShow, unref(showNav)]])
126
+ ], 2);
127
+ };
128
+ }
129
+ });
130
+ //#endregion
131
+ export { _sfc_main as default };
132
+
133
+ //# sourceMappingURL=tabs-horizontal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs-horizontal.js","names":[],"sources":["../../../src/components/tabs/tabs-horizontal.vue"],"sourcesContent":["<template>\n <div\n :class=\"[\n cls.b,\n cls.m('horizontal'),\n cls.m(position),\n cls.m(size),\n bem.is('block', block),\n bem.is('rounded', rounded)\n ]\"\n >\n <button\n v-show=\"showNav\"\n type=\"button\"\n :class=\"[cls.e('nav'), cls.em('nav', 'prev')]\"\n :disabled=\"!canPrev\"\n @click=\"scrollByStep(-1)\"\n >\n <u-icon> <ArrowLeft /> </u-icon>\n </button>\n\n <div ref=\"viewportRef\" :class=\"cls.e('viewport')\">\n <div ref=\"listRef\" :class=\"cls.e('list')\">\n <button\n type=\"button\"\n role=\"tab\"\n v-for=\"(item, index) in items\"\n :key=\"item.key\"\n :class=\"[\n cls.e('item'),\n bem.is('active', model === item.key),\n bem.is('disabled', item.disabled === true)\n ]\"\n @click.stop=\"handleClick(item, index)\"\n >\n <slot :name=\"`name:${item.key}`\" :item=\"item\">\n <span :class=\"cls.e('item-label')\">{{ item.name ?? item.key }}</span>\n </slot>\n\n <span\n v-if=\"isItemClosable(item)\"\n role=\"button\"\n tabindex=\"0\"\n :class=\"cls.e('close')\"\n :aria-label=\"`close ${item.name ?? item.key}`\"\n @click.stop=\"handleClose(item, index)\"\n >\n <u-icon>\n <Close />\n </u-icon>\n </span>\n </button>\n </div>\n </div>\n\n <button\n v-show=\"showNav\"\n type=\"button\"\n :class=\"[cls.e('nav'), cls.em('nav', 'next')]\"\n :disabled=\"!canNext\"\n @click=\"scrollByStep(1)\"\n >\n <u-icon> <ArrowRight /> </u-icon>\n </button>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps } from '@veltra/compositions'\nimport { ArrowLeft, ArrowRight, Close } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, shallowRef } from 'vue'\n\nimport type { ComponentSize, TabItem, TabsHorizontalEmits, TabsHorizontalProps } from '../../types'\nimport { UIcon } from '../icon'\nimport { useTabsBar } from './use-tabs-bar'\n\ndefineOptions({ name: 'TabsHorizontal' })\n\nconst props = withDefaults(defineProps<TabsHorizontalProps>(), {\n position: 'top',\n closable: false,\n block: false,\n rounded: false\n})\n\nconst emit = defineEmits<TabsHorizontalEmits>()\n\ndefineSlots<{\n [key: `name:${string}`]: (props: { item: TabItem }) => any\n}>()\n\nconst { size } = useFallbackProps([props], {\n size: 'default' as ComponentSize\n})\n\nconst cls = bem('tabs-bar')\n\nconst model = defineModel<string>()\n\nconst viewportRef = shallowRef<HTMLElement>()\nconst listRef = shallowRef<HTMLElement>()\n\nconst itemsRef = computed(() => props.items)\n\nconst { showNav, canPrev, canNext, scrollByStep } = useTabsBar({\n viewportRef,\n listRef,\n items: itemsRef,\n model\n})\n\nconst isItemClosable = (item: TabItem) => {\n if (item.disabled) return false\n return item.closable ?? props.closable\n}\n\nconst handleClick = (item: TabItem, index: number) => {\n if (item.disabled) return\n model.value = item.key\n emit('click', item, index)\n}\n\nconst handleClose = (item: TabItem, index: number) => {\n if (item.disabled) return\n emit('close', item, index)\n}\n</script>\n"],"mappings":"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"}