niuma-ui 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -1
- package/NOTICE +2 -2
- package/package.json +13 -5
- package/src/__tests__/RsButton.spec.ts +47 -0
- package/src/__tests__/RsCard.spec.ts +160 -1
- package/src/__tests__/RsConfirmDialog.spec.ts +127 -0
- package/src/__tests__/RsContextMenu.spec.ts +12 -0
- package/src/__tests__/RsDateTimePicker.spec.ts +11 -0
- package/src/__tests__/RsDescriptions.spec.ts +50 -0
- package/src/__tests__/RsDialog.spec.ts +117 -0
- package/src/__tests__/RsDrawer.spec.ts +96 -3
- package/src/__tests__/RsDynamicTags.spec.ts +52 -0
- package/src/__tests__/RsForm.spec.ts +245 -1
- package/src/__tests__/RsInput.spec.ts +15 -1
- package/src/__tests__/RsMenu.spec.ts +406 -14
- package/src/__tests__/RsPagination.spec.ts +67 -2
- package/src/__tests__/RsSelect.spec.ts +42 -1
- package/src/__tests__/RsSwitch.spec.ts +44 -0
- package/src/__tests__/RsTable.a11y.spec.ts +83 -0
- package/src/__tests__/RsTable.dom-baseline.spec.ts +87 -0
- package/src/__tests__/RsTable.perf.spec.ts +23 -4
- package/src/__tests__/RsTable.spec.ts +63 -8
- package/src/__tests__/RsTable.ssr.spec.ts +71 -0
- package/src/__tests__/RsTable.summary.spec.ts +45 -0
- package/src/__tests__/RsTabs.spec.ts +102 -4
- package/src/__tests__/RsTerminal.spec.ts +1 -1
- package/src/__tests__/RsTimePicker.spec.ts +11 -0
- package/src/__tests__/RsUpload.spec.ts +62 -6
- package/src/__tests__/createRsDialog.spec.ts +145 -0
- package/src/__tests__/form-rules.spec.ts +76 -0
- package/src/__tests__/perf-budgets.ts +32 -0
- package/src/__tests__/rs-table-api.spec.ts +206 -0
- package/src/__tests__/rs-table-compat-matrix.spec.ts +108 -0
- package/src/__tests__/rs-table-echarts-adapter.spec.ts +43 -0
- package/src/__tests__/rs-table-grid-nav.spec.ts +63 -0
- package/src/__tests__/rs-table-isolation.spec.ts +158 -0
- package/src/__tests__/split-pane-utils.spec.ts +21 -0
- package/src/__tests__/table-chart-utils.spec.ts +88 -0
- package/src/__tests__/table-summary-utils.spec.ts +62 -0
- package/src/__tests__/table-utils.spec.ts +124 -0
- package/src/__tests__/theme.spec.ts +25 -0
- package/src/__tests__/useRsTableColumnResize.spec.ts +25 -0
- package/src/__tests__/useRsTableCore.perf.spec.ts +170 -0
- package/src/components/RsAlert.vue +212 -0
- package/src/components/RsButton.vue +145 -21
- package/src/components/RsCard.vue +200 -16
- package/src/components/RsConfirmDialog.vue +215 -28
- package/src/components/RsContextMenu.vue +51 -13
- package/src/components/RsDatePicker.vue +267 -34
- package/src/components/RsDateTimePicker.vue +19 -2
- package/src/components/RsDescriptions.vue +186 -0
- package/src/components/RsDescriptionsItem.vue +32 -0
- package/src/components/RsDialog.vue +363 -51
- package/src/components/RsDivider.vue +89 -0
- package/src/components/RsDrawer.vue +558 -33
- package/src/components/RsDropdown.vue +33 -2
- package/src/components/RsDynamicTags.vue +517 -0
- package/src/components/RsForm.vue +97 -8
- package/src/components/RsInput.vue +80 -82
- package/src/components/RsInputNumber.vue +64 -23
- package/src/components/RsLabel.vue +20 -5
- package/src/components/RsLoadingBar.vue +151 -0
- package/src/components/RsMenu.vue +73 -15
- package/src/components/RsMenuItems.vue +147 -9
- package/src/components/RsPagination.vue +93 -14
- package/src/components/RsRadio.vue +81 -0
- package/src/components/RsRadioItem.vue +156 -0
- package/src/components/RsSelect.vue +67 -58
- package/src/components/RsSplitPane.vue +69 -1
- package/src/components/RsSwitch.vue +179 -0
- package/src/components/RsTable.vue +653 -2555
- package/src/components/RsTabs.vue +415 -83
- package/src/components/RsTag.vue +191 -0
- package/src/components/RsTerminal.vue +3 -3
- package/src/components/RsTimePicker.vue +44 -5
- package/src/components/RsTimePickerColumns.vue +136 -77
- package/src/components/RsToaster.vue +58 -18
- package/src/components/RsTree.vue +39 -21
- package/src/components/RsUpload.vue +145 -12
- package/src/components/RsVNodeHost.vue +20 -0
- package/src/components/button-utils.ts +39 -0
- package/src/components/context-menu-utils.ts +6 -0
- package/src/components/date-picker-utils.ts +84 -4
- package/src/components/descriptions-utils.ts +24 -0
- package/src/components/dialog-utils.ts +147 -0
- package/src/components/dialog-window.ts +74 -9
- package/src/components/drawer-utils.ts +73 -0
- package/src/components/form-rules.ts +257 -0
- package/src/components/form-utils.ts +52 -2
- package/src/components/menu-utils.ts +11 -0
- package/src/components/overlay-utils.ts +3 -0
- package/src/components/radio-utils.ts +12 -0
- package/src/components/reka.ts +5 -0
- package/src/components/select-utils.ts +21 -2
- package/src/components/split-pane-utils.ts +65 -12
- package/src/components/table/RsTableBody.vue +217 -0
- package/src/components/table/RsTableBodyRow.vue +90 -1
- package/src/components/table/RsTableColGroup.vue +41 -0
- package/src/components/table/RsTableHeader.vue +158 -0
- package/src/components/table/RsTableSummaryRow.vue +52 -0
- package/src/components/table/rs-table-api.ts +99 -0
- package/src/components/table/rs-table-compat-matrix.ts +71 -0
- package/src/components/table/rs-table-echarts-adapter.ts +81 -0
- package/src/components/table/rs-table-feature-host.ts +178 -0
- package/src/components/table/rs-table-grid-nav.ts +122 -0
- package/src/components/table/rs-table-header-types.ts +47 -0
- package/src/components/table/rs-table-module-registry.ts +72 -0
- package/src/components/table/rs-table-props.ts +274 -0
- package/src/components/table/rs-table-view-context.ts +258 -0
- package/src/components/table/rs-table.css +84 -6
- package/src/components/table/table-chart-utils.ts +165 -0
- package/src/components/table/table-features.ts +284 -0
- package/src/components/table/table-summary-utils.ts +194 -0
- package/src/components/table-utils.ts +452 -2
- package/src/components/tabs-utils.ts +142 -2
- package/src/components/terminal-utils.ts +1 -1
- package/src/components/terminal-wheel.ts +1 -1
- package/src/components/time-picker-utils.ts +76 -3
- package/src/components/upload-utils.ts +122 -0
- package/src/components/virtual-list-utils.ts +15 -2
- package/src/composables/assembleRsTableApi.ts +79 -0
- package/src/composables/bindRsTableViewContext.ts +269 -0
- package/src/composables/createRsDialog.ts +357 -0
- package/src/composables/createRsTableEditEmitBridge.ts +38 -0
- package/src/composables/useRsLoadingBar.ts +36 -0
- package/src/composables/useRsTable.ts +134 -0
- package/src/composables/useRsTableA11y.ts +52 -0
- package/src/composables/useRsTableChartBridge.ts +79 -0
- package/src/composables/useRsTableColumnLayout.ts +320 -0
- package/src/composables/useRsTableColumnResize.ts +244 -0
- package/src/composables/useRsTableColumnVirtual.ts +180 -0
- package/src/composables/useRsTableColumns.ts +200 -0
- package/src/composables/useRsTableContextMenu.ts +257 -0
- package/src/composables/useRsTableCore.ts +256 -0
- package/src/composables/useRsTableEditActions.ts +606 -0
- package/src/composables/useRsTableEditLayer.ts +251 -0
- package/src/composables/useRsTableEngine.ts +357 -0
- package/src/composables/useRsTableGridKeyboard.ts +123 -0
- package/src/composables/useRsTableHeadless.ts +137 -0
- package/src/composables/useRsTableInteraction.ts +433 -0
- package/src/composables/useRsTableScrollHost.ts +75 -0
- package/src/composables/useRsTableScrollLayout.ts +255 -0
- package/src/composables/useRsTableSelectionSource.ts +89 -0
- package/src/composables/useRsTableShell.ts +416 -0
- package/src/composables/useRsTableShellChrome.ts +170 -0
- package/src/composables/useRsTableSummary.ts +113 -0
- package/src/composables/useRsTableViewProvide.ts +263 -0
- package/src/composables/useRsTableVirtual.ts +154 -0
- package/src/composables/useRsTabsNav.ts +4 -0
- package/src/composables/useTableEdit.ts +1 -1
- package/src/icons/lucide.ts +2 -2
- package/src/index.ts +309 -9
- package/src/locale/messages.ts +30 -0
- package/src/styles.css +305 -52
- package/src/theme/brand.example.css +21 -2
- package/src/theme/presets.ts +4 -0
- package/src/theme/types.ts +67 -0
- package/src/components/RsTableColgroup.vue +0 -17
- package/src/components/RsTableHeaderRow.vue +0 -30
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RsTable 公开 API 契约(对标 AG Grid GridApi / TanStack table 实例)。
|
|
3
|
+
*
|
|
4
|
+
* 设计原则(大厂共性):
|
|
5
|
+
* - 业务与第三方只依赖本接口,不依赖 SFC 内部变量
|
|
6
|
+
* - 加性演进:新增方法用可选字段或 bump RS_TABLE_API_VERSION
|
|
7
|
+
* - 查询与命令分离:getXxx 无副作用;setXxx / commitXxx 为命令
|
|
8
|
+
*
|
|
9
|
+
* 兼容承诺:同主版本内不删除、不改语义已有方法签名。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { RsTableStagedCell } from '../../composables/useTableEdit'
|
|
13
|
+
import type { RsTableAnalyticsSnapshot } from '../../composables/useRsTableSelectionSource'
|
|
14
|
+
import type { RsTableColumn, RsTableRowData } from '../table-utils'
|
|
15
|
+
|
|
16
|
+
/** 公开 API 语义版本(与 package 版本解耦,专指 TableApi 契约) */
|
|
17
|
+
export const RS_TABLE_API_VERSION = '1.0.0' as const
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 表格实例 API:ref / expose / provide / inject 共用同一形状。
|
|
21
|
+
*/
|
|
22
|
+
export interface RsTableApi<T extends RsTableRowData = RsTableRowData> {
|
|
23
|
+
readonly apiVersion: typeof RS_TABLE_API_VERSION
|
|
24
|
+
|
|
25
|
+
// -- 行查询 --
|
|
26
|
+
getRowByKey: (rowKey: string) => T | undefined
|
|
27
|
+
getRowByIndex: (index: number) => T | undefined
|
|
28
|
+
getColumnValues: (colKey: string) => unknown[]
|
|
29
|
+
getCellValue: (rowKey: string, colKey: string) => unknown
|
|
30
|
+
|
|
31
|
+
// -- 视图 / 选中(分析与图表真源)--
|
|
32
|
+
/** 筛选/排序后的业务行(不含 group/expand 伪行) */
|
|
33
|
+
getViewRows: () => T[]
|
|
34
|
+
getSelectedRows: () => T[]
|
|
35
|
+
getSelectedRowKeys: () => string[]
|
|
36
|
+
getAnalyticsSnapshot: () => RsTableAnalyticsSnapshot<T>
|
|
37
|
+
subscribeAnalytics: (listener: (snap: RsTableAnalyticsSnapshot<T>) => void) => () => void
|
|
38
|
+
|
|
39
|
+
// -- 高亮 --
|
|
40
|
+
getHighlightedRowKey: () => string | undefined
|
|
41
|
+
setHighlightedRowKey: (key: string | undefined) => void
|
|
42
|
+
|
|
43
|
+
// -- 编辑 --
|
|
44
|
+
cancelCellEdit: () => void
|
|
45
|
+
cancelAllEdits: () => void
|
|
46
|
+
getDirtyCellKeys: () => string[]
|
|
47
|
+
getCellError: (rowKey: string, colKey: string) => string | undefined
|
|
48
|
+
setCellError: (rowKey: string, colKey: string, message: string | null) => void
|
|
49
|
+
stageCell: (cell: RsTableStagedCell) => void
|
|
50
|
+
rejectRowEdit: (rowKey: string, reason?: string) => void
|
|
51
|
+
undoEdit: () => void
|
|
52
|
+
redoEdit: () => void
|
|
53
|
+
commitRowEdits: (rowKey: string) => void
|
|
54
|
+
rollbackRowEdits: (rowKey: string) => void
|
|
55
|
+
|
|
56
|
+
// -- 扩展元数据(可选,便于遥测)--
|
|
57
|
+
getActiveFeatureIds?: () => string[]
|
|
58
|
+
/** 实例 Feature 贡献的工具条项 */
|
|
59
|
+
getToolbarItems?: () => import('./rs-table-feature-host').RsTableToolbarItem[]
|
|
60
|
+
/** 实例 Feature 贡献的 overlay 描述 */
|
|
61
|
+
getOverlayContributions?: () => import('./rs-table-feature-host').RsTableOverlayContribution[]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** provide/inject 键:子组件或插槽内拿表格 API */
|
|
65
|
+
export const RS_TABLE_API_KEY: unique symbol = Symbol('rsTableApi')
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 由 RsTable 内部装配公开 API(单一出口,避免 expose 与 inject 漂移)。
|
|
69
|
+
*/
|
|
70
|
+
export function createRsTableApi<T extends RsTableRowData>(
|
|
71
|
+
impl: Omit<RsTableApi<T>, 'apiVersion'> & { apiVersion?: typeof RS_TABLE_API_VERSION },
|
|
72
|
+
): RsTableApi<T> {
|
|
73
|
+
return {
|
|
74
|
+
apiVersion: impl.apiVersion ?? RS_TABLE_API_VERSION,
|
|
75
|
+
...impl,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 类型守卫:判断未知对象是否像 RsTableApi */
|
|
80
|
+
export function isRsTableApi<T extends RsTableRowData = RsTableRowData>(
|
|
81
|
+
value: unknown,
|
|
82
|
+
): value is RsTableApi<T> {
|
|
83
|
+
if (!value || typeof value !== 'object') return false
|
|
84
|
+
const api = value as Partial<RsTableApi<T>>
|
|
85
|
+
return (
|
|
86
|
+
typeof api.getViewRows === 'function' &&
|
|
87
|
+
typeof api.getSelectedRows === 'function' &&
|
|
88
|
+
typeof api.getAnalyticsSnapshot === 'function'
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 列定义辅助:标记某列参与图表分类(文档/约定,不强制运行时) */
|
|
93
|
+
export type RsTableColumnChartMeta = {
|
|
94
|
+
chartCategory?: boolean
|
|
95
|
+
chartValue?: boolean
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type RsTableColumnWithChartMeta<T extends RsTableRowData = RsTableRowData> = RsTableColumn<T> &
|
|
99
|
+
RsTableColumnChartMeta
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RsTable 公开契约兼容矩阵(单一真源)。
|
|
3
|
+
*
|
|
4
|
+
* - 测试锁定这些键:删除/改名 = 失败,须 bump RS_TABLE_API_VERSION
|
|
5
|
+
* - 文档与 CI 共用,避免矩阵与实现漂移
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { RS_TABLE_API_VERSION } from './rs-table-api'
|
|
9
|
+
import { RS_TABLE_PROP_DEFAULTS } from './rs-table-props'
|
|
10
|
+
|
|
11
|
+
/** 当前锁定的 API 契约版本(与 createRsTableApi 一致) */
|
|
12
|
+
export const RS_TABLE_COMPAT_API_VERSION = RS_TABLE_API_VERSION
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 1.0.x 必选 Api 方法(加性可选方法不在此列)。
|
|
16
|
+
* 删减任一项视为 breaking。
|
|
17
|
+
*/
|
|
18
|
+
export const RS_TABLE_API_REQUIRED_METHODS = [
|
|
19
|
+
'getRowByKey',
|
|
20
|
+
'getRowByIndex',
|
|
21
|
+
'getColumnValues',
|
|
22
|
+
'getCellValue',
|
|
23
|
+
'getViewRows',
|
|
24
|
+
'getSelectedRows',
|
|
25
|
+
'getSelectedRowKeys',
|
|
26
|
+
'getAnalyticsSnapshot',
|
|
27
|
+
'subscribeAnalytics',
|
|
28
|
+
'getHighlightedRowKey',
|
|
29
|
+
'setHighlightedRowKey',
|
|
30
|
+
'cancelCellEdit',
|
|
31
|
+
'cancelAllEdits',
|
|
32
|
+
'getDirtyCellKeys',
|
|
33
|
+
'getCellError',
|
|
34
|
+
'setCellError',
|
|
35
|
+
'stageCell',
|
|
36
|
+
'rejectRowEdit',
|
|
37
|
+
'undoEdit',
|
|
38
|
+
'redoEdit',
|
|
39
|
+
'commitRowEdits',
|
|
40
|
+
'rollbackRowEdits',
|
|
41
|
+
] as const
|
|
42
|
+
|
|
43
|
+
/** 1.0.x 加性可选 Api(可缺省,但若存在须为函数) */
|
|
44
|
+
export const RS_TABLE_API_OPTIONAL_METHODS = [
|
|
45
|
+
'getActiveFeatureIds',
|
|
46
|
+
'getToolbarItems',
|
|
47
|
+
'getOverlayContributions',
|
|
48
|
+
] as const
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 稳定 emit 名(同主版本不删)。
|
|
52
|
+
* 新增 emit 可追加到测试期望的超集校验。
|
|
53
|
+
*/
|
|
54
|
+
export const RS_TABLE_STABLE_EMITS = [
|
|
55
|
+
'rowClick',
|
|
56
|
+
'rowDblclick',
|
|
57
|
+
'cellView',
|
|
58
|
+
'update:selectedRowKeys',
|
|
59
|
+
'selectionChange',
|
|
60
|
+
'loadMore',
|
|
61
|
+
'cellEditCommit',
|
|
62
|
+
'cellEditCancel',
|
|
63
|
+
'rowEditCommit',
|
|
64
|
+
'rowEditRollback',
|
|
65
|
+
] as const
|
|
66
|
+
|
|
67
|
+
/** 带默认值的 props 键(与 RS_TABLE_PROP_DEFAULTS 同步校验) */
|
|
68
|
+
export const RS_TABLE_DEFAULTED_PROP_KEYS = Object.keys(RS_TABLE_PROP_DEFAULTS).sort()
|
|
69
|
+
|
|
70
|
+
export type RsTableApiRequiredMethod = (typeof RS_TABLE_API_REQUIRED_METHODS)[number]
|
|
71
|
+
export type RsTableApiOptionalMethod = (typeof RS_TABLE_API_OPTIONAL_METHODS)[number]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RsTable 系列 → ECharts option 纯映射(不依赖 echarts 包)。
|
|
3
|
+
*
|
|
4
|
+
* 业务侧:`import('echarts').then(e => e.init(el).setOption(option))`
|
|
5
|
+
* 内核保持库无关;本文件只产出普通 JSON 可序列化结构。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { RsTableChartSeries } from './table-chart-utils'
|
|
9
|
+
|
|
10
|
+
/** 最小 ECharts option 面(刻意放宽,避免绑定具体 echarts 类型版本) */
|
|
11
|
+
export type RsTableEChartsOption = {
|
|
12
|
+
tooltip?: { trigger?: string }
|
|
13
|
+
legend?: { data?: string[] }
|
|
14
|
+
xAxis?: { type?: string; data?: string[] } | Array<{ type?: string; data?: string[] }>
|
|
15
|
+
yAxis?: { type?: string } | Array<{ type?: string }>
|
|
16
|
+
series: Array<{
|
|
17
|
+
id?: string
|
|
18
|
+
name?: string
|
|
19
|
+
type: 'bar' | 'line' | 'pie'
|
|
20
|
+
data: Array<number | { name: string; value: number }>
|
|
21
|
+
}>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MapRsTableSeriesToEChartsOptions {
|
|
25
|
+
/** 多系列时是否共用类目轴(默认 true;饼图忽略) */
|
|
26
|
+
sharedCategoryAxis?: boolean
|
|
27
|
+
/** tooltip.trigger,默认按首系列推断 */
|
|
28
|
+
tooltipTrigger?: 'item' | 'axis'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function mapKind(kind: RsTableChartSeries['kind']): 'bar' | 'line' | 'pie' {
|
|
32
|
+
if (kind === 'pie') return 'pie'
|
|
33
|
+
if (kind === 'line') return 'line'
|
|
34
|
+
return 'bar'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 将库无关 series 映射为可直接 setOption 的 ECharts option。
|
|
39
|
+
*/
|
|
40
|
+
export function mapRsTableSeriesToEChartsOption(
|
|
41
|
+
seriesList: readonly RsTableChartSeries[],
|
|
42
|
+
options: MapRsTableSeriesToEChartsOptions = {},
|
|
43
|
+
): RsTableEChartsOption {
|
|
44
|
+
if (!seriesList.length) {
|
|
45
|
+
return { series: [] }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const allPie = seriesList.every((s) => s.kind === 'pie')
|
|
49
|
+
const tooltipTrigger =
|
|
50
|
+
options.tooltipTrigger ?? (allPie ? 'item' : 'axis')
|
|
51
|
+
|
|
52
|
+
if (allPie) {
|
|
53
|
+
return {
|
|
54
|
+
tooltip: { trigger: tooltipTrigger },
|
|
55
|
+
legend: { data: seriesList.flatMap((s) => s.categories) },
|
|
56
|
+
series: seriesList.map((s) => ({
|
|
57
|
+
id: s.id,
|
|
58
|
+
name: s.id,
|
|
59
|
+
type: 'pie' as const,
|
|
60
|
+
data: s.points.map((p) => ({ name: p.name, value: p.value })),
|
|
61
|
+
})),
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const shared = options.sharedCategoryAxis !== false
|
|
66
|
+
const primary = seriesList[0]!
|
|
67
|
+
return {
|
|
68
|
+
tooltip: { trigger: tooltipTrigger },
|
|
69
|
+
legend: { data: seriesList.map((s) => s.id) },
|
|
70
|
+
xAxis: shared
|
|
71
|
+
? { type: 'category', data: primary.categories }
|
|
72
|
+
: seriesList.map((s) => ({ type: 'category', data: s.categories })),
|
|
73
|
+
yAxis: { type: 'value' },
|
|
74
|
+
series: seriesList.map((s) => ({
|
|
75
|
+
id: s.id,
|
|
76
|
+
name: s.id,
|
|
77
|
+
type: mapKind(s.kind),
|
|
78
|
+
data: s.values,
|
|
79
|
+
})),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature 运行时宿主:贡献袋 + 生命周期(对标可插拔中间件总线)。
|
|
3
|
+
*
|
|
4
|
+
* 贡献点(实例级,多表互不串扰):
|
|
5
|
+
* - contextMenu:右键菜单
|
|
6
|
+
* - toolbar:表头工具条项
|
|
7
|
+
* - overlay:浮层/旁路 UI 标记(图表容器挂载点等)
|
|
8
|
+
*
|
|
9
|
+
* 每个 RsTable 各自 createRsTableFeatureHost();不要跨实例复用 host。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { RsContextMenuItem } from '../context-menu-utils'
|
|
13
|
+
import type { RsTableRowData } from '../table-utils'
|
|
14
|
+
import type { RsTableAnalyticsSnapshot } from '../../composables/useRsTableSelectionSource'
|
|
15
|
+
import type { RsTableFeature, RsTableFeatureContext } from './table-features'
|
|
16
|
+
import { setupTableFeatures } from './table-features'
|
|
17
|
+
|
|
18
|
+
/** 动态右键菜单贡献函数 */
|
|
19
|
+
export type RsTableContextMenuContributor<T extends RsTableRowData> = (
|
|
20
|
+
items: RsContextMenuItem[],
|
|
21
|
+
ctx: { row: T | null; selectedRows: T[] },
|
|
22
|
+
) => void
|
|
23
|
+
|
|
24
|
+
/** 工具条贡献项 */
|
|
25
|
+
export interface RsTableToolbarItem {
|
|
26
|
+
key: string
|
|
27
|
+
label: string
|
|
28
|
+
icon?: string
|
|
29
|
+
disabled?: boolean
|
|
30
|
+
onClick?: () => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type RsTableToolbarContributor = (
|
|
34
|
+
items: RsTableToolbarItem[],
|
|
35
|
+
) => void
|
|
36
|
+
|
|
37
|
+
/** Overlay 贡献(旁路挂载描述,不直接操作 DOM) */
|
|
38
|
+
export interface RsTableOverlayContribution {
|
|
39
|
+
key: string
|
|
40
|
+
/** 业务自定义渲染/挂载标识 */
|
|
41
|
+
slot?: string
|
|
42
|
+
meta?: Record<string, unknown>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type RsTableOverlayContributor = (
|
|
46
|
+
items: RsTableOverlayContribution[],
|
|
47
|
+
) => void
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 单次 setup 周期内的贡献收集器(实例私有)。
|
|
51
|
+
*/
|
|
52
|
+
export interface RsTableFeatureHost<T extends RsTableRowData = RsTableRowData> {
|
|
53
|
+
createContext: (base: {
|
|
54
|
+
getViewRows: () => T[]
|
|
55
|
+
getSelectedRows: () => T[]
|
|
56
|
+
getAnalyticsSnapshot: () => RsTableAnalyticsSnapshot<T>
|
|
57
|
+
subscribeAnalytics: (listener: (snap: RsTableAnalyticsSnapshot<T>) => void) => () => void
|
|
58
|
+
}) => RsTableFeatureContext<T>
|
|
59
|
+
setup: (features: RsTableFeature<T>[], ctx: RsTableFeatureContext<T>) => () => void
|
|
60
|
+
mergeContextMenuItems: (
|
|
61
|
+
baseItems: RsContextMenuItem[],
|
|
62
|
+
menuCtx: { row: T | null; selectedRows: T[] },
|
|
63
|
+
) => RsContextMenuItem[]
|
|
64
|
+
/** 合并 toolbar 贡献 */
|
|
65
|
+
getToolbarItems: () => RsTableToolbarItem[]
|
|
66
|
+
/** 合并 overlay 贡献 */
|
|
67
|
+
getOverlayContributions: () => RsTableOverlayContribution[]
|
|
68
|
+
getActiveFeatureIds: () => string[]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 创建 Feature 宿主(每个 RsTable 实例一个 —— 多表请各建各的)。
|
|
73
|
+
*/
|
|
74
|
+
export function createRsTableFeatureHost<T extends RsTableRowData>(): RsTableFeatureHost<T> {
|
|
75
|
+
let menuContributors: Array<RsTableContextMenuContributor<T>> = []
|
|
76
|
+
let toolbarContributors: RsTableToolbarContributor[] = []
|
|
77
|
+
let overlayContributors: RsTableOverlayContributor[] = []
|
|
78
|
+
let activeIds: string[] = []
|
|
79
|
+
|
|
80
|
+
function createContext(base: {
|
|
81
|
+
getViewRows: () => T[]
|
|
82
|
+
getSelectedRows: () => T[]
|
|
83
|
+
getAnalyticsSnapshot: () => RsTableAnalyticsSnapshot<T>
|
|
84
|
+
subscribeAnalytics: (listener: (snap: RsTableAnalyticsSnapshot<T>) => void) => () => void
|
|
85
|
+
}): RsTableFeatureContext<T> {
|
|
86
|
+
return {
|
|
87
|
+
getViewRows: base.getViewRows,
|
|
88
|
+
getSelectedRows: base.getSelectedRows,
|
|
89
|
+
getAnalyticsSnapshot: base.getAnalyticsSnapshot,
|
|
90
|
+
subscribeAnalytics: base.subscribeAnalytics,
|
|
91
|
+
contributeContextMenuItems(contributor) {
|
|
92
|
+
if (typeof contributor === 'function') {
|
|
93
|
+
menuContributors.push(contributor)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
const staticItems = contributor
|
|
97
|
+
menuContributors.push((items) => {
|
|
98
|
+
items.push(...staticItems)
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
contributeToolbarItems(contributor) {
|
|
102
|
+
if (typeof contributor === 'function') {
|
|
103
|
+
toolbarContributors.push(contributor)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
const staticItems = contributor
|
|
107
|
+
toolbarContributors.push((items) => {
|
|
108
|
+
items.push(...staticItems)
|
|
109
|
+
})
|
|
110
|
+
},
|
|
111
|
+
contributeOverlay(contributor) {
|
|
112
|
+
if (typeof contributor === 'function') {
|
|
113
|
+
overlayContributors.push(contributor)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
const staticItems = contributor
|
|
117
|
+
overlayContributors.push((items) => {
|
|
118
|
+
items.push(...staticItems)
|
|
119
|
+
})
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function setup(features: RsTableFeature<T>[], ctx: RsTableFeatureContext<T>): () => void {
|
|
125
|
+
menuContributors = []
|
|
126
|
+
toolbarContributors = []
|
|
127
|
+
overlayContributors = []
|
|
128
|
+
activeIds = features.map((f) => f.id)
|
|
129
|
+
const dispose = setupTableFeatures(features, ctx)
|
|
130
|
+
return () => {
|
|
131
|
+
dispose()
|
|
132
|
+
menuContributors = []
|
|
133
|
+
toolbarContributors = []
|
|
134
|
+
overlayContributors = []
|
|
135
|
+
activeIds = []
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function mergeContextMenuItems(
|
|
140
|
+
baseItems: RsContextMenuItem[],
|
|
141
|
+
menuCtx: { row: T | null; selectedRows: T[] },
|
|
142
|
+
): RsContextMenuItem[] {
|
|
143
|
+
const items = [...baseItems]
|
|
144
|
+
const before = items.length
|
|
145
|
+
for (const contribute of menuContributors) {
|
|
146
|
+
contribute(items, menuCtx)
|
|
147
|
+
}
|
|
148
|
+
if (before > 0 && items.length > before && !items[before]?.separator) {
|
|
149
|
+
items.splice(before, 0, { key: '__ctx-feature-sep', label: '', separator: true })
|
|
150
|
+
}
|
|
151
|
+
return items
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getToolbarItems(): RsTableToolbarItem[] {
|
|
155
|
+
const items: RsTableToolbarItem[] = []
|
|
156
|
+
for (const contribute of toolbarContributors) contribute(items)
|
|
157
|
+
return items
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function getOverlayContributions(): RsTableOverlayContribution[] {
|
|
161
|
+
const items: RsTableOverlayContribution[] = []
|
|
162
|
+
for (const contribute of overlayContributors) contribute(items)
|
|
163
|
+
return items
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getActiveFeatureIds(): string[] {
|
|
167
|
+
return [...activeIds]
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
createContext,
|
|
172
|
+
setup,
|
|
173
|
+
mergeContextMenuItems,
|
|
174
|
+
getToolbarItems,
|
|
175
|
+
getOverlayContributions,
|
|
176
|
+
getActiveFeatureIds,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARIA Grid 单元格导航(矩形行列模型,不限于可编辑格)。
|
|
3
|
+
* 对标 Excel / AG Grid 漫游:箭头、Home/End、Ctrl+Home/End、PageUp/PageDown。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type RsTableGridNavDirection =
|
|
7
|
+
| 'left'
|
|
8
|
+
| 'right'
|
|
9
|
+
| 'up'
|
|
10
|
+
| 'down'
|
|
11
|
+
| 'home'
|
|
12
|
+
| 'end'
|
|
13
|
+
| 'first'
|
|
14
|
+
| 'last'
|
|
15
|
+
| 'pageUp'
|
|
16
|
+
| 'pageDown'
|
|
17
|
+
|
|
18
|
+
export interface RsTableGridCellRef {
|
|
19
|
+
rowKey: string
|
|
20
|
+
colKey: string
|
|
21
|
+
rowIndex: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 在「行列表 × 列 key」矩形上移动焦点。
|
|
26
|
+
* rows 按视觉顺序;colKeys 为可见数据列顺序。
|
|
27
|
+
*/
|
|
28
|
+
export function navigateGridCell(
|
|
29
|
+
rows: Array<{ rowKey: string; rowIndex: number }>,
|
|
30
|
+
colKeys: string[],
|
|
31
|
+
current: RsTableGridCellRef | null,
|
|
32
|
+
direction: RsTableGridNavDirection,
|
|
33
|
+
pageSize = 10,
|
|
34
|
+
): RsTableGridCellRef | null {
|
|
35
|
+
if (!rows.length || !colKeys.length) return null
|
|
36
|
+
|
|
37
|
+
const seed = current ?? {
|
|
38
|
+
rowKey: rows[0]!.rowKey,
|
|
39
|
+
colKey: colKeys[0]!,
|
|
40
|
+
rowIndex: rows[0]!.rowIndex,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let rowPos = rows.findIndex((r) => r.rowKey === seed.rowKey)
|
|
44
|
+
if (rowPos < 0) rowPos = 0
|
|
45
|
+
let colPos = colKeys.indexOf(seed.colKey)
|
|
46
|
+
if (colPos < 0) colPos = 0
|
|
47
|
+
|
|
48
|
+
const lastRow = rows.length - 1
|
|
49
|
+
const lastCol = colKeys.length - 1
|
|
50
|
+
const step = Math.max(1, pageSize)
|
|
51
|
+
|
|
52
|
+
switch (direction) {
|
|
53
|
+
case 'left':
|
|
54
|
+
colPos = Math.max(0, colPos - 1)
|
|
55
|
+
break
|
|
56
|
+
case 'right':
|
|
57
|
+
colPos = Math.min(lastCol, colPos + 1)
|
|
58
|
+
break
|
|
59
|
+
case 'up':
|
|
60
|
+
rowPos = Math.max(0, rowPos - 1)
|
|
61
|
+
break
|
|
62
|
+
case 'down':
|
|
63
|
+
rowPos = Math.min(lastRow, rowPos + 1)
|
|
64
|
+
break
|
|
65
|
+
case 'home':
|
|
66
|
+
colPos = 0
|
|
67
|
+
break
|
|
68
|
+
case 'end':
|
|
69
|
+
colPos = lastCol
|
|
70
|
+
break
|
|
71
|
+
case 'first':
|
|
72
|
+
rowPos = 0
|
|
73
|
+
colPos = 0
|
|
74
|
+
break
|
|
75
|
+
case 'last':
|
|
76
|
+
rowPos = lastRow
|
|
77
|
+
colPos = lastCol
|
|
78
|
+
break
|
|
79
|
+
case 'pageUp':
|
|
80
|
+
rowPos = Math.max(0, rowPos - step)
|
|
81
|
+
break
|
|
82
|
+
case 'pageDown':
|
|
83
|
+
rowPos = Math.min(lastRow, rowPos + step)
|
|
84
|
+
break
|
|
85
|
+
default:
|
|
86
|
+
break
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const row = rows[rowPos]!
|
|
90
|
+
return {
|
|
91
|
+
rowKey: row.rowKey,
|
|
92
|
+
colKey: colKeys[colPos]!,
|
|
93
|
+
rowIndex: row.rowIndex,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 将 KeyboardEvent 映射为网格导航方向;无法识别则返回 null。
|
|
99
|
+
*/
|
|
100
|
+
export function resolveGridNavDirection(event: KeyboardEvent): RsTableGridNavDirection | null {
|
|
101
|
+
const mod = event.ctrlKey || event.metaKey
|
|
102
|
+
switch (event.key) {
|
|
103
|
+
case 'ArrowLeft':
|
|
104
|
+
return 'left'
|
|
105
|
+
case 'ArrowRight':
|
|
106
|
+
return 'right'
|
|
107
|
+
case 'ArrowUp':
|
|
108
|
+
return 'up'
|
|
109
|
+
case 'ArrowDown':
|
|
110
|
+
return 'down'
|
|
111
|
+
case 'Home':
|
|
112
|
+
return mod ? 'first' : 'home'
|
|
113
|
+
case 'End':
|
|
114
|
+
return mod ? 'last' : 'end'
|
|
115
|
+
case 'PageUp':
|
|
116
|
+
return 'pageUp'
|
|
117
|
+
case 'PageDown':
|
|
118
|
+
return 'pageDown'
|
|
119
|
+
default:
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RsTableHeader 视图契约(对标列头渲染层,与 Engine 解耦)。
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CSSProperties } from 'vue'
|
|
6
|
+
import type { RsTableColumn, RsTableRowData } from '../table-utils'
|
|
7
|
+
|
|
8
|
+
export interface RsTableHeaderProps<T extends RsTableRowData = RsTableRowData> {
|
|
9
|
+
showRowDragHandle: boolean
|
|
10
|
+
detailExpandable: boolean
|
|
11
|
+
showSelectColumn: boolean
|
|
12
|
+
isRadioSelection: boolean
|
|
13
|
+
selectAllState: 'checked' | 'indeterminate' | 'unchecked'
|
|
14
|
+
showEditGutterColumn: boolean
|
|
15
|
+
showIndexColumn: boolean
|
|
16
|
+
showRowStatusColumn: boolean
|
|
17
|
+
columnPadLeft: number
|
|
18
|
+
columnPadRight: number
|
|
19
|
+
visibleDataColumns: RsTableColumn<T>[]
|
|
20
|
+
columnDraggable: boolean
|
|
21
|
+
resizable: boolean
|
|
22
|
+
multiSort: boolean
|
|
23
|
+
columnFiltersState: Record<string, string>
|
|
24
|
+
dragColumnKey: string | null
|
|
25
|
+
dropColumnKey: string | null
|
|
26
|
+
dragLeadHeaderStyle: CSSProperties | Record<string, string> | undefined
|
|
27
|
+
expandLeadHeaderStyle: CSSProperties | Record<string, string> | undefined
|
|
28
|
+
selectLeadHeaderStyle: CSSProperties | Record<string, string> | undefined
|
|
29
|
+
gutterLeadHeaderStyle: CSSProperties | Record<string, string> | undefined
|
|
30
|
+
indexLeadHeaderStyle: CSSProperties | Record<string, string> | undefined
|
|
31
|
+
columnHeaderStyleMap: Map<string, Record<string, string> | undefined>
|
|
32
|
+
columnFilterValue: (key: string) => string
|
|
33
|
+
isColumnFilterActive: (filters: Record<string, string>, key: string) => boolean
|
|
34
|
+
sortOrderFor: (key: string) => string | null | undefined
|
|
35
|
+
sortPriorityFor: (key: string) => number
|
|
36
|
+
sortIconName: (key: string) => string
|
|
37
|
+
labels: {
|
|
38
|
+
selectAll: string
|
|
39
|
+
index: string
|
|
40
|
+
rowStatus: string
|
|
41
|
+
dragColumn: string
|
|
42
|
+
filterColumn: string
|
|
43
|
+
filterPlaceholder: string
|
|
44
|
+
filterClear: string
|
|
45
|
+
filterApply: string
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RsTable 模块注册表(对标 AG Grid ModuleRegistry)。
|
|
3
|
+
*
|
|
4
|
+
* - 全局 register:所有表格实例默认挂载(同页多表会共享这些模块)
|
|
5
|
+
* - 实例 props.features:仅该实例;同 id 覆盖全局
|
|
6
|
+
* - 不改内核渲染;模块通过 Feature.setup 订阅/贡献
|
|
7
|
+
* - 多表隔离要点:选中/编辑/ViewContext 仍是实例私有;仅 Registry 是全局点
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { RsTableFeature } from './table-features'
|
|
11
|
+
import type { RsTableRowData } from '../table-utils'
|
|
12
|
+
|
|
13
|
+
const globalModules: RsTableFeature<RsTableRowData>[] = []
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 全局模块注册表。
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* RsTableModuleRegistry.register(
|
|
21
|
+
* createAnalyticsTableFeature({ onSnapshot: (s) => syncCharts(s) }),
|
|
22
|
+
* )
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const RsTableModuleRegistry = {
|
|
26
|
+
/**
|
|
27
|
+
* 注册全局 feature(幂等:同 id 覆盖)。
|
|
28
|
+
*/
|
|
29
|
+
register<T extends RsTableRowData>(feature: RsTableFeature<T>): void {
|
|
30
|
+
const id = feature.id
|
|
31
|
+
const idx = globalModules.findIndex((item) => item.id === id)
|
|
32
|
+
const next = feature as RsTableFeature<RsTableRowData>
|
|
33
|
+
if (idx >= 0) globalModules[idx] = next
|
|
34
|
+
else globalModules.push(next)
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/** 按 id 注销全局模块 */
|
|
38
|
+
unregister(id: string): void {
|
|
39
|
+
const idx = globalModules.findIndex((item) => item.id === id)
|
|
40
|
+
if (idx >= 0) globalModules.splice(idx, 1)
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/** 清空(测试用) */
|
|
44
|
+
clear(): void {
|
|
45
|
+
globalModules.length = 0
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/** 当前全局模块快照(浅拷贝) */
|
|
49
|
+
getModules<T extends RsTableRowData = RsTableRowData>(): RsTableFeature<T>[] {
|
|
50
|
+
return [...globalModules] as RsTableFeature<T>[]
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
listIds(): string[] {
|
|
54
|
+
return globalModules.map((item) => item.id)
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 合并全局模块 + 实例 features(实例同 id 覆盖全局)。
|
|
60
|
+
*/
|
|
61
|
+
export function resolveInstanceFeatures<T extends RsTableRowData>(
|
|
62
|
+
instanceFeatures?: RsTableFeature<T>[] | null,
|
|
63
|
+
): RsTableFeature<T>[] {
|
|
64
|
+
const map = new Map<string, RsTableFeature<T>>()
|
|
65
|
+
for (const feature of RsTableModuleRegistry.getModules<T>()) {
|
|
66
|
+
map.set(feature.id, feature)
|
|
67
|
+
}
|
|
68
|
+
for (const feature of instanceFeatures ?? []) {
|
|
69
|
+
map.set(feature.id, feature)
|
|
70
|
+
}
|
|
71
|
+
return [...map.values()]
|
|
72
|
+
}
|