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.
- package/CHANGELOG.md +65 -1
- package/package.json +10 -2
- 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__/RsDatePicker.spec.ts +21 -0
- package/src/__tests__/RsDateTimePicker.spec.ts +11 -0
- package/src/__tests__/RsDescriptions.spec.ts +50 -0
- package/src/__tests__/RsDialog.spec.ts +178 -0
- package/src/__tests__/RsDrawer.spec.ts +96 -3
- package/src/__tests__/RsDynamicTags.spec.ts +52 -0
- package/src/__tests__/RsForm.spec.ts +250 -6
- package/src/__tests__/RsFormItem.spec.ts +478 -0
- package/src/__tests__/RsFormList.spec.ts +109 -0
- package/src/__tests__/RsInput.spec.ts +108 -1
- package/src/__tests__/RsInputNumber.spec.ts +33 -0
- package/src/__tests__/RsMenu.spec.ts +406 -14
- package/src/__tests__/RsPagination.spec.ts +67 -2
- package/src/__tests__/RsSelect.spec.ts +465 -2
- package/src/__tests__/RsSwitch.spec.ts +74 -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__/RsTimePicker.spec.ts +20 -0
- package/src/__tests__/RsTooltip.spec.ts +43 -0
- package/src/__tests__/RsUpload.spec.ts +62 -6
- package/src/__tests__/createRsDialog.spec.ts +145 -0
- package/src/__tests__/css-token.spec.ts +51 -0
- package/src/__tests__/date-picker-utils.spec.ts +49 -0
- package/src/__tests__/dialog-utils.spec.ts +43 -0
- package/src/__tests__/form-path.spec.ts +46 -0
- package/src/__tests__/form-rules.spec.ts +139 -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 +27 -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/RsAvatar.vue +1 -1
- package/src/components/RsBadge.vue +1 -1
- package/src/components/RsBreadcrumb.vue +1 -1
- package/src/components/RsButton.vue +147 -23
- package/src/components/RsCalendarGrid.vue +3 -3
- package/src/components/RsCard.vue +204 -20
- package/src/components/RsCodeBlock.vue +4 -4
- package/src/components/RsCodeEditor.vue +8 -8
- package/src/components/RsConfirmDialog.vue +217 -30
- package/src/components/RsContextMenu.vue +57 -17
- package/src/components/RsDatePicker.vue +316 -39
- package/src/components/RsDateTimePicker.vue +27 -2
- package/src/components/RsDescriptions.vue +186 -0
- package/src/components/RsDescriptionsItem.vue +32 -0
- package/src/components/RsDialog.vue +374 -52
- package/src/components/RsDivider.vue +89 -0
- package/src/components/RsDrawer.vue +559 -34
- package/src/components/RsDropdown.vue +34 -3
- package/src/components/RsDynamicTags.vue +531 -0
- package/src/components/RsEmpty.vue +1 -1
- package/src/components/RsForm.vue +164 -8
- package/src/components/RsFormItem.vue +423 -0
- package/src/components/RsFormItemControl.vue +21 -0
- package/src/components/RsFormList.vue +117 -0
- package/src/components/RsInput.vue +491 -100
- package/src/components/RsInputNumber.vue +77 -27
- package/src/components/RsLabel.vue +20 -5
- package/src/components/RsLink.vue +1 -1
- package/src/components/RsLoadingBar.vue +151 -0
- package/src/components/RsMarkdown.vue +5 -5
- package/src/components/RsMenu.vue +74 -16
- package/src/components/RsMenuItems.vue +147 -9
- package/src/components/RsMonacoEditor.vue +9 -3
- package/src/components/RsPagination.vue +93 -14
- package/src/components/RsProseEditor.vue +3 -3
- package/src/components/RsRadio.vue +81 -0
- package/src/components/RsRadioItem.vue +156 -0
- package/src/components/RsSelect.vue +372 -642
- package/src/components/RsSidebarGroup.vue +1 -1
- package/src/components/RsSplitPane.vue +90 -1
- package/src/components/RsStatCard.vue +2 -2
- package/src/components/RsSteps.vue +2 -2
- package/src/components/RsSwitch.vue +192 -0
- package/src/components/RsTable.vue +653 -2555
- package/src/components/RsTabs.vue +422 -90
- package/src/components/RsTag.vue +191 -0
- package/src/components/RsTerminal.vue +58 -19
- package/src/components/RsTimePicker.vue +72 -9
- package/src/components/RsTimePickerColumns.vue +137 -78
- package/src/components/RsToaster.vue +64 -24
- package/src/components/RsTooltip.vue +10 -1
- package/src/components/RsTooltipProvider.vue +11 -1
- package/src/components/RsTree.vue +45 -27
- package/src/components/RsUpload.vue +147 -14
- 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 +195 -4
- package/src/components/descriptions-utils.ts +24 -0
- package/src/components/dialog-utils.ts +182 -0
- package/src/components/dialog-window.ts +74 -9
- package/src/components/drawer-utils.ts +73 -0
- package/src/components/form-item-control.ts +55 -0
- package/src/components/form-path.ts +92 -0
- package/src/components/form-rules.ts +311 -0
- package/src/components/form-utils.ts +145 -3
- 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 +285 -6
- 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/RsTableHeaderFilter.vue +1 -1
- 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 +85 -7
- 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/time-picker-utils.ts +76 -3
- package/src/components/upload-utils.ts +122 -0
- package/src/components/use-rs-select.ts +436 -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/useRsConfig.ts +10 -3
- package/src/composables/useRsI18n.ts +16 -5
- 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/index.ts +378 -10
- package/src/locale/interpolate.ts +32 -0
- package/src/locale/messages.ts +40 -0
- package/src/styles.css +365 -56
- package/src/theme/brand.example.css +24 -2
- package/src/theme/css-token.ts +107 -0
- package/src/theme/presets.ts +4 -0
- package/src/theme/types.ts +68 -0
- package/src/components/RsTableColgroup.vue +0 -17
- package/src/components/RsTableHeaderRow.vue +0 -30
package/src/components/reka.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
+
export type RsSelectValue = string | number
|
|
2
|
+
|
|
3
|
+
/** 过滤 / 回显比对字段名,默认 label;可传 value 或 fieldNames 映射后的键 */
|
|
4
|
+
export type RsSelectOptionFilterProp = string
|
|
5
|
+
|
|
6
|
+
export type RsSelectPlacement = 'top' | 'bottom' | 'left' | 'right'
|
|
7
|
+
|
|
8
|
+
export type RsSelectStatus = 'error' | 'warning' | ''
|
|
9
|
+
|
|
10
|
+
/** 自定义过滤(对齐 Ant Design filterOption)。返回 false 则隐藏该项。 */
|
|
11
|
+
export type RsSelectFilterOption = (query: string, option: RsSelectOption) => boolean
|
|
12
|
+
|
|
13
|
+
/** 过滤后排序(对齐 Ant Design filterSort) */
|
|
14
|
+
export type RsSelectFilterSort = (a: RsSelectOption, b: RsSelectOption) => number
|
|
15
|
+
|
|
1
16
|
export interface RsSelectOption {
|
|
2
17
|
label: string
|
|
3
|
-
value:
|
|
18
|
+
value: RsSelectValue
|
|
4
19
|
disabled?: boolean
|
|
20
|
+
/** 原生 title;悬停展示补充说明 */
|
|
21
|
+
title?: string
|
|
5
22
|
}
|
|
6
23
|
|
|
7
24
|
export interface RsSelectOptionGroup {
|
|
@@ -11,6 +28,51 @@ export interface RsSelectOptionGroup {
|
|
|
11
28
|
|
|
12
29
|
export type RsSelectOptions = ReadonlyArray<RsSelectOption | RsSelectOptionGroup>
|
|
13
30
|
|
|
31
|
+
/** 未正规化的选项(配合 fieldNames 可用 name/id/children 等业务字段) */
|
|
32
|
+
export type RsSelectOptionInput = RsSelectOption | RsSelectOptionGroup | Record<string, unknown>
|
|
33
|
+
|
|
34
|
+
export type RsSelectOptionsInput = ReadonlyArray<RsSelectOptionInput>
|
|
35
|
+
|
|
36
|
+
/** 对齐 Ant Design fieldNames */
|
|
37
|
+
export interface RsSelectFieldNames {
|
|
38
|
+
label?: string
|
|
39
|
+
value?: string
|
|
40
|
+
options?: string
|
|
41
|
+
disabled?: string
|
|
42
|
+
groupLabel?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type RsSelectGetPopupContainer = (triggerNode?: HTMLElement) => string | HTMLElement | undefined
|
|
46
|
+
|
|
47
|
+
/** labelInValue 时的 v-model 项(对齐 Ant Design) */
|
|
48
|
+
export interface RsSelectLabeledValue {
|
|
49
|
+
label: string
|
|
50
|
+
value: RsSelectValue
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type RsSelectModelValue =
|
|
54
|
+
| RsSelectValue
|
|
55
|
+
| RsSelectValue[]
|
|
56
|
+
| RsSelectLabeledValue
|
|
57
|
+
| RsSelectLabeledValue[]
|
|
58
|
+
| ''
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Reka ComboboxItem 禁止 value 为空串(空串表示未选中 / placeholder)。
|
|
62
|
+
* 选项若传入 value: '',对内映射为此哨兵,避免崩溃;对外读写仍为 ''。
|
|
63
|
+
*/
|
|
64
|
+
export const RS_SELECT_EMPTY_VALUE = '__rs_select_empty__'
|
|
65
|
+
|
|
66
|
+
/** 业务 value → ComboboxItem token(空串走哨兵;数字转为十进制字符串) */
|
|
67
|
+
export function toComboboxValue(value: RsSelectValue): string {
|
|
68
|
+
return value === '' ? RS_SELECT_EMPTY_VALUE : String(value)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** ComboboxItem token → 业务字符串(仅还原空串哨兵,不恢复 number) */
|
|
72
|
+
export function fromComboboxValue(value: string): string {
|
|
73
|
+
return value === RS_SELECT_EMPTY_VALUE ? '' : value
|
|
74
|
+
}
|
|
75
|
+
|
|
14
76
|
export function isSelectOptionGroup(
|
|
15
77
|
item: RsSelectOption | RsSelectOptionGroup,
|
|
16
78
|
): item is RsSelectOptionGroup {
|
|
@@ -29,10 +91,40 @@ export function flattenSelectOptions(options: RsSelectOptions): RsSelectOption[]
|
|
|
29
91
|
return result
|
|
30
92
|
}
|
|
31
93
|
|
|
94
|
+
export function findSelectOption(
|
|
95
|
+
options: RsSelectOptions,
|
|
96
|
+
token: string,
|
|
97
|
+
): RsSelectOption | undefined {
|
|
98
|
+
const raw = fromComboboxValue(token)
|
|
99
|
+
return flattenSelectOptions(options).find((opt) => String(opt.value) === String(raw))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Combobox token → 业务 value。
|
|
104
|
+
* 优先返回 options 里的原始类型(number 0 保持为 0);手输/未知项保持字符串。
|
|
105
|
+
*/
|
|
106
|
+
export function restoreSelectValue(
|
|
107
|
+
token: string,
|
|
108
|
+
options: RsSelectOptions,
|
|
109
|
+
): RsSelectValue {
|
|
110
|
+
const matched = findSelectOption(options, token)
|
|
111
|
+
if (matched) return matched.value
|
|
112
|
+
return fromComboboxValue(token)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function restoreSelectValues(
|
|
116
|
+
tokens: string[],
|
|
117
|
+
options: RsSelectOptions,
|
|
118
|
+
): RsSelectValue[] {
|
|
119
|
+
return tokens.map((token) => restoreSelectValue(token, options))
|
|
120
|
+
}
|
|
121
|
+
|
|
32
122
|
export function buildOptionLabelMap(options: RsSelectOptions): Map<string, string> {
|
|
33
123
|
const map = new Map<string, string>()
|
|
34
124
|
for (const opt of flattenSelectOptions(options)) {
|
|
35
|
-
|
|
125
|
+
const key = toComboboxValue(opt.value)
|
|
126
|
+
map.set(key, opt.label)
|
|
127
|
+
if (opt.value === '') map.set('', opt.label)
|
|
36
128
|
}
|
|
37
129
|
return map
|
|
38
130
|
}
|
|
@@ -40,27 +132,130 @@ export function buildOptionLabelMap(options: RsSelectOptions): Map<string, strin
|
|
|
40
132
|
export function buildOptionDisabledMap(options: RsSelectOptions): Map<string, boolean> {
|
|
41
133
|
const map = new Map<string, boolean>()
|
|
42
134
|
for (const opt of flattenSelectOptions(options)) {
|
|
43
|
-
|
|
135
|
+
const key = toComboboxValue(opt.value)
|
|
136
|
+
map.set(key, Boolean(opt.disabled))
|
|
44
137
|
}
|
|
45
138
|
return map
|
|
46
139
|
}
|
|
47
140
|
|
|
141
|
+
function optionFieldText(option: RsSelectOption, prop: string): string {
|
|
142
|
+
if (prop === 'value') return String(option.value)
|
|
143
|
+
if (prop === 'label' || !prop) return String(option.label)
|
|
144
|
+
const extra = Reflect.get(option, prop)
|
|
145
|
+
return extra == null ? String(option.label) : String(extra)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function defaultMatchOption(
|
|
149
|
+
option: RsSelectOption,
|
|
150
|
+
query: string,
|
|
151
|
+
contains: (text: string, search: string) => boolean,
|
|
152
|
+
optionFilterProp: RsSelectOptionFilterProp,
|
|
153
|
+
): boolean {
|
|
154
|
+
return contains(optionFieldText(option, optionFilterProp), query)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function coerceSelectValue(raw: unknown): RsSelectValue {
|
|
158
|
+
if (typeof raw === 'number' || typeof raw === 'string') return raw
|
|
159
|
+
if (raw == null) return ''
|
|
160
|
+
return String(raw)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function mapSelectOption(
|
|
164
|
+
raw: Record<string, unknown>,
|
|
165
|
+
names: Required<Pick<RsSelectFieldNames, 'label' | 'value' | 'disabled'>>,
|
|
166
|
+
): RsSelectOption {
|
|
167
|
+
const value = coerceSelectValue(raw[names.value])
|
|
168
|
+
const label = raw[names.label]
|
|
169
|
+
return {
|
|
170
|
+
...raw,
|
|
171
|
+
value,
|
|
172
|
+
label: label == null ? String(value) : String(label),
|
|
173
|
+
disabled: Boolean(raw[names.disabled]),
|
|
174
|
+
title: typeof raw.title === 'string' ? raw.title : undefined,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 按 fieldNames 把业务选项正规化为 { label, value, disabled }。
|
|
180
|
+
* 默认键即为 label / value / options,既有调用无需改。
|
|
181
|
+
*/
|
|
182
|
+
export function normalizeSelectOptions(
|
|
183
|
+
options: RsSelectOptionsInput,
|
|
184
|
+
fieldNames?: RsSelectFieldNames,
|
|
185
|
+
): RsSelectOptions {
|
|
186
|
+
const labelKey = fieldNames?.label ?? 'label'
|
|
187
|
+
const valueKey = fieldNames?.value ?? 'value'
|
|
188
|
+
const optionsKey = fieldNames?.options ?? 'options'
|
|
189
|
+
const disabledKey = fieldNames?.disabled ?? 'disabled'
|
|
190
|
+
const groupLabelKey = fieldNames?.groupLabel ?? labelKey
|
|
191
|
+
const result: Array<RsSelectOption | RsSelectOptionGroup> = []
|
|
192
|
+
|
|
193
|
+
for (const item of options) {
|
|
194
|
+
if (!item || typeof item !== 'object') continue
|
|
195
|
+
const raw = item as Record<string, unknown>
|
|
196
|
+
const nested = raw[optionsKey]
|
|
197
|
+
if (Array.isArray(nested)) {
|
|
198
|
+
const children: RsSelectOption[] = []
|
|
199
|
+
for (const child of nested) {
|
|
200
|
+
if (!child || typeof child !== 'object') continue
|
|
201
|
+
children.push(mapSelectOption(child as Record<string, unknown>, {
|
|
202
|
+
label: labelKey,
|
|
203
|
+
value: valueKey,
|
|
204
|
+
disabled: disabledKey,
|
|
205
|
+
}))
|
|
206
|
+
}
|
|
207
|
+
result.push({
|
|
208
|
+
label: String(raw[groupLabelKey] ?? ''),
|
|
209
|
+
options: children,
|
|
210
|
+
})
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
result.push(mapSelectOption(raw, { label: labelKey, value: valueKey, disabled: disabledKey }))
|
|
214
|
+
}
|
|
215
|
+
return result
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** 触发器 / tag 展示文案,对齐 Ant optionLabelProp */
|
|
219
|
+
export function optionDisplayLabel(option: RsSelectOption, prop = 'label'): string {
|
|
220
|
+
return optionFieldText(option, prop)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function comboboxBindingToTokens(value: unknown, multiple: boolean): string[] {
|
|
224
|
+
if (multiple) return Array.isArray(value) ? value.map(String) : []
|
|
225
|
+
if (value == null || value === '') return []
|
|
226
|
+
return [String(value)]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function selectModelEntries(value: unknown): unknown[] {
|
|
230
|
+
if (Array.isArray(value)) return value
|
|
231
|
+
if (value === '' || value == null) return []
|
|
232
|
+
return [value]
|
|
233
|
+
}
|
|
234
|
+
|
|
48
235
|
export function filterSelectOptions(
|
|
49
236
|
options: RsSelectOptions,
|
|
50
237
|
query: string,
|
|
51
238
|
contains: (text: string, search: string) => boolean,
|
|
239
|
+
filterOption?: RsSelectFilterOption | boolean,
|
|
240
|
+
optionFilterProp: RsSelectOptionFilterProp = 'label',
|
|
52
241
|
): RsSelectOptions {
|
|
242
|
+
if (filterOption === false) return options
|
|
53
243
|
const trimmed = query.trim()
|
|
54
244
|
if (!trimmed) return options
|
|
55
245
|
|
|
246
|
+
const match =
|
|
247
|
+
typeof filterOption === 'function'
|
|
248
|
+
? (opt: RsSelectOption) => filterOption(trimmed, opt)
|
|
249
|
+
: (opt: RsSelectOption) => defaultMatchOption(opt, trimmed, contains, optionFilterProp)
|
|
250
|
+
|
|
56
251
|
const result: Array<RsSelectOption | RsSelectOptionGroup> = []
|
|
57
252
|
for (const item of options) {
|
|
58
253
|
if (isSelectOptionGroup(item)) {
|
|
59
|
-
const filtered = item.options.filter(
|
|
254
|
+
const filtered = item.options.filter(match)
|
|
60
255
|
if (filtered.length > 0) {
|
|
61
256
|
result.push({ label: item.label, options: filtered })
|
|
62
257
|
}
|
|
63
|
-
} else if (
|
|
258
|
+
} else if (match(item)) {
|
|
64
259
|
result.push(item)
|
|
65
260
|
}
|
|
66
261
|
}
|
|
@@ -68,5 +263,89 @@ export function filterSelectOptions(
|
|
|
68
263
|
}
|
|
69
264
|
|
|
70
265
|
export function flattenSelectValues(options: RsSelectOptions): string[] {
|
|
71
|
-
return flattenSelectOptions(options).map((opt) => opt.value)
|
|
266
|
+
return flattenSelectOptions(options).map((opt) => toComboboxValue(opt.value))
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function toSelectedTokens(value: unknown, multiple: boolean): string[] {
|
|
270
|
+
const entries = multiple
|
|
271
|
+
? selectModelEntries(value)
|
|
272
|
+
: Array.isArray(value) || value === '' || value == null
|
|
273
|
+
? []
|
|
274
|
+
: [value]
|
|
275
|
+
const tokens: string[] = []
|
|
276
|
+
for (const entry of entries) {
|
|
277
|
+
const raw = unwrapSelectEntry(entry)
|
|
278
|
+
if (raw === null) continue
|
|
279
|
+
tokens.push(toComboboxValue(raw))
|
|
280
|
+
}
|
|
281
|
+
return tokens
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function isSelectLabeledValue(value: unknown): value is RsSelectLabeledValue {
|
|
285
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && 'value' in value
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function unwrapSelectEntry(item: unknown): RsSelectValue | null {
|
|
289
|
+
if (item == null || item === '') return null
|
|
290
|
+
if (isSelectLabeledValue(item)) return item.value
|
|
291
|
+
if (typeof item === 'string' || typeof item === 'number') return item
|
|
292
|
+
return null
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function toLabeledValue(
|
|
296
|
+
value: RsSelectValue,
|
|
297
|
+
options: RsSelectOptions,
|
|
298
|
+
): RsSelectLabeledValue {
|
|
299
|
+
const matched = flattenSelectOptions(options).find(
|
|
300
|
+
(opt) => String(opt.value) === String(value),
|
|
301
|
+
)
|
|
302
|
+
return {
|
|
303
|
+
value: matched ? matched.value : value,
|
|
304
|
+
label: matched?.label ?? String(value),
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function packSelectModel(
|
|
309
|
+
values: RsSelectValue[],
|
|
310
|
+
options: RsSelectOptions,
|
|
311
|
+
multiple: boolean,
|
|
312
|
+
labelInValue: boolean,
|
|
313
|
+
): RsSelectModelValue {
|
|
314
|
+
if (multiple) {
|
|
315
|
+
return labelInValue ? values.map((value) => toLabeledValue(value, options)) : values
|
|
316
|
+
}
|
|
317
|
+
const first = values[0]
|
|
318
|
+
if (first === undefined) return ''
|
|
319
|
+
return labelInValue ? toLabeledValue(first, options) : first
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function sortSelectOptions(
|
|
323
|
+
options: RsSelectOptions,
|
|
324
|
+
sort?: RsSelectFilterSort,
|
|
325
|
+
): RsSelectOptions {
|
|
326
|
+
if (!sort) return options
|
|
327
|
+
const next: Array<RsSelectOption | RsSelectOptionGroup> = []
|
|
328
|
+
const flats: RsSelectOption[] = []
|
|
329
|
+
const flushFlats = () => {
|
|
330
|
+
if (!flats.length) return
|
|
331
|
+
next.push(...[...flats].sort(sort))
|
|
332
|
+
flats.length = 0
|
|
333
|
+
}
|
|
334
|
+
for (const item of options) {
|
|
335
|
+
if (isSelectOptionGroup(item)) {
|
|
336
|
+
flushFlats()
|
|
337
|
+
next.push({ label: item.label, options: [...item.options].sort(sort) })
|
|
338
|
+
} else {
|
|
339
|
+
flats.push(item)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
flushFlats()
|
|
343
|
+
return next as RsSelectOptions
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function splitByTokenSeparators(raw: string, separators: string[]): string[] | null {
|
|
347
|
+
if (!separators.length) return null
|
|
348
|
+
const found = separators.find((sep) => sep && raw.includes(sep))
|
|
349
|
+
if (!found) return null
|
|
350
|
+
return raw.split(found)
|
|
72
351
|
}
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
/** 面板分割方向:horizontal 左右排布(拖动横向)· vertical 上下排布(拖动纵向) */
|
|
2
2
|
export type RsSplitOrientation = 'horizontal' | 'vertical'
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** 面板尺寸:数字为占比百分比;'auto' 按内容自适应(不占百分比配额) */
|
|
5
|
+
export type RsSplitPaneSize = number | 'auto'
|
|
6
|
+
|
|
7
|
+
/** 单个面板定义;百分比尺寸单位为 0~100 */
|
|
5
8
|
export interface RsSplitPaneItem {
|
|
6
9
|
/** 唯一标识,用于插槽名与 v-model:sizes 的顺序映射 */
|
|
7
10
|
key: string
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* 初始尺寸
|
|
13
|
+
* - number:占比百分比
|
|
14
|
+
* - 'auto':按内容自适应(flex-basis: auto)
|
|
15
|
+
* - 缺省:在「非 auto」面板的剩余百分比内均分
|
|
16
|
+
*/
|
|
17
|
+
size?: RsSplitPaneSize
|
|
18
|
+
/** 最小尺寸(百分比),默认 0;auto 面板可作为 min-height/min-width % */
|
|
11
19
|
min?: number
|
|
12
|
-
/** 最大尺寸(百分比),默认 100 */
|
|
20
|
+
/** 最大尺寸(百分比),默认 100;auto 面板可作为 max-height/max-width % */
|
|
13
21
|
max?: number
|
|
14
22
|
/** 是否可折叠:拖动越过阈值时吸附到 collapsedSize */
|
|
15
23
|
collapsible?: boolean
|
|
@@ -36,6 +44,16 @@ export function roundSize(value: number, precision = 4): number {
|
|
|
36
44
|
return Math.round(value * factor) / factor
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
/** 是否为内容自适应尺寸 */
|
|
48
|
+
export function isRsSplitPaneAutoSize(size: RsSplitPaneSize | undefined): size is 'auto' {
|
|
49
|
+
return size === 'auto'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 各面板是否声明为 auto(忽略受控 provided 时的瞬时物化) */
|
|
53
|
+
export function resolveSplitAutoFlags(panes: RsSplitPaneItem[]): boolean[] {
|
|
54
|
+
return panes.map((pane) => isRsSplitPaneAutoSize(pane.size))
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
/** 将面板定义解析为规范化约束,保证 0 ≤ min ≤ max ≤ 100 且 collapsedSize ≤ min */
|
|
40
58
|
export function resolveSplitConstraints(panes: RsSplitPaneItem[]): RsSplitConstraint[] {
|
|
41
59
|
return panes.map((pane) => {
|
|
@@ -65,8 +83,8 @@ function rescaleTo100(sizes: number[]): number[] {
|
|
|
65
83
|
}
|
|
66
84
|
|
|
67
85
|
/**
|
|
68
|
-
*
|
|
69
|
-
* 优先使用 provided(受控值),其次面板自身的 size
|
|
86
|
+
* 计算初始尺寸数组(非 auto 面板总和 100;auto 位为 0 占位,样式层不使用 flex-grow)。
|
|
87
|
+
* 优先使用 provided(受控值),其次面板自身的 number size,未指定的非 auto 项在剩余空间内均分。
|
|
70
88
|
*/
|
|
71
89
|
export function normalizeSplitSizes(
|
|
72
90
|
panes: RsSplitPaneItem[],
|
|
@@ -75,18 +93,53 @@ export function normalizeSplitSizes(
|
|
|
75
93
|
const count = panes.length
|
|
76
94
|
if (count === 0) return []
|
|
77
95
|
|
|
96
|
+
const autoFlags = resolveSplitAutoFlags(panes)
|
|
97
|
+
// 受控 sizes 一旦提供完整数组,视为已全部物化为百分比(含原 auto)
|
|
98
|
+
const fullyControlled =
|
|
99
|
+
Array.isArray(provided) &&
|
|
100
|
+
provided.length === count &&
|
|
101
|
+
provided.every((value) => typeof value === 'number' && Number.isFinite(value))
|
|
102
|
+
|
|
103
|
+
if (fullyControlled) {
|
|
104
|
+
return rescaleTo100(provided!.map((value) => Math.max(0, value))).map((value) =>
|
|
105
|
+
roundSize(value),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
const seed: (number | null)[] = panes.map((pane, index) => {
|
|
79
|
-
|
|
110
|
+
if (autoFlags[index]) return 0
|
|
111
|
+
const candidate = provided?.[index] ?? (typeof pane.size === 'number' ? pane.size : null)
|
|
80
112
|
return typeof candidate === 'number' && Number.isFinite(candidate)
|
|
81
113
|
? Math.max(0, candidate)
|
|
82
114
|
: null
|
|
83
115
|
})
|
|
84
116
|
|
|
85
|
-
const
|
|
86
|
-
|
|
117
|
+
const flexibleIndices = panes
|
|
118
|
+
.map((_, index) => index)
|
|
119
|
+
.filter((index) => !autoFlags[index])
|
|
120
|
+
|
|
121
|
+
if (flexibleIndices.length === 0) {
|
|
122
|
+
// 全部 auto:占位全 0,由内容撑开
|
|
123
|
+
return panes.map(() => 0)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const missing = flexibleIndices.filter((index) => seed[index] === null).length
|
|
127
|
+
const knownSum = flexibleIndices.reduce<number>((acc, index) => acc + (seed[index] ?? 0), 0)
|
|
87
128
|
const each = missing > 0 ? Math.max(0, 100 - knownSum) / missing : 0
|
|
88
|
-
|
|
89
|
-
|
|
129
|
+
|
|
130
|
+
const filled = seed.map((value, index) => {
|
|
131
|
+
if (autoFlags[index]) return 0
|
|
132
|
+
return value ?? each
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// 只对非 auto 部分缩放到 100
|
|
136
|
+
const flexibleValues = flexibleIndices.map((index) => filled[index] ?? 0)
|
|
137
|
+
const scaledFlexible = rescaleTo100(flexibleValues)
|
|
138
|
+
const next = filled.slice()
|
|
139
|
+
flexibleIndices.forEach((paneIndex, i) => {
|
|
140
|
+
next[paneIndex] = roundSize(scaledFlexible[i] ?? 0)
|
|
141
|
+
})
|
|
142
|
+
return next
|
|
90
143
|
}
|
|
91
144
|
|
|
92
145
|
/** 判断某面板当前是否处于折叠态 */
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends import('../table-utils').RsTableRowData">
|
|
2
|
+
/**
|
|
3
|
+
* RsTable 表体视图:通过 ViewContext inject 取状态(多表互不串扰)。
|
|
4
|
+
* 行级 v-memo 保留,避免抽离后丢失细粒度跳过渲染。
|
|
5
|
+
*/
|
|
6
|
+
import { isTableRowDisabled, resolveEntryKey } from '../table-utils'
|
|
7
|
+
import type { RsTableRowEntry } from '../table-utils'
|
|
8
|
+
import RsTableBodyRow from './RsTableBodyRow.vue'
|
|
9
|
+
import { useRsTableView } from './rs-table-view-context'
|
|
10
|
+
|
|
11
|
+
const view = useRsTableView<T>()
|
|
12
|
+
|
|
13
|
+
type RowEntry = Extract<RsTableRowEntry<T>, { type: 'row' }>
|
|
14
|
+
|
|
15
|
+
function editingColKeyFor(entry: RowEntry): string | null {
|
|
16
|
+
const key = view.rowKeyFor(entry)
|
|
17
|
+
return view.editingCell?.rowKey === key ? view.editingCell.colKey : null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function focusColKeyFor(entry: RowEntry): string | null {
|
|
21
|
+
const key = view.rowKeyFor(entry)
|
|
22
|
+
return view.focusCell?.rowKey === key ? view.focusCell.colKey : null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function memoEditingDraft(entry: RowEntry): string {
|
|
26
|
+
return view.editingCell?.rowKey === view.rowKeyFor(entry) ? view.editingDraft : ''
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<tbody
|
|
32
|
+
@pointerover.passive="view.onBodyPointerOver($event)"
|
|
33
|
+
@pointerout.passive="view.onBodyPointerOut($event)"
|
|
34
|
+
>
|
|
35
|
+
<tr v-if="view.loading">
|
|
36
|
+
<td class="rs-table__empty" :colspan="view.bodyColspan">
|
|
37
|
+
<div class="rs-table__empty-content" :style="view.emptyContentStyle">
|
|
38
|
+
{{ view.bodyLabels.loading }}
|
|
39
|
+
</div>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
42
|
+
<tr v-else-if="!view.hasData">
|
|
43
|
+
<td class="rs-table__empty" :colspan="view.bodyColspan">
|
|
44
|
+
<div class="rs-table__empty-content" :style="view.emptyContentStyle">
|
|
45
|
+
<slot name="empty">{{ view.bodyLabels.empty }}</slot>
|
|
46
|
+
</div>
|
|
47
|
+
</td>
|
|
48
|
+
</tr>
|
|
49
|
+
<template v-else>
|
|
50
|
+
<tr
|
|
51
|
+
v-if="view.virtualScrollEnabled && view.virtualSlice.paddingTop > 0"
|
|
52
|
+
class="rs-table__virtual-pad"
|
|
53
|
+
>
|
|
54
|
+
<td :colspan="view.bodyColspan" :style="{ height: `${view.virtualSlice.paddingTop}px` }" />
|
|
55
|
+
</tr>
|
|
56
|
+
<template
|
|
57
|
+
v-for="entry in view.visibleEntries"
|
|
58
|
+
:key="resolveEntryKey(entry, view.rowKey)"
|
|
59
|
+
>
|
|
60
|
+
<tr v-if="entry.type === 'group'" class="rs-table__group-row">
|
|
61
|
+
<td class="rs-table__group-cell" :colspan="view.bodyColspan">
|
|
62
|
+
<slot name="group" :key="entry.key" :label="entry.label">
|
|
63
|
+
{{ entry.label }}
|
|
64
|
+
</slot>
|
|
65
|
+
</td>
|
|
66
|
+
</tr>
|
|
67
|
+
<tr v-else-if="entry.type === 'expand'" class="rs-table__expand-row">
|
|
68
|
+
<td class="rs-table__expand-cell" :colspan="view.bodyColspan">
|
|
69
|
+
<slot name="expand" :row="entry.row" :index="entry.rowIndex" />
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
72
|
+
<RsTableBodyRow
|
|
73
|
+
v-else
|
|
74
|
+
v-memo="[
|
|
75
|
+
view.rowKeyFor(entry),
|
|
76
|
+
entry.row,
|
|
77
|
+
view.isRowSelected(entry),
|
|
78
|
+
view.isRowIndeterminate(entry),
|
|
79
|
+
view.isRowExpanded(entry),
|
|
80
|
+
view.isHighlighted(view.rowKeyFor(entry)),
|
|
81
|
+
view.rowEditPending(entry),
|
|
82
|
+
view.stagedMapSize,
|
|
83
|
+
memoEditingDraft(entry),
|
|
84
|
+
view.activeEditCellKey,
|
|
85
|
+
view.activeFocusCellKey,
|
|
86
|
+
view.activeErrorMapSize,
|
|
87
|
+
view.activeValidatingMapSize,
|
|
88
|
+
view.dragRowKeys.join(','),
|
|
89
|
+
view.dropRowTargetKey,
|
|
90
|
+
view.dropRowPosition,
|
|
91
|
+
view.columnPadLeft,
|
|
92
|
+
view.columnPadRight,
|
|
93
|
+
view.treeDepthOf(entry),
|
|
94
|
+
view.canExpandRow(entry),
|
|
95
|
+
view.treeLoadingKeySet.has(view.rowKeyFor(entry)),
|
|
96
|
+
view.visibleDataColumns.map((c) => c.key).join('\0'),
|
|
97
|
+
view.instanceId,
|
|
98
|
+
]"
|
|
99
|
+
:row="entry.row"
|
|
100
|
+
:row-index="entry.rowIndex"
|
|
101
|
+
:row-key="view.rowKeyFor(entry)"
|
|
102
|
+
:columns="view.visibleDataColumns"
|
|
103
|
+
:column-pad-left="view.columnPadLeft"
|
|
104
|
+
:column-pad-right="view.columnPadRight"
|
|
105
|
+
:column-td-class-map="view.columnTdClassMap"
|
|
106
|
+
:column-style-map="view.columnStyleMap"
|
|
107
|
+
:show-row-drag-handle="view.showRowDragHandle"
|
|
108
|
+
:expandable="view.detailExpandable"
|
|
109
|
+
:tree-mode="view.treeMode"
|
|
110
|
+
:tree-depth="view.treeDepthOf(entry)"
|
|
111
|
+
:tree-has-children="view.canExpandRow(entry)"
|
|
112
|
+
:tree-indent="view.treeIndentPx"
|
|
113
|
+
:tree-column-key="view.treeExpandColumnKey"
|
|
114
|
+
:tree-loading="view.treeLoadingKeySet.has(view.rowKeyFor(entry))"
|
|
115
|
+
:selectable="view.showSelectColumn"
|
|
116
|
+
:show-index="view.showIndexColumn"
|
|
117
|
+
:show-edit-gutter="view.showEditGutterColumn"
|
|
118
|
+
:striped="view.striped"
|
|
119
|
+
:selection-type="view.selectionType"
|
|
120
|
+
:is-radio-selection="view.isRadioSelection"
|
|
121
|
+
:table-editable="view.editable"
|
|
122
|
+
:edit-trigger="view.editTrigger"
|
|
123
|
+
:row-commit="view.rowCommit"
|
|
124
|
+
:allow-null="view.allowNull"
|
|
125
|
+
:focus-mode="view.editFocusMode"
|
|
126
|
+
:null-label="view.nullLabel"
|
|
127
|
+
:show-row-status="view.showRowStatusColumn"
|
|
128
|
+
:selected="view.isRowSelected(entry)"
|
|
129
|
+
:indeterminate="view.isRowIndeterminate(entry)"
|
|
130
|
+
:highlighted="view.isHighlighted(view.rowKeyFor(entry))"
|
|
131
|
+
:expanded="view.isRowExpanded(entry)"
|
|
132
|
+
:disabled="isTableRowDisabled(entry.row)"
|
|
133
|
+
:row-drag-by-row="view.isRowDragByRow(entry)"
|
|
134
|
+
:dragging="view.isRowDragging(entry)"
|
|
135
|
+
:drop-target="view.isRowDropTarget(entry)"
|
|
136
|
+
:drop-position="view.isRowDropTarget(entry) ? view.dropRowPosition : null"
|
|
137
|
+
:drag-lead-style="view.dragLeadStyle"
|
|
138
|
+
:expand-lead-style="view.expandLeadStyle"
|
|
139
|
+
:select-lead-style="view.selectLeadStyle"
|
|
140
|
+
:index-lead-style="view.indexLeadStyle"
|
|
141
|
+
:gutter-lead-style="view.gutterLeadStyle"
|
|
142
|
+
:can-expand="view.canExpandRow(entry)"
|
|
143
|
+
:can-select="view.canSelectRow(entry)"
|
|
144
|
+
:row-edit-pending="view.rowEditPending(entry)"
|
|
145
|
+
:row-dirty="view.isRowDirty(view.rowKeyFor(entry))"
|
|
146
|
+
:show-gutter-rollback="view.rowCommit || view.isExternalRowPending(entry)"
|
|
147
|
+
:editing-col-key="editingColKeyFor(entry)"
|
|
148
|
+
:focus-col-key="focusColKeyFor(entry)"
|
|
149
|
+
:has-column-slot="view.hasColumnSlot"
|
|
150
|
+
:has-edit-slot="view.hasEditSlot"
|
|
151
|
+
:cell-tooltip-enabled="view.cellTooltipEnabled"
|
|
152
|
+
:cell-tooltip-mode="view.cellTooltipMode"
|
|
153
|
+
:cell-tooltip-text="view.cellTooltipText"
|
|
154
|
+
:cell-tooltip-fallback-title="view.cellTooltipFallbackTitle"
|
|
155
|
+
:get-cell-draft="view.getCellDraft"
|
|
156
|
+
:is-cell-dirty="view.isCellDirty"
|
|
157
|
+
:get-cell-error="view.getCellError"
|
|
158
|
+
:is-cell-validating="view.isCellValidating"
|
|
159
|
+
:drag-row-label="view.bodyLabels.dragRow"
|
|
160
|
+
:expand-row-label="view.bodyLabels.expandRow"
|
|
161
|
+
:collapse-row-label="view.bodyLabels.collapseRow"
|
|
162
|
+
:select-row-label="view.bodyLabels.selectRow"
|
|
163
|
+
:row-commit-label="view.bodyLabels.rowCommit"
|
|
164
|
+
:row-rollback-label="view.bodyLabels.rowRollback"
|
|
165
|
+
:gutter-commit-hint="view.bodyLabels.gutterCommit"
|
|
166
|
+
@click="view.onRowClick(entry, $event)"
|
|
167
|
+
@mousedown="view.onRowSelectMouseDown($event)"
|
|
168
|
+
@cell-click="(colKey, event) => view.onCellClick(entry, colKey, event)"
|
|
169
|
+
@cell-dblclick="(colKey, event) => view.onCellDblclick(entry, colKey, event)"
|
|
170
|
+
@cell-contextmenu="(colKey, event) => view.onCellContextmenu(entry, colKey, event)"
|
|
171
|
+
@dblclick="view.onRowDblclick(entry, $event)"
|
|
172
|
+
@contextmenu="view.onRowContextmenu(entry, $event)"
|
|
173
|
+
@dragover="view.onRowDragOver(entry, $event)"
|
|
174
|
+
@dragleave="view.onRowDragLeave($event)"
|
|
175
|
+
@drop="view.onRowDrop(entry, $event)"
|
|
176
|
+
@row-drag-start="view.onRowDragStart(entry, $event)"
|
|
177
|
+
@row-drag-end="view.onRowDragEnd()"
|
|
178
|
+
@toggle-expand="view.onToggleExpand(entry)"
|
|
179
|
+
@toggle-select="view.onToggleSelect(entry)"
|
|
180
|
+
@cell-start-edit="view.onCellStartEdit(entry, $event)"
|
|
181
|
+
@cell-commit="(colKey, value) => view.onCellCommit(entry, colKey, value)"
|
|
182
|
+
@cell-cancel="view.onCellCancel(entry, $event)"
|
|
183
|
+
@cell-update-draft="(colKey, value) => view.onCellUpdateDraft(entry, colKey, value)"
|
|
184
|
+
@cell-navigate="(colKey, direction) => view.onCellNavigate(entry, colKey, direction)"
|
|
185
|
+
@row-commit="view.onRowCommit(entry)"
|
|
186
|
+
@row-rollback="view.onRowRollback(entry)"
|
|
187
|
+
@gutter-commit="view.onGutterCommit(entry)"
|
|
188
|
+
>
|
|
189
|
+
<template v-for="column in view.visibleDataColumns" #[column.key]="slotProps">
|
|
190
|
+
<slot :name="column.key" v-bind="slotProps" />
|
|
191
|
+
</template>
|
|
192
|
+
<template
|
|
193
|
+
v-for="column in view.visibleDataColumns"
|
|
194
|
+
:key="`edit-${column.key}`"
|
|
195
|
+
#[`edit-${column.key}`]="slotProps"
|
|
196
|
+
>
|
|
197
|
+
<slot :name="`edit-${column.key}`" v-bind="slotProps" />
|
|
198
|
+
</template>
|
|
199
|
+
</RsTableBodyRow>
|
|
200
|
+
</template>
|
|
201
|
+
<tr
|
|
202
|
+
v-if="view.virtualScrollEnabled && view.virtualSlice.paddingBottom > 0"
|
|
203
|
+
class="rs-table__virtual-pad"
|
|
204
|
+
>
|
|
205
|
+
<td
|
|
206
|
+
:colspan="view.bodyColspan"
|
|
207
|
+
:style="{ height: `${view.virtualSlice.paddingBottom}px` }"
|
|
208
|
+
/>
|
|
209
|
+
</tr>
|
|
210
|
+
<tr v-if="view.infinite && view.loadingMore">
|
|
211
|
+
<td class="rs-table__empty rs-table__empty--more" :colspan="view.bodyColspan">
|
|
212
|
+
{{ view.bodyLabels.loadingMore }}
|
|
213
|
+
</td>
|
|
214
|
+
</tr>
|
|
215
|
+
</template>
|
|
216
|
+
</tbody>
|
|
217
|
+
</template>
|