niuma-ui 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/package.json +10 -2
  3. package/src/__tests__/RsButton.spec.ts +47 -0
  4. package/src/__tests__/RsCard.spec.ts +160 -1
  5. package/src/__tests__/RsConfirmDialog.spec.ts +127 -0
  6. package/src/__tests__/RsContextMenu.spec.ts +12 -0
  7. package/src/__tests__/RsDatePicker.spec.ts +21 -0
  8. package/src/__tests__/RsDateTimePicker.spec.ts +11 -0
  9. package/src/__tests__/RsDescriptions.spec.ts +50 -0
  10. package/src/__tests__/RsDialog.spec.ts +178 -0
  11. package/src/__tests__/RsDrawer.spec.ts +96 -3
  12. package/src/__tests__/RsDynamicTags.spec.ts +52 -0
  13. package/src/__tests__/RsForm.spec.ts +250 -6
  14. package/src/__tests__/RsFormItem.spec.ts +478 -0
  15. package/src/__tests__/RsFormList.spec.ts +109 -0
  16. package/src/__tests__/RsInput.spec.ts +108 -1
  17. package/src/__tests__/RsInputNumber.spec.ts +33 -0
  18. package/src/__tests__/RsMenu.spec.ts +406 -14
  19. package/src/__tests__/RsPagination.spec.ts +67 -2
  20. package/src/__tests__/RsSelect.spec.ts +465 -2
  21. package/src/__tests__/RsSwitch.spec.ts +74 -0
  22. package/src/__tests__/RsTable.a11y.spec.ts +83 -0
  23. package/src/__tests__/RsTable.dom-baseline.spec.ts +87 -0
  24. package/src/__tests__/RsTable.perf.spec.ts +23 -4
  25. package/src/__tests__/RsTable.spec.ts +63 -8
  26. package/src/__tests__/RsTable.ssr.spec.ts +71 -0
  27. package/src/__tests__/RsTable.summary.spec.ts +45 -0
  28. package/src/__tests__/RsTabs.spec.ts +102 -4
  29. package/src/__tests__/RsTimePicker.spec.ts +20 -0
  30. package/src/__tests__/RsTooltip.spec.ts +43 -0
  31. package/src/__tests__/RsUpload.spec.ts +62 -6
  32. package/src/__tests__/createRsDialog.spec.ts +145 -0
  33. package/src/__tests__/css-token.spec.ts +51 -0
  34. package/src/__tests__/date-picker-utils.spec.ts +49 -0
  35. package/src/__tests__/dialog-utils.spec.ts +43 -0
  36. package/src/__tests__/form-path.spec.ts +46 -0
  37. package/src/__tests__/form-rules.spec.ts +139 -0
  38. package/src/__tests__/perf-budgets.ts +32 -0
  39. package/src/__tests__/rs-table-api.spec.ts +206 -0
  40. package/src/__tests__/rs-table-compat-matrix.spec.ts +108 -0
  41. package/src/__tests__/rs-table-echarts-adapter.spec.ts +43 -0
  42. package/src/__tests__/rs-table-grid-nav.spec.ts +63 -0
  43. package/src/__tests__/rs-table-isolation.spec.ts +158 -0
  44. package/src/__tests__/split-pane-utils.spec.ts +21 -0
  45. package/src/__tests__/table-chart-utils.spec.ts +88 -0
  46. package/src/__tests__/table-summary-utils.spec.ts +62 -0
  47. package/src/__tests__/table-utils.spec.ts +124 -0
  48. package/src/__tests__/theme.spec.ts +27 -0
  49. package/src/__tests__/useRsTableColumnResize.spec.ts +25 -0
  50. package/src/__tests__/useRsTableCore.perf.spec.ts +170 -0
  51. package/src/components/RsAlert.vue +212 -0
  52. package/src/components/RsAvatar.vue +1 -1
  53. package/src/components/RsBadge.vue +1 -1
  54. package/src/components/RsBreadcrumb.vue +1 -1
  55. package/src/components/RsButton.vue +147 -23
  56. package/src/components/RsCalendarGrid.vue +3 -3
  57. package/src/components/RsCard.vue +204 -20
  58. package/src/components/RsCodeBlock.vue +4 -4
  59. package/src/components/RsCodeEditor.vue +8 -8
  60. package/src/components/RsConfirmDialog.vue +217 -30
  61. package/src/components/RsContextMenu.vue +57 -17
  62. package/src/components/RsDatePicker.vue +316 -39
  63. package/src/components/RsDateTimePicker.vue +27 -2
  64. package/src/components/RsDescriptions.vue +186 -0
  65. package/src/components/RsDescriptionsItem.vue +32 -0
  66. package/src/components/RsDialog.vue +374 -52
  67. package/src/components/RsDivider.vue +89 -0
  68. package/src/components/RsDrawer.vue +559 -34
  69. package/src/components/RsDropdown.vue +34 -3
  70. package/src/components/RsDynamicTags.vue +531 -0
  71. package/src/components/RsEmpty.vue +1 -1
  72. package/src/components/RsForm.vue +164 -8
  73. package/src/components/RsFormItem.vue +423 -0
  74. package/src/components/RsFormItemControl.vue +21 -0
  75. package/src/components/RsFormList.vue +117 -0
  76. package/src/components/RsInput.vue +491 -100
  77. package/src/components/RsInputNumber.vue +77 -27
  78. package/src/components/RsLabel.vue +20 -5
  79. package/src/components/RsLink.vue +1 -1
  80. package/src/components/RsLoadingBar.vue +151 -0
  81. package/src/components/RsMarkdown.vue +5 -5
  82. package/src/components/RsMenu.vue +74 -16
  83. package/src/components/RsMenuItems.vue +147 -9
  84. package/src/components/RsMonacoEditor.vue +9 -3
  85. package/src/components/RsPagination.vue +93 -14
  86. package/src/components/RsProseEditor.vue +3 -3
  87. package/src/components/RsRadio.vue +81 -0
  88. package/src/components/RsRadioItem.vue +156 -0
  89. package/src/components/RsSelect.vue +372 -642
  90. package/src/components/RsSidebarGroup.vue +1 -1
  91. package/src/components/RsSplitPane.vue +90 -1
  92. package/src/components/RsStatCard.vue +2 -2
  93. package/src/components/RsSteps.vue +2 -2
  94. package/src/components/RsSwitch.vue +192 -0
  95. package/src/components/RsTable.vue +653 -2555
  96. package/src/components/RsTabs.vue +422 -90
  97. package/src/components/RsTag.vue +191 -0
  98. package/src/components/RsTerminal.vue +58 -19
  99. package/src/components/RsTimePicker.vue +72 -9
  100. package/src/components/RsTimePickerColumns.vue +137 -78
  101. package/src/components/RsToaster.vue +64 -24
  102. package/src/components/RsTooltip.vue +10 -1
  103. package/src/components/RsTooltipProvider.vue +11 -1
  104. package/src/components/RsTree.vue +45 -27
  105. package/src/components/RsUpload.vue +147 -14
  106. package/src/components/RsVNodeHost.vue +20 -0
  107. package/src/components/button-utils.ts +39 -0
  108. package/src/components/context-menu-utils.ts +6 -0
  109. package/src/components/date-picker-utils.ts +195 -4
  110. package/src/components/descriptions-utils.ts +24 -0
  111. package/src/components/dialog-utils.ts +182 -0
  112. package/src/components/dialog-window.ts +74 -9
  113. package/src/components/drawer-utils.ts +73 -0
  114. package/src/components/form-item-control.ts +55 -0
  115. package/src/components/form-path.ts +92 -0
  116. package/src/components/form-rules.ts +311 -0
  117. package/src/components/form-utils.ts +145 -3
  118. package/src/components/menu-utils.ts +11 -0
  119. package/src/components/overlay-utils.ts +3 -0
  120. package/src/components/radio-utils.ts +12 -0
  121. package/src/components/reka.ts +5 -0
  122. package/src/components/select-utils.ts +285 -6
  123. package/src/components/split-pane-utils.ts +65 -12
  124. package/src/components/table/RsTableBody.vue +217 -0
  125. package/src/components/table/RsTableBodyRow.vue +90 -1
  126. package/src/components/table/RsTableColGroup.vue +41 -0
  127. package/src/components/table/RsTableHeader.vue +158 -0
  128. package/src/components/table/RsTableHeaderFilter.vue +1 -1
  129. package/src/components/table/RsTableSummaryRow.vue +52 -0
  130. package/src/components/table/rs-table-api.ts +99 -0
  131. package/src/components/table/rs-table-compat-matrix.ts +71 -0
  132. package/src/components/table/rs-table-echarts-adapter.ts +81 -0
  133. package/src/components/table/rs-table-feature-host.ts +178 -0
  134. package/src/components/table/rs-table-grid-nav.ts +122 -0
  135. package/src/components/table/rs-table-header-types.ts +47 -0
  136. package/src/components/table/rs-table-module-registry.ts +72 -0
  137. package/src/components/table/rs-table-props.ts +274 -0
  138. package/src/components/table/rs-table-view-context.ts +258 -0
  139. package/src/components/table/rs-table.css +85 -7
  140. package/src/components/table/table-chart-utils.ts +165 -0
  141. package/src/components/table/table-features.ts +284 -0
  142. package/src/components/table/table-summary-utils.ts +194 -0
  143. package/src/components/table-utils.ts +452 -2
  144. package/src/components/tabs-utils.ts +142 -2
  145. package/src/components/time-picker-utils.ts +76 -3
  146. package/src/components/upload-utils.ts +122 -0
  147. package/src/components/use-rs-select.ts +436 -0
  148. package/src/components/virtual-list-utils.ts +15 -2
  149. package/src/composables/assembleRsTableApi.ts +79 -0
  150. package/src/composables/bindRsTableViewContext.ts +269 -0
  151. package/src/composables/createRsDialog.ts +357 -0
  152. package/src/composables/createRsTableEditEmitBridge.ts +38 -0
  153. package/src/composables/useRsConfig.ts +10 -3
  154. package/src/composables/useRsI18n.ts +16 -5
  155. package/src/composables/useRsLoadingBar.ts +36 -0
  156. package/src/composables/useRsTable.ts +134 -0
  157. package/src/composables/useRsTableA11y.ts +52 -0
  158. package/src/composables/useRsTableChartBridge.ts +79 -0
  159. package/src/composables/useRsTableColumnLayout.ts +320 -0
  160. package/src/composables/useRsTableColumnResize.ts +244 -0
  161. package/src/composables/useRsTableColumnVirtual.ts +180 -0
  162. package/src/composables/useRsTableColumns.ts +200 -0
  163. package/src/composables/useRsTableContextMenu.ts +257 -0
  164. package/src/composables/useRsTableCore.ts +256 -0
  165. package/src/composables/useRsTableEditActions.ts +606 -0
  166. package/src/composables/useRsTableEditLayer.ts +251 -0
  167. package/src/composables/useRsTableEngine.ts +357 -0
  168. package/src/composables/useRsTableGridKeyboard.ts +123 -0
  169. package/src/composables/useRsTableHeadless.ts +137 -0
  170. package/src/composables/useRsTableInteraction.ts +433 -0
  171. package/src/composables/useRsTableScrollHost.ts +75 -0
  172. package/src/composables/useRsTableScrollLayout.ts +255 -0
  173. package/src/composables/useRsTableSelectionSource.ts +89 -0
  174. package/src/composables/useRsTableShell.ts +416 -0
  175. package/src/composables/useRsTableShellChrome.ts +170 -0
  176. package/src/composables/useRsTableSummary.ts +113 -0
  177. package/src/composables/useRsTableViewProvide.ts +263 -0
  178. package/src/composables/useRsTableVirtual.ts +154 -0
  179. package/src/composables/useRsTabsNav.ts +4 -0
  180. package/src/composables/useTableEdit.ts +1 -1
  181. package/src/index.ts +378 -10
  182. package/src/locale/interpolate.ts +32 -0
  183. package/src/locale/messages.ts +40 -0
  184. package/src/styles.css +365 -56
  185. package/src/theme/brand.example.css +24 -2
  186. package/src/theme/css-token.ts +107 -0
  187. package/src/theme/presets.ts +4 -0
  188. package/src/theme/types.ts +68 -0
  189. package/src/components/RsTableColgroup.vue +0 -17
  190. package/src/components/RsTableHeaderRow.vue +0 -30
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 装配 RsTable 公开 Api(从 SFC 下沉,单一出口)。
3
+ */
4
+
5
+ import type { ComputedRef, Ref } from 'vue'
6
+ import type { RsTableApi } from '../components/table/rs-table-api'
7
+ import type { RsTableRowData, RsTableRowEntry } from '../components/table-utils'
8
+ import type { RsTableAnalyticsSnapshot } from './useRsTableSelectionSource'
9
+ import type { useRsTable } from './useRsTable'
10
+
11
+ type RowEntry<T extends RsTableRowData> = Extract<RsTableRowEntry<T>, { type: 'row' }>
12
+
13
+ export interface AssembleRsTableApiOptions<T extends RsTableRowData> {
14
+ tableSurface: ReturnType<typeof useRsTable<T>>
15
+ dataRows: Ref<RowEntry<T>[]> | ComputedRef<RowEntry<T>[]>
16
+ rowKeyByIndex: Ref<Map<number, string>> | ComputedRef<Map<number, string>>
17
+ viewRows: Ref<T[]> | ComputedRef<T[]>
18
+ selectedRows: Ref<T[]> | ComputedRef<T[]>
19
+ selectedRowKeys: Ref<string[]> | ComputedRef<string[]>
20
+ getAnalyticsSnapshot: () => RsTableAnalyticsSnapshot<T>
21
+ subscribeAnalytics: (listener: (snap: RsTableAnalyticsSnapshot<T>) => void) => () => void
22
+ getRowByKey: (
23
+ rowKey: string,
24
+ dataRows: RowEntry<T>[],
25
+ rowKeyByIndex: Map<number, string>,
26
+ ) => T | undefined
27
+ getRowByIndex: (index: number, dataRows: RowEntry<T>[]) => T | undefined
28
+ getColumnValues: (colKey: string, dataRows: RowEntry<T>[]) => unknown[]
29
+ getCellValue: (
30
+ rowKey: string,
31
+ colKey: string,
32
+ dataRows: RowEntry<T>[],
33
+ rowKeyByIndex: Map<number, string>,
34
+ ) => unknown
35
+ toApiEditSlice: (ctx: {
36
+ dataRows: Ref<RowEntry<T>[]> | ComputedRef<RowEntry<T>[]>
37
+ rowKeyByIndex: Ref<Map<number, string>> | ComputedRef<Map<number, string>>
38
+ }) => Pick<
39
+ RsTableApi<T>,
40
+ | 'getHighlightedRowKey'
41
+ | 'setHighlightedRowKey'
42
+ | 'cancelCellEdit'
43
+ | 'cancelAllEdits'
44
+ | 'getDirtyCellKeys'
45
+ | 'getCellError'
46
+ | 'setCellError'
47
+ | 'stageCell'
48
+ | 'rejectRowEdit'
49
+ | 'undoEdit'
50
+ | 'redoEdit'
51
+ | 'commitRowEdits'
52
+ | 'rollbackRowEdits'
53
+ >
54
+ }
55
+
56
+ /**
57
+ * 创建并返回 RsTableApi(含 feature 元数据)。
58
+ */
59
+ export function assembleRsTableApi<T extends RsTableRowData>(
60
+ options: AssembleRsTableApiOptions<T>,
61
+ ): RsTableApi<T> {
62
+ const { tableSurface, dataRows, rowKeyByIndex } = options
63
+ return tableSurface.createApi({
64
+ getRowByKey: (rowKey) =>
65
+ options.getRowByKey(rowKey, dataRows.value, rowKeyByIndex.value),
66
+ getRowByIndex: (index) => options.getRowByIndex(index, dataRows.value),
67
+ getColumnValues: (colKey) => options.getColumnValues(colKey, dataRows.value),
68
+ getCellValue: (rowKey, colKey) =>
69
+ options.getCellValue(rowKey, colKey, dataRows.value, rowKeyByIndex.value),
70
+ getViewRows: () => options.viewRows.value,
71
+ getSelectedRows: () => options.selectedRows.value,
72
+ getSelectedRowKeys: () => [...options.selectedRowKeys.value],
73
+ getAnalyticsSnapshot: options.getAnalyticsSnapshot,
74
+ subscribeAnalytics: options.subscribeAnalytics,
75
+ ...options.toApiEditSlice({ dataRows, rowKeyByIndex }),
76
+ getToolbarItems: () => tableSurface.getToolbarItems(),
77
+ getOverlayContributions: () => tableSurface.getOverlayContributions(),
78
+ })
79
+ }
@@ -0,0 +1,269 @@
1
+ /**
2
+ * 将 Shell / Edit / Chrome 接线捆成 ViewContext provide(削薄 RsTable.vue)。
3
+ */
4
+
5
+ import type { ComputedRef, Ref } from 'vue'
6
+ import type {
7
+ RsTableRowData,
8
+ RsTableRowEntry,
9
+ RsTableRowKey,
10
+ RsTableSelectionType,
11
+ } from '../components/table-utils'
12
+ import type { RsTableCellEditFocusMode, RsTableCellEditTrigger } from '../components/table/table-edit-utils'
13
+ import type { RsTableBodyLabels, RsTableHeaderLabels } from '../components/table/rs-table-view-context'
14
+ import type { useTableEdit } from './useTableEdit'
15
+ import {
16
+ useRsTableViewProvide,
17
+ type RsTableViewProvideHandlers,
18
+ type RsTableViewProvideSources,
19
+ } from './useRsTableViewProvide'
20
+
21
+ type RowEntry<T extends RsTableRowData> = Extract<RsTableRowEntry<T>, { type: 'row' }>
22
+ type TableEditApi = ReturnType<typeof useTableEdit>
23
+
24
+ export interface BindRsTableViewContextDeps<T extends RsTableRowData> {
25
+ props: {
26
+ columnDraggable: () => boolean
27
+ resizable: () => boolean
28
+ multiSort: () => boolean
29
+ loading: () => boolean
30
+ loadingMore: () => boolean
31
+ infinite: () => boolean
32
+ rowKey: () => RsTableRowKey<T> | undefined
33
+ striped: () => boolean
34
+ selectionType: () => RsTableSelectionType
35
+ editable: () => boolean
36
+ editTrigger: () => RsTableCellEditTrigger
37
+ rowCommit: () => boolean
38
+ allowNull: () => boolean
39
+ editFocusMode: () => RsTableCellEditFocusMode
40
+ nullLabel: () => string
41
+ }
42
+ layout: Pick<
43
+ RsTableViewProvideSources<T>,
44
+ | 'useFixedColumnLayout'
45
+ | 'showRowDragHandle'
46
+ | 'detailExpandable'
47
+ | 'showSelectColumn'
48
+ | 'showEditGutterColumn'
49
+ | 'showIndexColumn'
50
+ | 'showRowStatusColumn'
51
+ | 'columnPadLeft'
52
+ | 'columnPadRight'
53
+ | 'visibleDataColumns'
54
+ | 'prefixWidths'
55
+ | 'resolvedGutterWidth'
56
+ | 'resolvedIndexWidth'
57
+ | 'isRadioSelection'
58
+ | 'selectAllState'
59
+ | 'columnFiltersState'
60
+ | 'dragColumnKey'
61
+ | 'dropColumnKey'
62
+ | 'dragLeadHeaderStyle'
63
+ | 'expandLeadHeaderStyle'
64
+ | 'selectLeadHeaderStyle'
65
+ | 'gutterLeadHeaderStyle'
66
+ | 'indexLeadHeaderStyle'
67
+ | 'columnHeaderStyleMap'
68
+ | 'bodyColspan'
69
+ | 'emptyContentStyle'
70
+ | 'virtualScrollEnabled'
71
+ | 'virtualSlice'
72
+ | 'visibleEntries'
73
+ | 'treeMode'
74
+ | 'treeIndentPx'
75
+ | 'treeExpandColumnKey'
76
+ | 'treeLoadingKeySet'
77
+ | 'dragLeadStyle'
78
+ | 'expandLeadStyle'
79
+ | 'selectLeadStyle'
80
+ | 'indexLeadStyle'
81
+ | 'gutterLeadStyle'
82
+ | 'columnTdClassMap'
83
+ | 'columnStyleMap'
84
+ | 'dragRowKeys'
85
+ | 'dropRowTargetKey'
86
+ | 'dropRowPosition'
87
+ >
88
+ chrome: {
89
+ hasData: Ref<boolean> | ComputedRef<boolean>
90
+ headerLabels: Ref<RsTableHeaderLabels> | ComputedRef<RsTableHeaderLabels>
91
+ bodyLabels: Ref<RsTableBodyLabels> | ComputedRef<RsTableBodyLabels>
92
+ hasColumnSlot: (key: string) => boolean
93
+ hasEditSlot: (key: string) => boolean
94
+ }
95
+ edit: {
96
+ tableEdit: TableEditApi
97
+ activeEditCellKey: Ref<string> | ComputedRef<string>
98
+ activeFocusCellKey: Ref<string> | ComputedRef<string>
99
+ activeErrorMapSize: Ref<number> | ComputedRef<number>
100
+ activeValidatingMapSize: Ref<number> | ComputedRef<number>
101
+ cellTooltipEnabled: RsTableViewProvideHandlers<T>['cellTooltipEnabled']
102
+ cellTooltipMode: RsTableViewProvideHandlers<T>['cellTooltipMode']
103
+ cellTooltipText: RsTableViewProvideHandlers<T>['cellTooltipText']
104
+ cellTooltipFallbackTitle: RsTableViewProvideHandlers<T>['cellTooltipFallbackTitle']
105
+ rowEditPending: RsTableViewProvideHandlers<T>['rowEditPending']
106
+ isExternalRowPending: RsTableViewProvideHandlers<T>['isExternalRowPending']
107
+ onCellStartEdit: RsTableViewProvideHandlers<T>['onCellStartEdit']
108
+ onCellCommit: RsTableViewProvideHandlers<T>['onCellCommit']
109
+ onCellCancel: RsTableViewProvideHandlers<T>['onCellCancel']
110
+ onCellUpdateDraft: RsTableViewProvideHandlers<T>['onCellUpdateDraft']
111
+ onCellNavigate: RsTableViewProvideHandlers<T>['onCellNavigate']
112
+ onRowCommit: RsTableViewProvideHandlers<T>['onRowCommit']
113
+ onRowRollback: RsTableViewProvideHandlers<T>['onRowRollback']
114
+ onGutterCommit: RsTableViewProvideHandlers<T>['onGutterCommit']
115
+ onCellClick: RsTableViewProvideHandlers<T>['onCellClick']
116
+ onCellDblclick: RsTableViewProvideHandlers<T>['onCellDblclick']
117
+ }
118
+ interaction: {
119
+ isHighlighted: (key: string) => boolean
120
+ onCellTipOver: (event: PointerEvent) => void
121
+ onCellTipOut: (event: PointerEvent) => void
122
+ } & Pick<
123
+ RsTableViewProvideHandlers<T>,
124
+ | 'resolvedDataColumnWidth'
125
+ | 'columnFilterValue'
126
+ | 'sortOrderFor'
127
+ | 'sortPriorityFor'
128
+ | 'sortIconName'
129
+ | 'onToggleSelectAll'
130
+ | 'onColumnDragOver'
131
+ | 'onColumnDrop'
132
+ | 'onColumnDragStart'
133
+ | 'onColumnDragEnd'
134
+ | 'updateColumnFilter'
135
+ | 'onHeaderClick'
136
+ | 'onResizeStart'
137
+ | 'rowKeyFor'
138
+ | 'isRowSelected'
139
+ | 'isRowIndeterminate'
140
+ | 'isRowExpanded'
141
+ | 'treeDepthOf'
142
+ | 'canExpandRow'
143
+ | 'canSelectRow'
144
+ | 'isRowDragByRow'
145
+ | 'isRowDragging'
146
+ | 'isRowDropTarget'
147
+ | 'onRowClick'
148
+ | 'onRowSelectMouseDown'
149
+ | 'onCellContextmenu'
150
+ | 'onRowDblclick'
151
+ | 'onRowContextmenu'
152
+ | 'onRowDragOver'
153
+ | 'onRowDragLeave'
154
+ | 'onRowDrop'
155
+ | 'onRowDragStart'
156
+ | 'onRowDragEnd'
157
+ | 'onToggleExpand'
158
+ | 'onToggleSelect'
159
+ >
160
+ }
161
+
162
+ /**
163
+ * 装配并 provide ViewContext。
164
+ */
165
+ export function bindRsTableViewContext<T extends RsTableRowData>(
166
+ deps: BindRsTableViewContextDeps<T>,
167
+ ) {
168
+ const { props, layout, chrome, edit, interaction } = deps
169
+ const { tableEdit } = edit
170
+
171
+ return useRsTableViewProvide<T>(
172
+ {
173
+ ...layout,
174
+ columnDraggable: props.columnDraggable,
175
+ resizable: props.resizable,
176
+ multiSort: props.multiSort,
177
+ headerLabels: chrome.headerLabels,
178
+ loading: props.loading,
179
+ hasData: chrome.hasData,
180
+ loadingMore: props.loadingMore,
181
+ infinite: props.infinite,
182
+ rowKey: props.rowKey,
183
+ striped: props.striped,
184
+ selectionType: props.selectionType,
185
+ editable: props.editable,
186
+ editTrigger: props.editTrigger,
187
+ rowCommit: props.rowCommit,
188
+ allowNull: props.allowNull,
189
+ editFocusMode: props.editFocusMode,
190
+ nullLabel: props.nullLabel,
191
+ activeEditCellKey: edit.activeEditCellKey,
192
+ activeFocusCellKey: edit.activeFocusCellKey,
193
+ activeErrorMapSize: edit.activeErrorMapSize,
194
+ activeValidatingMapSize: edit.activeValidatingMapSize,
195
+ stagedMapSize: () => tableEdit.stagedMap.value.size,
196
+ editingDraft: () => tableEdit.editingDraft.value,
197
+ editingCell: () => tableEdit.editingCell.value,
198
+ focusCell: () => tableEdit.focusCell.value,
199
+ bodyLabels: chrome.bodyLabels,
200
+ },
201
+ {
202
+ resolvedDataColumnWidth: interaction.resolvedDataColumnWidth,
203
+ columnFilterValue: interaction.columnFilterValue,
204
+ sortOrderFor: interaction.sortOrderFor,
205
+ sortPriorityFor: interaction.sortPriorityFor,
206
+ sortIconName: interaction.sortIconName,
207
+ onToggleSelectAll: interaction.onToggleSelectAll,
208
+ onColumnDragOver: interaction.onColumnDragOver,
209
+ onColumnDrop: interaction.onColumnDrop,
210
+ onColumnDragStart: interaction.onColumnDragStart,
211
+ onColumnDragEnd: interaction.onColumnDragEnd,
212
+ updateColumnFilter: interaction.updateColumnFilter,
213
+ onHeaderClick: interaction.onHeaderClick,
214
+ onResizeStart: interaction.onResizeStart,
215
+ onHeaderPointerOver: interaction.onCellTipOver,
216
+ onHeaderPointerOut: interaction.onCellTipOut,
217
+ rowKeyFor: interaction.rowKeyFor,
218
+ isRowSelected: interaction.isRowSelected,
219
+ isRowIndeterminate: interaction.isRowIndeterminate,
220
+ isRowExpanded: interaction.isRowExpanded,
221
+ isHighlighted: interaction.isHighlighted,
222
+ rowEditPending: edit.rowEditPending,
223
+ isExternalRowPending: edit.isExternalRowPending,
224
+ treeDepthOf: interaction.treeDepthOf,
225
+ canExpandRow: interaction.canExpandRow,
226
+ canSelectRow: interaction.canSelectRow,
227
+ isRowDragByRow: interaction.isRowDragByRow,
228
+ isRowDragging: interaction.isRowDragging,
229
+ isRowDropTarget: interaction.isRowDropTarget,
230
+ isRowDirty: tableEdit.isRowDirty,
231
+ hasColumnSlot: chrome.hasColumnSlot,
232
+ hasEditSlot: chrome.hasEditSlot,
233
+ cellTooltipEnabled: edit.cellTooltipEnabled,
234
+ cellTooltipMode: edit.cellTooltipMode,
235
+ cellTooltipText: edit.cellTooltipText,
236
+ cellTooltipFallbackTitle: edit.cellTooltipFallbackTitle,
237
+ getCellDraft: tableEdit.getDraft,
238
+ isCellDirty: tableEdit.isDirty,
239
+ getCellError: tableEdit.getCellError,
240
+ isCellValidating: tableEdit.isValidating,
241
+ onBodyPointerOver: interaction.onCellTipOver,
242
+ onBodyPointerOut: interaction.onCellTipOut,
243
+ onRowClick: interaction.onRowClick,
244
+ onRowSelectMouseDown: interaction.onRowSelectMouseDown,
245
+ onCellClick: edit.onCellClick,
246
+ onCellDblclick: edit.onCellDblclick,
247
+ onCellContextmenu: interaction.onCellContextmenu,
248
+ onRowDblclick: interaction.onRowDblclick,
249
+ onRowContextmenu: interaction.onRowContextmenu,
250
+ onRowDragOver: interaction.onRowDragOver,
251
+ onRowDragLeave: interaction.onRowDragLeave,
252
+ onRowDrop: interaction.onRowDrop,
253
+ onRowDragStart: interaction.onRowDragStart,
254
+ onRowDragEnd: interaction.onRowDragEnd,
255
+ onToggleExpand: interaction.onToggleExpand,
256
+ onToggleSelect: interaction.onToggleSelect,
257
+ onCellStartEdit: edit.onCellStartEdit,
258
+ onCellCommit: edit.onCellCommit,
259
+ onCellCancel: edit.onCellCancel,
260
+ onCellUpdateDraft: edit.onCellUpdateDraft,
261
+ onCellNavigate: edit.onCellNavigate,
262
+ onRowCommit: edit.onRowCommit,
263
+ onRowRollback: edit.onRowRollback,
264
+ onGutterCommit: edit.onGutterCommit,
265
+ },
266
+ )
267
+ }
268
+
269
+ export type { RowEntry }
@@ -0,0 +1,357 @@
1
+ import { createApp, h, ref, type App } from 'vue'
2
+ import RsConfigProvider from '../components/RsConfigProvider.vue'
3
+ import RsConfirmDialog from '../components/RsConfirmDialog.vue'
4
+ import RsDialog from '../components/RsDialog.vue'
5
+ import type { RsConfirmOptions } from '../components/dialog-utils'
6
+ import type { RsDialogBeforeClose, RsDialogCloseReason, RsDialogWidth } from '../components/dialog-utils'
7
+ import type { RsFeedbackTone } from '../components/overlay-utils'
8
+ import { defaultLocale, type RsLocale } from '../locale/types'
9
+ import type { RsThemeMode } from '../theme/types'
10
+
11
+ export type RsConfirmResult = boolean
12
+
13
+ export type RsConfirmInput = RsConfirmOptions | string
14
+
15
+ export interface RsDialogOpenOptions {
16
+ title?: string
17
+ subtitle?: string
18
+ description?: string
19
+ width?: RsDialogWidth
20
+ tone?: RsFeedbackTone
21
+ /**
22
+ * 布局。默认 window(工作窗)。
23
+ * confirm 仅作历史兼容;新代码的确认/提示请用 rsConfirm / RsConfirmDialog。
24
+ */
25
+ layout?: 'window' | 'confirm'
26
+ showOverlay?: boolean
27
+ /** 遮罩不透明度 0–1 */
28
+ overlayOpacity?: number
29
+ /** 遮罩模糊;number 为 px */
30
+ overlayBlur?: number | string
31
+ showClose?: boolean
32
+ closeOnOverlayClick?: boolean
33
+ closeOnEsc?: boolean
34
+ showFooter?: boolean
35
+ showCancel?: boolean
36
+ showConfirm?: boolean
37
+ cancelText?: string
38
+ confirmText?: string
39
+ confirmVariant?: 'primary' | 'danger'
40
+ autoCloseOnConfirm?: boolean
41
+ beforeClose?: RsDialogBeforeClose
42
+ teleportTo?: string | HTMLElement
43
+ /** 默认插槽对应 #body 的渲染函数 */
44
+ body?: () => unknown
45
+ footer?: () => unknown
46
+ onConfirm?: () => void | Promise<void>
47
+ onCancel?: () => void
48
+ onAfterClose?: (reason: RsDialogCloseReason) => void
49
+ /** 命令式挂载时覆盖主题;默认读 document data-rs-theme */
50
+ theme?: RsThemeMode
51
+ /** 命令式挂载时覆盖语言;默认读 document data-rs-locale */
52
+ locale?: RsLocale
53
+ }
54
+
55
+ export interface RsDialogHandle {
56
+ close: (reason?: RsDialogCloseReason) => Promise<boolean>
57
+ destroy: () => void
58
+ }
59
+
60
+ /** tone 快捷方法:可传完整选项或纯文案(作为 description) */
61
+ export interface RsConfirmApi {
62
+ (options?: RsConfirmOptions): Promise<RsConfirmResult>
63
+ warning: (options?: RsConfirmInput) => Promise<RsConfirmResult>
64
+ danger: (options?: RsConfirmInput) => Promise<RsConfirmResult>
65
+ info: (options?: RsConfirmInput) => Promise<RsConfirmResult>
66
+ success: (options?: RsConfirmInput) => Promise<RsConfirmResult>
67
+ error: (options?: RsConfirmInput) => Promise<RsConfirmResult>
68
+ confirm: (options?: RsConfirmInput) => Promise<RsConfirmResult>
69
+ }
70
+
71
+ function readDomTheme(): RsThemeMode {
72
+ if (typeof document === 'undefined') return 'light'
73
+ return document.documentElement.getAttribute('data-rs-theme') === 'dark' ? 'dark' : 'light'
74
+ }
75
+
76
+ function readDomLocale(): RsLocale {
77
+ if (typeof document === 'undefined') return defaultLocale
78
+ const value = document.documentElement.getAttribute('data-rs-locale')
79
+ return value === 'en-US' || value === 'zh-CN' ? value : defaultLocale
80
+ }
81
+
82
+ function mountHost(
83
+ render: () => unknown,
84
+ config?: { theme?: RsThemeMode; locale?: RsLocale },
85
+ ): { app: App; container: HTMLDivElement; destroy: () => void } {
86
+ const container = document.createElement('div')
87
+ document.body.appendChild(container)
88
+
89
+ // themeScope=local:不覆盖主应用的 document 主题;Portal 仍靠页面已有 data-rs-theme。
90
+ // ConfigProvider 负责 provide locale,使命令式实例的 i18n 默认文案正确。
91
+ const theme = config?.theme ?? readDomTheme()
92
+ const locale = config?.locale ?? readDomLocale()
93
+
94
+ const app = createApp({
95
+ setup() {
96
+ return () =>
97
+ h(
98
+ RsConfigProvider,
99
+ { theme, locale, themeScope: 'local' },
100
+ { default: () => render() },
101
+ )
102
+ },
103
+ })
104
+ app.mount(container)
105
+ const destroy = () => {
106
+ app.unmount()
107
+ container.remove()
108
+ }
109
+ return { app, container, destroy }
110
+ }
111
+
112
+ function normalizeConfirmInput(
113
+ options: RsConfirmInput | undefined,
114
+ defaults: Partial<RsConfirmOptions>,
115
+ ): RsConfirmOptions {
116
+ if (typeof options === 'string') {
117
+ return { ...defaults, description: options }
118
+ }
119
+ return { ...defaults, ...(options ?? {}) }
120
+ }
121
+
122
+ /**
123
+ * 命令式确认框(基于 RsConfirmDialog)。
124
+ * resolve(true) 为确认,resolve(false) 为取消/关闭。
125
+ * 若传入 onConfirm 且返回 Promise,确认期间自动 loading,成功后关闭。
126
+ */
127
+ function rsConfirmCore(options: RsConfirmOptions = {}): Promise<RsConfirmResult> {
128
+ return new Promise((resolve) => {
129
+ const open = ref(true)
130
+ const loading = ref(options.confirmLoading ?? false)
131
+ let settled = false
132
+ let confirmed = false
133
+ const hasAsyncConfirm = typeof options.onConfirm === 'function'
134
+
135
+ const settle = (value: boolean) => {
136
+ if (settled) return
137
+ settled = true
138
+ open.value = false
139
+ if (!value) options.onCancel?.()
140
+ // 等关闭帧后再销毁,避免动画被硬切
141
+ window.setTimeout(() => destroy(), 200)
142
+ resolve(value)
143
+ }
144
+
145
+ const { destroy } = mountHost(
146
+ () =>
147
+ h(
148
+ RsConfirmDialog,
149
+ {
150
+ open: open.value,
151
+ 'onUpdate:open': (value: boolean) => {
152
+ open.value = value
153
+ if (!value) {
154
+ // 等同 tick 内的 confirm/cancel 事件先落定,避免 Action 关闭抢先 resolve(false)
155
+ queueMicrotask(() => settle(confirmed))
156
+ }
157
+ },
158
+ title: options.title,
159
+ subtitle: options.subtitle,
160
+ description: options.description,
161
+ tone: options.tone,
162
+ icon: options.icon,
163
+ width: options.width,
164
+ confirmText: options.confirmText,
165
+ cancelText: options.cancelText,
166
+ confirmVariant: options.confirmVariant,
167
+ showCancel: options.showCancel,
168
+ confirmLoading: loading.value,
169
+ beforeClose: options.beforeClose,
170
+ // 有异步 onConfirm 时由我们在结束后关闭,避免提前关掉
171
+ autoCloseOnConfirm: !hasAsyncConfirm,
172
+ showOverlay: options.showOverlay ?? true,
173
+ overlayOpacity: options.overlayOpacity,
174
+ overlayBlur: options.overlayBlur,
175
+ teleportTo: options.teleportTo === false ? false : options.teleportTo,
176
+ onConfirm: () => {
177
+ if (!hasAsyncConfirm) {
178
+ confirmed = true
179
+ return
180
+ }
181
+ loading.value = true
182
+ void Promise.resolve()
183
+ .then(() => options.onConfirm?.())
184
+ .then(() => {
185
+ confirmed = true
186
+ settle(true)
187
+ })
188
+ .catch(() => {
189
+ loading.value = false
190
+ })
191
+ },
192
+ onCancel: () => {
193
+ confirmed = false
194
+ },
195
+ },
196
+ options.extra
197
+ ? {
198
+ extra: () => options.extra?.(),
199
+ }
200
+ : undefined,
201
+ ),
202
+ { theme: options.theme, locale: options.locale },
203
+ )
204
+ })
205
+ }
206
+
207
+ /**
208
+ * 命令式确认 / 提示 API。
209
+ * - rsConfirm(options) 基础调用
210
+ * - rsConfirm.warning / .danger / .confirm 双按钮确认
211
+ * - rsConfirm.info / .success / .error 默认单按钮提示(showCancel: false)
212
+ */
213
+ export const rsConfirm: RsConfirmApi = Object.assign(rsConfirmCore, {
214
+ warning: (options?: RsConfirmInput) =>
215
+ rsConfirmCore(
216
+ normalizeConfirmInput(options, {
217
+ tone: 'danger',
218
+ confirmVariant: 'danger',
219
+ showOverlay: true,
220
+ }),
221
+ ),
222
+ danger: (options?: RsConfirmInput) =>
223
+ rsConfirmCore(
224
+ normalizeConfirmInput(options, {
225
+ tone: 'danger',
226
+ confirmVariant: 'danger',
227
+ showOverlay: true,
228
+ }),
229
+ ),
230
+ info: (options?: RsConfirmInput) =>
231
+ rsConfirmCore(
232
+ normalizeConfirmInput(options, {
233
+ tone: 'info',
234
+ confirmVariant: 'primary',
235
+ showCancel: false,
236
+ showOverlay: true,
237
+ }),
238
+ ),
239
+ success: (options?: RsConfirmInput) =>
240
+ rsConfirmCore(
241
+ normalizeConfirmInput(options, {
242
+ tone: 'success',
243
+ confirmVariant: 'primary',
244
+ showCancel: false,
245
+ showOverlay: true,
246
+ }),
247
+ ),
248
+ error: (options?: RsConfirmInput) =>
249
+ rsConfirmCore(
250
+ normalizeConfirmInput(options, {
251
+ tone: 'danger',
252
+ confirmVariant: 'danger',
253
+ showCancel: false,
254
+ showOverlay: true,
255
+ }),
256
+ ),
257
+ confirm: (options?: RsConfirmInput) =>
258
+ rsConfirmCore(
259
+ normalizeConfirmInput(options, {
260
+ tone: 'warning',
261
+ confirmVariant: 'primary',
262
+ showOverlay: true,
263
+ }),
264
+ ),
265
+ })
266
+
267
+ /**
268
+ * 命令式打开 RsDialog;适合临时表单/说明层。
269
+ * 确认/危险提示请用 rsConfirm,不要传 layout: 'confirm'。
270
+ * 复杂长期状态仍建议声明式使用组件。
271
+ */
272
+ export function openRsDialog(options: RsDialogOpenOptions = {}): RsDialogHandle {
273
+ const open = ref(true)
274
+ let destroyed = false
275
+ let dialogExpose: { close?: (reason?: RsDialogCloseReason) => Promise<boolean> } | null = null
276
+ const layout = options.layout ?? 'window'
277
+
278
+ if (import.meta.env.DEV && layout === 'confirm') {
279
+ console.warn(
280
+ '[openRsDialog] layout:"confirm" 已弃用。确认/提示请使用 rsConfirm / RsConfirmDialog;本 API 仅用于工作窗/表单。',
281
+ )
282
+ }
283
+
284
+ const { destroy: unmount } = mountHost(
285
+ () =>
286
+ h(
287
+ RsDialog,
288
+ {
289
+ ref: (instance: unknown) => {
290
+ dialogExpose = instance as typeof dialogExpose
291
+ },
292
+ open: open.value,
293
+ 'onUpdate:open': (value: boolean) => {
294
+ open.value = value
295
+ if (!value && !destroyed) {
296
+ window.setTimeout(() => destroy(), 200)
297
+ }
298
+ },
299
+ title: options.title,
300
+ description:
301
+ [options.subtitle, options.description].filter(Boolean).join('\n\n') ||
302
+ options.description,
303
+ width: options.width ?? 'md',
304
+ tone: options.tone,
305
+ layout,
306
+ resizable: layout === 'window',
307
+ fullscreenable: layout === 'window',
308
+ showOverlay: options.showOverlay ?? true,
309
+ overlayOpacity: options.overlayOpacity,
310
+ overlayBlur: options.overlayBlur,
311
+ showClose: options.showClose,
312
+ closeOnOverlayClick: options.closeOnOverlayClick,
313
+ closeOnEsc: options.closeOnEsc,
314
+ showFooter: options.showFooter ?? true,
315
+ showCancel: options.showCancel,
316
+ showConfirm: options.showConfirm,
317
+ cancelText: options.cancelText,
318
+ confirmText: options.confirmText,
319
+ confirmVariant: options.confirmVariant,
320
+ autoCloseOnConfirm: options.autoCloseOnConfirm ?? true,
321
+ beforeClose: options.beforeClose,
322
+ teleportTo: options.teleportTo,
323
+ deferBodyMount: false,
324
+ onConfirm: () => {
325
+ void options.onConfirm?.()
326
+ },
327
+ onCancel: () => {
328
+ options.onCancel?.()
329
+ },
330
+ onAfterClose: (reason: RsDialogCloseReason) => {
331
+ options.onAfterClose?.(reason)
332
+ },
333
+ },
334
+ {
335
+ body: options.body,
336
+ footer: options.footer,
337
+ },
338
+ ),
339
+ { theme: options.theme, locale: options.locale },
340
+ )
341
+
342
+ function destroy(): void {
343
+ if (destroyed) return
344
+ destroyed = true
345
+ unmount()
346
+ }
347
+
348
+ return {
349
+ async close(reason: RsDialogCloseReason = 'programmatic') {
350
+ if (destroyed) return false
351
+ if (dialogExpose?.close) return dialogExpose.close(reason)
352
+ open.value = false
353
+ return true
354
+ },
355
+ destroy,
356
+ }
357
+ }