@veltra/desktop 1.1.31 → 1.1.33

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 (80) hide show
  1. package/dist/components/action/action-group.vue.d.ts +1 -1
  2. package/dist/components/action/action.vue.d.ts +1 -1
  3. package/dist/components/cascade/cascade.vue.d.ts +1 -1
  4. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  5. package/dist/components/contextmenu/api.d.ts +8 -0
  6. package/dist/components/{context-menu → contextmenu}/api.js +2 -2
  7. package/dist/components/contextmenu/api.js.map +1 -0
  8. package/dist/components/contextmenu/contextmenu-item.js +64 -0
  9. package/dist/components/contextmenu/contextmenu-item.js.map +1 -0
  10. package/dist/components/contextmenu/contextmenu-panel.js +33 -0
  11. package/dist/components/contextmenu/contextmenu-panel.js.map +1 -0
  12. package/dist/components/contextmenu/contextmenu-sub.js +83 -0
  13. package/dist/components/contextmenu/contextmenu-sub.js.map +1 -0
  14. package/dist/components/contextmenu/contextmenu.js +79 -0
  15. package/dist/components/contextmenu/contextmenu.js.map +1 -0
  16. package/dist/components/{context-menu/context-menu.vue.d.ts → contextmenu/contextmenu.vue.d.ts} +5 -5
  17. package/dist/components/contextmenu/di.js +7 -0
  18. package/dist/components/contextmenu/di.js.map +1 -0
  19. package/dist/components/contextmenu/helper.js +8 -0
  20. package/dist/components/contextmenu/helper.js.map +1 -0
  21. package/dist/components/contextmenu/index.d.ts +2 -0
  22. package/dist/components/contextmenu/style2.css +154 -0
  23. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  24. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  25. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  26. package/dist/components/file-viewer/previewers/sheet-previewer.js +33 -93
  27. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -1
  28. package/dist/components/file-viewer/sheet-parser.js +46 -0
  29. package/dist/components/file-viewer/sheet-parser.js.map +1 -0
  30. package/dist/components/index.d.ts +2 -2
  31. package/dist/components/index.js +3 -3
  32. package/dist/components/index.js.map +1 -1
  33. package/dist/components/number/number.vue.d.ts +1 -1
  34. package/dist/components/table/node/col.d.ts +5 -0
  35. package/dist/components/table/node/col.js +6 -0
  36. package/dist/components/table/node/col.js.map +1 -1
  37. package/dist/components/table/use-col-resize.js +10 -10
  38. package/dist/components/table/use-col-resize.js.map +1 -1
  39. package/dist/components/tip/tip.vue.d.ts +1 -1
  40. package/dist/index.d.ts +5 -5
  41. package/dist/index.js +3 -3
  42. package/dist/style.js +1 -1
  43. package/dist/types/{context-menu.d.ts → contextmenu.d.ts} +12 -10
  44. package/dist/types/index.d.ts +2 -2
  45. package/package.json +6 -6
  46. package/src/components/{context-menu → contextmenu}/api.ts +4 -4
  47. package/src/components/contextmenu/contextmenu-item.vue +63 -0
  48. package/src/components/contextmenu/contextmenu-panel.vue +28 -0
  49. package/src/components/contextmenu/contextmenu-sub.vue +88 -0
  50. package/src/components/contextmenu/contextmenu.vue +77 -0
  51. package/src/components/contextmenu/di.ts +13 -0
  52. package/src/components/contextmenu/helper.ts +5 -0
  53. package/src/components/contextmenu/index.ts +3 -0
  54. package/src/components/contextmenu/style.scss +135 -0
  55. package/src/components/file-viewer/previewers/sheet-previewer.vue +35 -131
  56. package/src/components/file-viewer/sheet-parser.ts +52 -0
  57. package/src/components/index.ts +1 -1
  58. package/src/components/table/node/col.ts +7 -0
  59. package/src/components/table/use-col-resize.ts +18 -16
  60. package/src/style.ts +1 -1
  61. package/src/types/{context-menu.ts → contextmenu.ts} +9 -7
  62. package/src/types/index.ts +1 -1
  63. package/dist/components/context-menu/api.d.ts +0 -8
  64. package/dist/components/context-menu/api.js.map +0 -1
  65. package/dist/components/context-menu/context-menu-item.js +0 -60
  66. package/dist/components/context-menu/context-menu-item.js.map +0 -1
  67. package/dist/components/context-menu/context-menu.js +0 -95
  68. package/dist/components/context-menu/context-menu.js.map +0 -1
  69. package/dist/components/context-menu/di.js +0 -6
  70. package/dist/components/context-menu/di.js.map +0 -1
  71. package/dist/components/context-menu/index.d.ts +0 -2
  72. package/dist/components/context-menu/style2.css +0 -52
  73. package/src/components/context-menu/context-menu-item.vue +0 -67
  74. package/src/components/context-menu/context-menu.vue +0 -119
  75. package/src/components/context-menu/di.ts +0 -5
  76. package/src/components/context-menu/index.ts +0 -3
  77. package/src/components/context-menu/style.scss +0 -51
  78. /package/dist/components/{context-menu → contextmenu}/style.d.ts +0 -0
  79. /package/dist/components/{context-menu → contextmenu}/style.js +0 -0
  80. /package/src/components/{context-menu → contextmenu}/style.ts +0 -0
@@ -20,9 +20,11 @@
20
20
 
21
21
  <script lang="ts" setup>
22
22
  import { bem } from '@veltra/utils'
23
+ import { ListTable } from '@visactor/vtable'
23
24
  import { onBeforeUnmount, ref, shallowRef, useTemplateRef, watch } from 'vue'
24
25
 
25
26
  import type { FileViewerItem } from '../../../types/file-viewer'
27
+ import { parseSheetFile, type SheetPreview } from '../sheet-parser'
26
28
 
27
29
  defineOptions({ name: 'FileViewerSheetPreviewer' })
28
30
 
@@ -33,73 +35,53 @@ const props = withDefaults(defineProps<{ file: FileViewerItem; maxRows?: number
33
35
  const emit = defineEmits<{ (e: 'error', err: unknown): void }>()
34
36
 
35
37
  const cls = bem('file-viewer')
36
-
37
38
  const container = useTemplateRef<HTMLDivElement>('container')
38
39
  const loading = ref(true)
39
40
  const truncated = ref(false)
40
41
  const totalRows = ref(0)
41
42
  const renderedRows = ref(0)
42
-
43
- interface SheetData {
44
- name: string
45
- headers: string[]
46
- rows: string[][]
47
- totalCount: number
48
- }
49
-
50
- const sheets = shallowRef<SheetData[]>([])
43
+ const sheets = shallowRef<SheetPreview[]>([])
51
44
  const activeSheetIndex = ref(0)
52
45
 
53
46
  let controller: AbortController | undefined
54
- let instance: { release?: () => void; resize?: () => void } | undefined
47
+ let table: ListTable | undefined
55
48
  let resizeObserver: ResizeObserver | undefined
56
49
 
50
+ function releaseTable() {
51
+ table?.release()
52
+ table = undefined
53
+ }
54
+
55
+ function renderActive() {
56
+ const sheet = sheets.value[activeSheetIndex.value]
57
+ const el = container.value
58
+ if (!sheet || !el) return
59
+
60
+ releaseTable()
61
+
62
+ const limit = props.maxRows > 0 ? Math.min(props.maxRows, sheet.rows.length) : sheet.rows.length
63
+ totalRows.value = sheet.rows.length
64
+ renderedRows.value = limit
65
+ truncated.value = props.maxRows > 0 && sheet.rows.length > props.maxRows
66
+
67
+ table = new ListTable(el, {
68
+ records: sheet.rows.slice(0, limit),
69
+ columns: sheet.headers.map((title, i) => ({ field: String(i), title, width: 'auto' })),
70
+ widthMode: 'standard'
71
+ })
72
+ }
73
+
57
74
  async function load() {
58
75
  controller?.abort()
59
76
  controller = new AbortController()
60
- const signal = controller.signal
61
77
  loading.value = true
62
78
  sheets.value = []
63
79
  activeSheetIndex.value = 0
64
80
 
65
81
  try {
66
- const ext = props.file.name.slice(props.file.name.lastIndexOf('.') + 1).toLowerCase()
67
- const { toArrayBuffer, parseCsv } = await import('../helper')
68
- const buf = await toArrayBuffer(props.file.src, signal)
69
- if (signal.aborted) return
70
-
71
- if (ext === 'csv') {
72
- const text = new TextDecoder('utf-8', { fatal: false }).decode(buf)
73
- const parsed = parseCsv(text).filter((row) => row.some((c) => c.length > 0))
74
- const headers = parsed[0] ?? []
75
- const rows = parsed.slice(1)
76
- sheets.value = [
77
- {
78
- name: props.file.name,
79
- headers: headers.map((h, i) => String(h) || `Column ${i + 1}`),
80
- rows,
81
- totalCount: rows.length
82
- }
83
- ]
84
- } else {
85
- const { readWorkbook } = await import('@cat-kit/excel')
86
- const workbook = await readWorkbook(new Uint8Array(buf))
87
- if (signal.aborted) return
88
- sheets.value = workbook.worksheets.map((ws) => {
89
- const wsRows = ws.getRows()
90
- const firstRow = wsRows[0]
91
- const headers = firstRow ? firstRow.toValues().map((v) => cellToString(v)) : []
92
- const rows = wsRows.slice(1).map((r) => r.toValues().map(cellToString))
93
- return {
94
- name: ws.name,
95
- headers: headers.map((h, i) => h || `Column ${i + 1}`),
96
- rows,
97
- totalCount: rows.length
98
- }
99
- })
100
- }
101
-
102
- await renderActive()
82
+ sheets.value = await parseSheetFile(props.file, controller.signal)
83
+ if (controller.signal.aborted) return
84
+ renderActive()
103
85
  } catch (err) {
104
86
  if ((err as { name?: string })?.name === 'AbortError') return
105
87
  emit('error', err)
@@ -108,97 +90,19 @@ async function load() {
108
90
  }
109
91
  }
110
92
 
111
- function cellToString(v: unknown): string {
112
- if (v === null || v === undefined) return ''
113
- if (typeof v === 'string') return v
114
- if (typeof v === 'number' || typeof v === 'boolean') return String(v)
115
- if (v instanceof Date) return v.toLocaleString()
116
- if (typeof v === 'object' && v !== null && 'formula' in v) {
117
- const f = v as { formula: string; result?: unknown }
118
- if (f.result !== undefined && f.result !== null) return cellToString(f.result)
119
- return `=${f.formula}`
120
- }
121
- return String(v)
122
- }
123
-
124
- async function renderActive() {
125
- const sheet = sheets.value[activeSheetIndex.value]
126
- if (!sheet || !container.value) return
127
-
128
- releaseInstance()
129
-
130
- const limit = props.maxRows > 0 ? Math.min(props.maxRows, sheet.rows.length) : sheet.rows.length
131
- totalRows.value = sheet.totalCount
132
- renderedRows.value = limit
133
- truncated.value = props.maxRows > 0 && sheet.rows.length > props.maxRows
134
-
135
- const columns = sheet.headers.map((h, i) => ({
136
- field: 'c' + i,
137
- title: h,
138
- width: 'auto' as const
139
- }))
140
-
141
- const records: Record<string, string>[] = []
142
- for (let i = 0; i < limit; i++) {
143
- const row = sheet.rows[i]
144
- if (!row) continue
145
- const record: Record<string, string> = {}
146
- for (let j = 0; j < columns.length; j++) {
147
- record['c' + j] = row[j] ?? ''
148
- }
149
- records.push(record)
150
- }
151
-
152
- const VTable = await import('@visactor/vtable')
153
- if (!container.value) return
154
-
155
- instance = new VTable.ListTable(container.value, {
156
- records,
157
- columns,
158
- widthMode: 'standard',
159
- heightMode: 'standard',
160
- defaultRowHeight: 32,
161
- defaultHeaderRowHeight: 36,
162
- autoFillWidth: true,
163
- frozenColCount: 0,
164
- theme: (VTable.themes.ARCO ?? VTable.themes.DEFAULT).extends({
165
- defaultStyle: { borderLineWidth: 1 }
166
- })
167
- })
168
- }
169
-
170
- function releaseInstance() {
171
- if (instance && typeof instance.release === 'function') {
172
- try {
173
- instance.release()
174
- } catch {
175
- /* noop */
176
- }
177
- }
178
- instance = undefined
179
- }
180
-
181
- watch(
182
- () => props.file,
183
- () => load(),
184
- { immediate: true }
185
- )
186
-
187
- watch(activeSheetIndex, () => renderActive())
188
-
93
+ watch(() => props.file, load, { immediate: true })
94
+ watch(activeSheetIndex, renderActive)
189
95
  watch(container, (el) => {
190
96
  resizeObserver?.disconnect()
191
97
  if (!el) return
192
- resizeObserver = new ResizeObserver(() => {
193
- instance?.resize?.()
194
- })
98
+ resizeObserver = new ResizeObserver(() => table?.resize?.())
195
99
  resizeObserver.observe(el)
196
100
  })
197
101
 
198
102
  onBeforeUnmount(() => {
199
103
  controller?.abort()
200
104
  resizeObserver?.disconnect()
201
- releaseInstance()
105
+ releaseTable()
202
106
  sheets.value = []
203
107
  })
204
108
  </script>
@@ -0,0 +1,52 @@
1
+ import { readWorkbook } from '@cat-kit/excel'
2
+
3
+ import type { FileViewerItem } from '../../types/file-viewer'
4
+ import { getExtension, parseCsv, toArrayBuffer } from './helper'
5
+
6
+ export interface SheetPreview {
7
+ name: string
8
+ headers: string[]
9
+ rows: string[][]
10
+ }
11
+
12
+ function cellToString(v: unknown): string {
13
+ if (v == null) return ''
14
+ if (typeof v === 'string') return v
15
+ if (typeof v === 'number' || typeof v === 'boolean') return String(v)
16
+ if (v instanceof Date) return v.toLocaleString()
17
+ if (typeof v === 'object' && 'formula' in v) {
18
+ const f = v as { formula: string; result?: unknown }
19
+ return f.result != null ? cellToString(f.result) : `=${f.formula}`
20
+ }
21
+ return JSON.stringify(v)
22
+ }
23
+
24
+ /** 解析 CSV / Excel 为表格预览数据 */
25
+ export async function parseSheetFile(
26
+ file: FileViewerItem,
27
+ signal?: AbortSignal
28
+ ): Promise<SheetPreview[]> {
29
+ const ext = getExtension(file.name)
30
+ const buf = await toArrayBuffer(file.src, signal)
31
+ if (signal?.aborted) return []
32
+
33
+ if (ext === 'csv') {
34
+ const text = new TextDecoder('utf-8', { fatal: false }).decode(buf)
35
+ const parsed = parseCsv(text).filter((row) => row.some((c) => c.length > 0))
36
+ const headers = (parsed[0] ?? []).map((h, i) => String(h) || `Column ${i + 1}`)
37
+ return [{ name: file.name, headers, rows: parsed.slice(1) }]
38
+ }
39
+
40
+ const workbook = await readWorkbook(new Uint8Array(buf))
41
+ if (signal?.aborted) return []
42
+
43
+ return workbook.worksheets.map((ws) => {
44
+ const wsRows = ws.getRows()
45
+ const firstRow = wsRows[0]
46
+ const headers = (firstRow ? firstRow.toValues().map(cellToString) : []).map(
47
+ (h, i) => h || `Column ${i + 1}`
48
+ )
49
+ const rows = wsRows.slice(1).map((r) => r.toValues().map(cellToString))
50
+ return { name: ws.name, headers, rows }
51
+ })
52
+ }
@@ -66,7 +66,7 @@ export * from './table-editor'
66
66
 
67
67
  export * from './check-tag'
68
68
 
69
- export * from './context-menu'
69
+ export * from './contextmenu'
70
70
 
71
71
  export * from './password-input'
72
72
 
@@ -94,9 +94,16 @@ export class ColumnNode extends TreeNode<TableColumn, ColumnNode> {
94
94
  return this.data.resizable ?? true
95
95
  }
96
96
 
97
+ /**
98
+ * 是否在列配置上显式指定了 width,或经表头拖拽锁定。
99
+ * 此类列不参与剩余宽度的均分。
100
+ */
101
+ explicitWidth: boolean
102
+
97
103
  style: Record<string, number> = reactive({})
98
104
 
99
105
  constructor(val: TableColumn, index: number, depth: number, parent?: ColumnNode) {
100
106
  super(isReactive(val) ? val : shallowReactive(val), index, depth, parent)
107
+ this.explicitWidth = val.width !== undefined
101
108
  }
102
109
  }
@@ -34,22 +34,23 @@ export function useColResize(options: Options): UseColResizeReturned {
34
34
 
35
35
  if (!containerWidth || !leafColumns.value.length) return
36
36
 
37
- // 如果每个列都设置了宽度, 有两种情况
38
- // 1. 表格容器宽度大于所有列宽度之和,则将剩下的宽度均匀分布到所有列中
39
- // 2. 表格容器宽度小于所有列宽度之和,则什么也不管
40
- const allColumnsWidth = leafColumns.value.reduce((acc, cur) => {
41
- return acc + (cur.width ?? cur.minWidth!)
42
- }, 0)
43
-
44
- if (allColumnsWidth < containerWidth) {
45
- const freeWidth = containerWidth - allColumnsWidth
46
-
47
- const allocatableColumns = leafColumns.value.filter((column) => column.resizable !== false)
48
-
49
- const allocatedWidth = freeWidth / allocatableColumns.length
50
-
51
- allocatableColumns.forEach((column) => {
52
- column.width = (column.width ?? column.minWidth!) + allocatedWidth
37
+ const flexColumns = leafColumns.value.filter((column) => !column.explicitWidth)
38
+ const fixedWidthTotal = leafColumns.value
39
+ .filter((column) => column.explicitWidth)
40
+ .reduce((acc, cur) => acc + (cur.width ?? cur.minWidth!), 0)
41
+ const flexBaseTotal = flexColumns.reduce((acc, cur) => acc + cur.minWidth!, 0)
42
+ const totalBase = fixedWidthTotal + flexBaseTotal
43
+
44
+ // 仅未显式指定 width 的列参与剩余宽度均分;指定了 width 的列保持该宽度。
45
+ if (flexColumns.length > 0 && totalBase < containerWidth) {
46
+ const allocatedWidth = (containerWidth - totalBase) / flexColumns.length
47
+
48
+ flexColumns.forEach((column) => {
49
+ column.width = column.minWidth! + allocatedWidth
50
+ })
51
+ } else {
52
+ flexColumns.forEach((column) => {
53
+ column.data.width = undefined
53
54
  })
54
55
  }
55
56
 
@@ -110,6 +111,7 @@ export function useColResize(options: Options): UseColResizeReturned {
110
111
  originWidth + e.pageX - originX,
111
112
  currentResizeColumn!.minWidth!
112
113
  )
114
+ currentResizeColumn!.explicitWidth = true
113
115
 
114
116
  showResizeLine.value = false
115
117
  currentResizeColumn = null
package/src/style.ts CHANGED
@@ -13,7 +13,7 @@ import './components/checkbox/style'
13
13
  import './components/code-editor/style'
14
14
  import './components/collapse/style'
15
15
  import './components/condition-editor/style'
16
- import './components/context-menu/style'
16
+ import './components/contextmenu/style'
17
17
  import './components/date-panel/style'
18
18
  import './components/date-picker/style'
19
19
  import './components/date-range-picker/style'
@@ -4,21 +4,23 @@ import type { Component } from 'vue'
4
4
  /**
5
5
  * 右键菜单项
6
6
  */
7
- export interface ContextMenuItem {
7
+ export interface ContextmenuItem {
8
8
  /** 菜单名称 */
9
9
  label: string
10
10
  /** 菜单描述 */
11
11
  description?: string
12
12
  /** 菜单图标 */
13
13
  icon?: Component
14
+ /** 子菜单 */
15
+ children?: ContextmenuItem[]
14
16
  /** 菜单点击时的回调 */
15
- callback?: () => void | Promise<void>
17
+ callback?: () => any
16
18
  /** 是否禁用 */
17
19
  disabled?: boolean | (() => boolean)
18
20
  }
19
21
 
20
22
  /** 鼠标右键菜单组件属性 */
21
- export interface ContextMenuProps extends ComponentProps {
23
+ export interface ContextmenuProps extends ComponentProps {
22
24
  /** 鼠标位置 */
23
25
  mousePosition: { x: number; y: number }
24
26
  /**
@@ -27,16 +29,16 @@ export interface ContextMenuProps extends ComponentProps {
27
29
  */
28
30
  width?: number | string
29
31
  /** 菜单项 */
30
- menus: ContextMenuItem[] | (() => ContextMenuItem[])
32
+ menus: ContextmenuItem[] | (() => ContextmenuItem[])
31
33
  }
32
34
 
33
35
  /** 鼠标右键菜单组件定义的事件 */
34
- export interface ContextMenuEmits {
36
+ export interface ContextmenuEmits {
35
37
  (e: 'destroy'): void
36
38
  }
37
39
 
38
40
  /** 鼠标右键菜单组件暴露的属性和方法(组件内部使用) */
39
- export interface _ContextMenuExposed {}
41
+ export interface _ContextmenuExposed {}
40
42
 
41
43
  /** 鼠标右键菜单组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
42
- export type ContextMenuExposed = DeconstructValue<_ContextMenuExposed>
44
+ export type ContextmenuExposed = DeconstructValue<_ContextmenuExposed>
@@ -51,7 +51,7 @@ export * from './menu'
51
51
  export * from './password-input'
52
52
  export * from './date-picker'
53
53
  export * from './dropdown'
54
- export * from './context-menu'
54
+ export * from './contextmenu'
55
55
  export * from './select'
56
56
  export * from './radio'
57
57
  export * from './checkbox'
@@ -1,8 +0,0 @@
1
- import { ContextMenuProps } from "../../types/context-menu.js";
2
- //#region src/components/context-menu/api.d.ts
3
- declare const contextmenu: {
4
- pop(options: ContextMenuProps): void;
5
- };
6
- //#endregion
7
- export { contextmenu };
8
- //# sourceMappingURL=api.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.js","names":["MContextMenu"],"sources":["../../../src/components/context-menu/api.ts"],"sourcesContent":["import { createVNode, render, type VNode } from 'vue'\n\nimport type { ContextMenuProps } from '../../types'\nimport MContextMenu from './context-menu.vue'\n\nlet node: VNode\n\nexport const contextmenu = {\n pop(options: ContextMenuProps): void {\n if (node) {\n render(null, document.body)\n }\n\n node = createVNode(MContextMenu, {\n ...options,\n onDestroy() {\n render(null, document.body)\n }\n })\n\n render(node, document.body)\n }\n}\n"],"mappings":";;;AAKA,IAAI;AAEJ,MAAa,cAAc,EACzB,IAAI,SAAiC;CACnC,IAAI,MACF,OAAO,MAAM,SAAS,IAAI;CAG5B,OAAO,YAAYA,WAAc;EAC/B,GAAG;EACH,YAAY;GACV,OAAO,MAAM,SAAS,IAAI;EAC5B;CACF,CAAC;CAED,OAAO,MAAM,SAAS,IAAI;AAC5B,EACF"}
@@ -1,60 +0,0 @@
1
- import _sfc_main$1 from "../icon/icon.js";
2
- import { ContextMenuDIKey } from "./di.js";
3
- import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createVNode, defineComponent, inject, normalizeClass, openBlock, resolveDynamicComponent, shallowRef, toDisplayString, unref, withCtx, withDirectives } from "vue";
4
- import { bem } from "@veltra/utils";
5
- import { Loading } from "@veltra/icons/normal";
6
- import { vRipple } from "@veltra/directives";
7
- //#region src/components/context-menu/context-menu-item.vue
8
- const _sfc_main = /* @__PURE__ */ defineComponent({
9
- name: "ContextMenuItem",
10
- __name: "context-menu-item",
11
- props: { menu: {} },
12
- emits: ["click-start", "click-end"],
13
- setup(__props, { emit: __emit }) {
14
- const emit = __emit;
15
- const { cls } = inject(ContextMenuDIKey);
16
- const loading = shallowRef(false);
17
- const disabled = computed(() => {
18
- return (typeof __props.menu.disabled === "function" ? __props.menu.disabled() : __props.menu.disabled) ?? false;
19
- });
20
- function handleClickMenu() {
21
- if (disabled.value || loading.value) return;
22
- emit("click-start");
23
- loading.value = true;
24
- const result = __props.menu.callback?.();
25
- if (result instanceof Promise) return result.then(() => {}).finally(() => {
26
- emit("click-end");
27
- loading.value = false;
28
- });
29
- loading.value = false;
30
- emit("click-end");
31
- }
32
- return (_ctx, _cache) => {
33
- return withDirectives((openBlock(), createElementBlock("li", {
34
- class: normalizeClass([
35
- unref(cls).e("item"),
36
- unref(bem).is("disabled", disabled.value),
37
- unref(bem).is("loading", loading.value),
38
- unref(cls).em("item", "menu")
39
- ]),
40
- onClick: handleClickMenu
41
- }, [loading.value ? (openBlock(), createBlock(unref(_sfc_main$1), {
42
- key: 0,
43
- class: normalizeClass(unref(bem).is("loading"))
44
- }, {
45
- default: withCtx(() => [createVNode(unref(Loading))]),
46
- _: 1
47
- }, 8, ["class"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [__props.menu.icon ? (openBlock(), createBlock(unref(_sfc_main$1), { key: 0 }, {
48
- default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(__props.menu.icon)))]),
49
- _: 1
50
- })) : (openBlock(), createElementBlock("i", {
51
- key: 1,
52
- class: normalizeClass(unref(cls).e("icon-place"))
53
- }, null, 2))], 64)), createElementVNode("span", { class: normalizeClass(unref(cls).e("label")) }, toDisplayString(__props.menu.label), 3)], 2)), [[unref(vRipple), !disabled.value && !loading.value]]);
54
- };
55
- }
56
- });
57
- //#endregion
58
- export { _sfc_main as default };
59
-
60
- //# sourceMappingURL=context-menu-item.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-menu-item.js","names":[],"sources":["../../../src/components/context-menu/context-menu-item.vue"],"sourcesContent":["<template>\n <li\n :class=\"[\n cls.e('item'),\n bem.is('disabled', disabled),\n bem.is('loading', loading),\n cls.em('item', 'menu')\n ]\"\n v-ripple=\"!disabled && !loading\"\n @click=\"handleClickMenu\"\n >\n <u-icon v-if=\"loading\" :class=\"bem.is('loading')\">\n <Loading />\n </u-icon>\n <template v-else>\n <u-icon v-if=\"menu.icon\">\n <component :is=\"menu.icon\" />\n </u-icon>\n <i v-else :class=\"cls.e('icon-place')\"></i>\n </template>\n\n <span :class=\"cls.e('label')\">{{ menu.label }}</span>\n </li>\n</template>\n\n<script lang=\"ts\" setup>\nimport { vRipple } from '@veltra/directives'\nimport { Loading } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, shallowRef } from 'vue'\n\nimport type { ContextMenuItem } from '../../types'\nimport { UIcon } from '../icon'\nimport { ContextMenuDIKey } from './di'\n\ndefineOptions({ name: 'ContextMenuItem' })\n\nconst { menu } = defineProps<{ menu: ContextMenuItem }>()\n\nconst emit = defineEmits<{ (e: 'click-start'): void; (e: 'click-end'): void }>()\n\nconst { cls } = inject(ContextMenuDIKey)!\n\nconst loading = shallowRef(false)\n\nconst disabled = computed(() => {\n return (typeof menu.disabled === 'function' ? menu.disabled() : menu.disabled) ?? false\n})\n\nfunction handleClickMenu() {\n if (disabled.value || loading.value) return\n emit('click-start')\n loading.value = true\n const result = menu.callback?.()\n\n if (result instanceof Promise) {\n return result\n .then(() => {})\n .finally(() => {\n emit('click-end')\n loading.value = false\n })\n }\n loading.value = false\n emit('click-end')\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;EAuCA,MAAM,OAAO;EAEb,MAAM,EAAE,QAAQ,OAAO,gBAAgB;EAEvC,MAAM,UAAU,WAAW,KAAK;EAEhC,MAAM,WAAW,eAAe;GAC9B,QAAQ,OAAO,QAAA,KAAK,aAAa,aAAa,QAAA,KAAK,SAAS,IAAI,QAAA,KAAK,aAAa;EACpF,CAAC;EAED,SAAS,kBAAkB;GACzB,IAAI,SAAS,SAAS,QAAQ,OAAO;GACrC,KAAK,aAAa;GAClB,QAAQ,QAAQ;GAChB,MAAM,SAAS,QAAA,KAAK,WAAW;GAE/B,IAAI,kBAAkB,SACpB,OAAO,OACJ,WAAW,CAAC,CAAC,EACb,cAAc;IACb,KAAK,WAAW;IAChB,QAAQ,QAAQ;GAClB,CAAC;GAEL,QAAQ,QAAQ;GAChB,KAAK,WAAW;EAClB;;uCAhEE,mBAqBK,MAAA;IApBF,OAAK,eAAA;KAAU,MAAA,GAAA,EAAI,EAAC,MAAA;KAAgB,MAAA,GAAA,EAAI,GAAE,YAAa,SAAA,KAAQ;KAAS,MAAA,GAAA,EAAI,GAAE,WAAY,QAAA,KAAO;KAAS,MAAA,GAAA,EAAI,GAAE,QAAA,MAAA;;IAOhH,SAAO;OAEM,QAAA,SAAA,UAAA,GAAd,YAES,MAAA,WAAA,GAAA;;IAFe,OAAK,eAAE,MAAA,GAAA,EAAI,GAAE,SAAA,CAAA;;2BACxB,CAAX,YAAW,MAAA,OAAA,CAAA,CAAA,CAAA;;qCAEb,mBAKW,UAAA,EAAA,KAAA,EAAA,GAAA,CAJK,QAAA,KAAK,QAAA,UAAA,GAAnB,YAES,MAAA,WAAA,GAAA,EAAA,KAAA,EAAA,GAAA;2BADsB,EAAA,UAAA,GAA7B,YAA6B,wBAAb,QAAA,KAAK,IAAI,CAAA,EAAA,CAAA;;uBAE3B,mBAA2C,KAAA;;IAAhC,OAAK,eAAE,MAAA,GAAA,EAAI,EAAC,YAAA,CAAA;wBAGzB,mBAAqD,QAAA,EAA9C,OAAK,eAAE,MAAA,GAAA,EAAI,EAAC,OAAA,CAAA,EAAA,GAAA,gBAAc,QAAA,KAAK,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA,IAAA,CAAA,CAAA,MAAA,OAAA,GAAA,CAbhC,SAAA,SAAQ,CAAK,QAAA,KAAO,CAAA,CAAA"}
@@ -1,95 +0,0 @@
1
- import { ContextMenuDIKey } from "./di.js";
2
- import _sfc_main$1 from "./context-menu-item.js";
3
- import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, provide, renderList, shallowRef, unref, withCtx, withDirectives } from "vue";
4
- import { useFallbackProps } from "@veltra/compositions";
5
- import { bem, withUnit, zIndex } from "@veltra/utils";
6
- import { vClickOutside } from "@veltra/directives";
7
- //#region src/components/context-menu/context-menu.vue
8
- const _sfc_main = /* @__PURE__ */ defineComponent({
9
- name: "ContextMenu",
10
- __name: "context-menu",
11
- props: {
12
- mousePosition: {},
13
- width: { default: 150 },
14
- menus: {},
15
- size: {}
16
- },
17
- emits: ["destroy"],
18
- setup(__props, { emit: __emit }) {
19
- const props = __props;
20
- const emit = __emit;
21
- const { size } = useFallbackProps([props], { size: "default" });
22
- const cls = bem("context-menu");
23
- const contextMenuRef = shallowRef();
24
- const visible = shallowRef(true);
25
- const computePosition = () => {
26
- const position = {};
27
- const { mousePosition } = props;
28
- const { x, y } = mousePosition;
29
- if (x > window.innerWidth / 2) position.right = window.innerWidth - x - 1;
30
- else position.left = x + 1;
31
- if (y > window.innerHeight / 2) position.bottom = window.innerHeight - y - 1;
32
- else position.top = y + 1;
33
- position.transformOrigin = `${position.top ? "top" : "bottom"} ${position.left ? "left" : "right"}`;
34
- return Object.fromEntries(Object.entries(position).map(([k, v]) => [k, withUnit(v, "px")]));
35
- };
36
- const style = computed(() => {
37
- return {
38
- width: withUnit(props.width, "px"),
39
- ...computePosition(),
40
- zIndex: zIndex()
41
- };
42
- });
43
- const menus = computed(() => {
44
- const { menus } = props;
45
- if (typeof menus === "function") return menus();
46
- return menus;
47
- });
48
- const getMenuDisabled = (menu) => {
49
- return typeof menu.disabled === "function" ? menu.disabled() : menu.disabled;
50
- };
51
- function close() {
52
- visible.value = false;
53
- }
54
- let loading = false;
55
- function handleClickStart() {
56
- loading = true;
57
- }
58
- function handleClickEnd() {
59
- loading = false;
60
- close();
61
- }
62
- function handleClickOutside() {
63
- if (loading) return;
64
- close();
65
- }
66
- provide(ContextMenuDIKey, { cls });
67
- return (_ctx, _cache) => {
68
- return openBlock(), createBlock(Teleport, { to: "body" }, [createVNode(Transition, {
69
- name: "zoom-in",
70
- appear: "",
71
- onAfterLeave: _cache[0] || (_cache[0] = ($event) => emit("destroy"))
72
- }, {
73
- default: withCtx(() => [visible.value ? withDirectives((openBlock(), createElementBlock("ul", {
74
- key: 0,
75
- class: normalizeClass([unref(cls).b, unref(cls).m(unref(size))]),
76
- style: normalizeStyle(style.value),
77
- ref_key: "contextMenuRef",
78
- ref: contextMenuRef
79
- }, [(openBlock(true), createElementBlock(Fragment, null, renderList(menus.value, (menu) => {
80
- return openBlock(), createBlock(_sfc_main$1, {
81
- menu,
82
- disabled: getMenuDisabled(menu),
83
- onClickStart: handleClickStart,
84
- onClickEnd: handleClickEnd
85
- }, null, 8, ["menu", "disabled"]);
86
- }), 256))], 6)), [[unref(vClickOutside), handleClickOutside]]) : createCommentVNode("v-if", true)]),
87
- _: 1
88
- })]);
89
- };
90
- }
91
- });
92
- //#endregion
93
- export { _sfc_main as default };
94
-
95
- //# sourceMappingURL=context-menu.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-menu.js","names":[],"sources":["../../../src/components/context-menu/context-menu.vue"],"sourcesContent":["<template>\n <Teleport to=\"body\">\n <transition name=\"zoom-in\" appear @after-leave=\"emit('destroy')\">\n <ul\n :class=\"[cls.b, cls.m(size)]\"\n :style=\"style\"\n ref=\"contextMenuRef\"\n v-if=\"visible\"\n v-click-outside=\"handleClickOutside\"\n >\n <UContextMenuItem\n v-for=\"menu of menus\"\n :menu=\"menu\"\n :disabled=\"getMenuDisabled(menu)\"\n @click-start=\"handleClickStart\"\n @click-end=\"handleClickEnd\"\n />\n </ul>\n </transition>\n </Teleport>\n</template>\n\n<script lang=\"ts\" setup>\nimport { useFallbackProps } from '@veltra/compositions'\nimport { vClickOutside } from '@veltra/directives'\nimport { bem, withUnit, zIndex } from '@veltra/utils'\nimport { computed, provide, shallowRef, type CSSProperties } from 'vue'\n\nimport type {\n ContextMenuEmits,\n ContextMenuItem,\n ContextMenuProps,\n ComponentSize\n} from '../../types'\nimport UContextMenuItem from './context-menu-item.vue'\nimport { ContextMenuDIKey } from './di'\n\ndefineOptions({ name: 'ContextMenu' })\n\nconst props = withDefaults(defineProps<ContextMenuProps>(), { width: 150 })\n\nconst emit = defineEmits<ContextMenuEmits>()\n\nconst { size } = useFallbackProps([props], { size: 'default' as ComponentSize })\n\nconst cls = bem('context-menu')\n\nconst contextMenuRef = shallowRef<HTMLElement>()\n\nconst visible = shallowRef(true)\n\nconst computePosition = () => {\n const position: {\n top?: number\n left?: number\n right?: number\n bottom?: number\n transformOrigin?: string\n } = {}\n\n const { mousePosition } = props\n const { x, y } = mousePosition\n if (x > window.innerWidth / 2) {\n position.right = window.innerWidth - x - 1\n } else {\n position.left = x + 1\n }\n if (y > window.innerHeight / 2) {\n position.bottom = window.innerHeight - y - 1\n } else {\n position.top = y + 1\n }\n\n const positionY = position.top ? 'top' : 'bottom'\n const positionX = position.left ? 'left' : 'right'\n position.transformOrigin = `${positionY} ${positionX}`\n\n return Object.fromEntries(\n Object.entries(position).map(([k, v]) => [k, withUnit(v as number | string | undefined, 'px')])\n ) as CSSProperties\n}\n\nconst style = computed<CSSProperties>(() => {\n return { width: withUnit(props.width, 'px'), ...computePosition(), zIndex: zIndex() }\n})\n\nconst menus = computed(() => {\n const { menus } = props\n if (typeof menus === 'function') {\n return menus()\n }\n return menus\n})\n\nconst getMenuDisabled = (menu: ContextMenuItem) => {\n return typeof menu.disabled === 'function' ? menu.disabled() : menu.disabled\n}\n\nfunction close() {\n visible.value = false\n}\n\nlet loading = false\nfunction handleClickStart() {\n loading = true\n}\n\nfunction handleClickEnd() {\n loading = false\n close()\n}\n\nfunction handleClickOutside() {\n if (loading) return\n close()\n}\n\nprovide(ContextMenuDIKey, { cls })\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAuCA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,EAAE,SAAS,iBAAiB,CAAC,KAAK,GAAG,EAAE,MAAM,UAA2B,CAAC;EAE/E,MAAM,MAAM,IAAI,cAAc;EAE9B,MAAM,iBAAiB,WAAwB;EAE/C,MAAM,UAAU,WAAW,IAAI;EAE/B,MAAM,wBAAwB;GAC5B,MAAM,WAMF,CAAC;GAEL,MAAM,EAAE,kBAAkB;GAC1B,MAAM,EAAE,GAAG,MAAM;GACjB,IAAI,IAAI,OAAO,aAAa,GAC1B,SAAS,QAAQ,OAAO,aAAa,IAAI;QAEzC,SAAS,OAAO,IAAI;GAEtB,IAAI,IAAI,OAAO,cAAc,GAC3B,SAAS,SAAS,OAAO,cAAc,IAAI;QAE3C,SAAS,MAAM,IAAI;GAKrB,SAAS,kBAAkB,GAFT,SAAS,MAAM,QAAQ,SAED,GADtB,SAAS,OAAO,SAAS;GAG3C,OAAO,OAAO,YACZ,OAAO,QAAQ,QAAQ,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,SAAS,GAAkC,IAAI,CAAC,CAAC,CAChG;EACF;EAEA,MAAM,QAAQ,eAA8B;GAC1C,OAAO;IAAE,OAAO,SAAS,MAAM,OAAO,IAAI;IAAG,GAAG,gBAAgB;IAAG,QAAQ,OAAO;GAAE;EACtF,CAAC;EAED,MAAM,QAAQ,eAAe;GAC3B,MAAM,EAAE,UAAU;GAClB,IAAI,OAAO,UAAU,YACnB,OAAO,MAAM;GAEf,OAAO;EACT,CAAC;EAED,MAAM,mBAAmB,SAA0B;GACjD,OAAO,OAAO,KAAK,aAAa,aAAa,KAAK,SAAS,IAAI,KAAK;EACtE;EAEA,SAAS,QAAQ;GACf,QAAQ,QAAQ;EAClB;EAEA,IAAI,UAAU;EACd,SAAS,mBAAmB;GAC1B,UAAU;EACZ;EAEA,SAAS,iBAAiB;GACxB,UAAU;GACV,MAAM;EACR;EAEA,SAAS,qBAAqB;GAC5B,IAAI,SAAS;GACb,MAAM;EACR;EAEA,QAAQ,kBAAkB,EAAE,IAAI,CAAC;;uBApH/B,YAkBW,UAAA,EAlBD,IAAG,OAAM,GAAA,CACjB,YAgBa,YAAA;IAhBD,MAAK;IAAU,QAAA;IAAQ,cAAW,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;2BAe7C,CAVG,QAAA,QAAA,gBAAA,UAAA,GAJR,mBAcK,MAAA;;KAbF,OAAK,eAAA,CAAG,MAAA,GAAA,EAAI,GAAG,MAAA,GAAA,EAAI,EAAE,MAAA,IAAA,CAAI,CAAA,CAAA;KACzB,OAAK,eAAE,MAAA,KAAK;cACT;KAAJ,KAAI;0BAIJ,mBAME,UAAA,MAAA,WALe,MAAA,QAAR,SAAI;yBADb,YAME,aAAA;MAJO;MACN,UAAU,gBAAgB,IAAI;MAC9B,cAAa;MACb,YAAW;;6CAPG,kBAAkB,CAAA,CAAA,IAAA,mBAAA,QAAA,IAAA,CAAA,CAAA"}
@@ -1,6 +0,0 @@
1
- //#region src/components/context-menu/di.ts
2
- const ContextMenuDIKey = Symbol("ContextMenuDIKey");
3
- //#endregion
4
- export { ContextMenuDIKey };
5
-
6
- //# sourceMappingURL=di.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"di.js","names":[],"sources":["../../../src/components/context-menu/di.ts"],"sourcesContent":["import type { BEM } from '@veltra/utils'\nimport type { InjectionKey } from 'vue'\n\nexport const ContextMenuDIKey: InjectionKey<{ cls: BEM<'context-menu'> }> =\n Symbol('ContextMenuDIKey')\n"],"mappings":";AAGA,MAAa,mBACX,OAAO,kBAAkB"}
@@ -1,2 +0,0 @@
1
- import { _default } from "./context-menu.vue.js";
2
- import { contextmenu } from "./api.js";