xiaodao-spreader 0.1.34 → 0.1.36

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 (34) hide show
  1. package/README.ZH.md +58 -1
  2. package/README.md +58 -1
  3. package/dist/style.css +1 -1
  4. package/dist/types/components/spreader/components/data-validation-input-message.vue.d.ts +22 -0
  5. package/dist/types/components/spreader/components/{conditional-format-manager.vue.d.ts → pickers/conditional-format-manager.vue.d.ts} +1 -1
  6. package/dist/types/components/spreader/components/{conditional-format-rule-editor.vue.d.ts → pickers/conditional-format-rule-editor.vue.d.ts} +1 -1
  7. package/dist/types/components/spreader/components/pickers/data-validation-alert.vue.d.ts +18 -0
  8. package/dist/types/components/spreader/components/pickers/data-validation-dialog.vue.d.ts +22 -0
  9. package/dist/types/components/spreader/components/pickers/data-validation-dropdown.vue.d.ts +31 -0
  10. package/dist/types/components/spreader/components/{filter-popup.vue.d.ts → pickers/filter-popup.vue.d.ts} +1 -1
  11. package/dist/types/components/spreader/components/pickers/outline-picker.vue.d.ts +26 -0
  12. package/dist/types/components/spreader/components/spreader.vue.d.ts +17 -5
  13. package/dist/types/components/spreader/components/toolbar.vue.d.ts +9 -1
  14. package/dist/types/components/spreader/composables/borders-merge.d.ts +3 -1
  15. package/dist/types/components/spreader/composables/core-state.d.ts +121 -2
  16. package/dist/types/components/spreader/composables/interactions.d.ts +33 -1
  17. package/dist/types/components/spreader/core/autofill.d.ts +11 -2
  18. package/dist/types/components/spreader/core/constants.d.ts +2 -0
  19. package/dist/types/components/spreader/core/data-validation.d.ts +119 -0
  20. package/dist/types/components/spreader/core/outline-core.d.ts +64 -0
  21. package/dist/types/components/spreader/core/types.d.ts +90 -0
  22. package/dist/types/components/spreader/index.d.ts +2 -1
  23. package/dist/xiaodao-spreader.es.js +9901 -7153
  24. package/dist/xiaodao-spreader.umd.js +9 -9
  25. package/package.json +1 -1
  26. package/dist/types/components/spreader/components/pickers/{borderPicker.vue.d.ts → border-picker.vue.d.ts} +0 -0
  27. package/dist/types/components/spreader/components/pickers/{calcPicker.vue.d.ts → calc-picker.vue.d.ts} +0 -0
  28. package/dist/types/components/spreader/components/pickers/{colorPicker.vue.d.ts → color-picker.vue.d.ts} +0 -0
  29. package/dist/types/components/spreader/components/{conditional-format-menu.vue.d.ts → pickers/conditional-format-menu.vue.d.ts} +1 -1
  30. /package/dist/types/components/spreader/components/pickers/{insertFunctionDialog.vue.d.ts → insert-function-dialog.vue.d.ts} +0 -0
  31. /package/dist/types/components/spreader/components/pickers/{mergePicker.vue.d.ts → merge-picker.vue.d.ts} +0 -0
  32. /package/dist/types/components/spreader/components/pickers/{numberFormatDialog.vue.d.ts → number-format-dialog.vue.d.ts} +0 -0
  33. /package/dist/types/components/spreader/components/pickers/{sortConfirmDialog.vue.d.ts → sort-confirm-dialog.vue.d.ts} +0 -0
  34. /package/dist/types/components/spreader/components/pickers/{sortPicker.vue.d.ts → sort-picker.vue.d.ts} +0 -0
@@ -0,0 +1,26 @@
1
+ type __VLS_Props = {
2
+ modelOpen?: boolean;
3
+ locale?: string;
4
+ triggerEl?: HTMLElement | null;
5
+ };
6
+ declare function openMenu(): void;
7
+ declare function close(): void;
8
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {
9
+ open: import("vue").Ref<boolean, boolean>;
10
+ openMenu: typeof openMenu;
11
+ close: typeof close;
12
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
13
+ "update:modelOpen": (v: boolean) => any;
14
+ action: (v: string) => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ "onUpdate:modelOpen"?: ((v: boolean) => any) | undefined;
17
+ onAction?: ((v: string) => any) | undefined;
18
+ }>, {
19
+ modelOpen: boolean;
20
+ triggerEl: HTMLElement | null;
21
+ locale: string;
22
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {
23
+ rootRef: HTMLDivElement;
24
+ menuRef: HTMLDivElement;
25
+ }, HTMLDivElement>;
26
+ export default _default;
@@ -43,10 +43,11 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
43
43
  cachedTextColor: string;
44
44
  cachedFillColor: string;
45
45
  borderMenuOpen: boolean;
46
- cachedBorder: import("@/components/spreader/components/pickers/borderPicker.vue").BorderType;
46
+ cachedBorder: import("@/components/spreader/components/pickers/border-picker.vue").BorderType;
47
47
  sortMenuOpen: boolean;
48
48
  cachedSortOrder: import("@/components/spreader/core/sort-core.ts").SortOrder;
49
49
  canSort: boolean;
50
+ outlineMenuOpen: boolean;
50
51
  canFilter: boolean;
51
52
  filterActive: boolean;
52
53
  selHAlign: string;
@@ -92,6 +93,7 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
92
93
  "onToggle-filter"?: (() => any) | undefined;
93
94
  "onCf-new-rule"?: (() => any) | undefined;
94
95
  "onCf-manage"?: (() => any) | undefined;
96
+ "onOpen-data-validation"?: (() => any) | undefined;
95
97
  "onFont-family-change"?: ((v: string | number) => any) | undefined;
96
98
  "onFont-size-change"?: ((v: string | number) => any) | undefined;
97
99
  "onH-align-change"?: ((v: string | number) => any) | undefined;
@@ -102,15 +104,17 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
102
104
  "onNumber-format-change"?: ((v: string) => any) | undefined;
103
105
  "onFreeze-change"?: ((v: string) => any) | undefined;
104
106
  "onCf-preset"?: ((v: string) => any) | undefined;
107
+ "onOutline-action"?: ((v: string) => any) | undefined;
105
108
  "onFont-size-keydown"?: ((ev: KeyboardEvent) => any) | undefined;
106
109
  "onUpdate:font-size-menu-open"?: ((v: boolean) => any) | undefined;
107
110
  "onUpdate:text-color-menu-open"?: ((v: boolean) => any) | undefined;
108
111
  "onUpdate:fill-color-menu-open"?: ((v: boolean) => any) | undefined;
109
112
  "onUpdate:border-menu-open"?: ((v: boolean) => any) | undefined;
110
113
  "onUpdate:sort-menu-open"?: ((v: boolean) => any) | undefined;
114
+ "onUpdate:outline-menu-open"?: ((v: boolean) => any) | undefined;
111
115
  "onUpdate:merge-menu-open"?: ((v: boolean) => any) | undefined;
112
116
  "onUpdate:calc-menu-open"?: ((v: boolean) => any) | undefined;
113
- "onBorder-change"?: ((v: import("@/components/spreader/components/pickers/borderPicker.vue").BorderType) => any) | undefined;
117
+ "onBorder-change"?: ((v: import("@/components/spreader/components/pickers/border-picker.vue").BorderType) => any) | undefined;
114
118
  "onSort-change"?: ((v: import("@/components/spreader/core/sort-core.ts").SortOrder) => any) | undefined;
115
119
  "onMerge-change"?: ((v: import("../core/constants").MergeType) => any) | undefined;
116
120
  "onCf-clear"?: ((scope: "sheet" | "selection") => any) | undefined;
@@ -144,6 +148,7 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
144
148
  "toggle-filter": () => void;
145
149
  "cf-new-rule": () => void;
146
150
  "cf-manage": () => void;
151
+ "open-data-validation": () => void;
147
152
  "font-family-change": (v: string | number) => void;
148
153
  "font-size-change": (v: string | number) => void;
149
154
  "h-align-change": (v: string | number) => void;
@@ -154,15 +159,17 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
154
159
  "number-format-change": (v: string) => void;
155
160
  "freeze-change": (v: string) => void;
156
161
  "cf-preset": (v: string) => void;
162
+ "outline-action": (v: string) => void;
157
163
  "font-size-keydown": (ev: KeyboardEvent) => void;
158
164
  "update:font-size-menu-open": (v: boolean) => void;
159
165
  "update:text-color-menu-open": (v: boolean) => void;
160
166
  "update:fill-color-menu-open": (v: boolean) => void;
161
167
  "update:border-menu-open": (v: boolean) => void;
162
168
  "update:sort-menu-open": (v: boolean) => void;
169
+ "update:outline-menu-open": (v: boolean) => void;
163
170
  "update:merge-menu-open": (v: boolean) => void;
164
171
  "update:calc-menu-open": (v: boolean) => void;
165
- "border-change": (v: import("@/components/spreader/components/pickers/borderPicker.vue").BorderType) => void;
172
+ "border-change": (v: import("@/components/spreader/components/pickers/border-picker.vue").BorderType) => void;
166
173
  "sort-change": (v: import("@/components/spreader/core/sort-core.ts").SortOrder) => void;
167
174
  "merge-change": (v: import("../core/constants").MergeType) => void;
168
175
  "cf-clear": (scope: "sheet" | "selection") => void;
@@ -175,6 +182,7 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
175
182
  mergeArrowRef: HTMLButtonElement;
176
183
  calcArrowRef: HTMLButtonElement;
177
184
  sortArrowRef: HTMLButtonElement;
185
+ outlineArrowRef: HTMLButtonElement;
178
186
  moreBtnEl: HTMLButtonElement;
179
187
  overflowMenuEl: HTMLDivElement;
180
188
  }, any, import("vue").ComponentProvideOptions, {
@@ -207,10 +215,11 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
207
215
  cachedTextColor: string;
208
216
  cachedFillColor: string;
209
217
  borderMenuOpen: boolean;
210
- cachedBorder: import("@/components/spreader/components/pickers/borderPicker.vue").BorderType;
218
+ cachedBorder: import("@/components/spreader/components/pickers/border-picker.vue").BorderType;
211
219
  sortMenuOpen: boolean;
212
220
  cachedSortOrder: import("@/components/spreader/core/sort-core.ts").SortOrder;
213
221
  canSort: boolean;
222
+ outlineMenuOpen: boolean;
214
223
  canFilter: boolean;
215
224
  filterActive: boolean;
216
225
  selHAlign: string;
@@ -256,6 +265,7 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
256
265
  "onToggle-filter"?: (() => any) | undefined;
257
266
  "onCf-new-rule"?: (() => any) | undefined;
258
267
  "onCf-manage"?: (() => any) | undefined;
268
+ "onOpen-data-validation"?: (() => any) | undefined;
259
269
  "onFont-family-change"?: ((v: string | number) => any) | undefined;
260
270
  "onFont-size-change"?: ((v: string | number) => any) | undefined;
261
271
  "onH-align-change"?: ((v: string | number) => any) | undefined;
@@ -266,15 +276,17 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
266
276
  "onNumber-format-change"?: ((v: string) => any) | undefined;
267
277
  "onFreeze-change"?: ((v: string) => any) | undefined;
268
278
  "onCf-preset"?: ((v: string) => any) | undefined;
279
+ "onOutline-action"?: ((v: string) => any) | undefined;
269
280
  "onFont-size-keydown"?: ((ev: KeyboardEvent) => any) | undefined;
270
281
  "onUpdate:font-size-menu-open"?: ((v: boolean) => any) | undefined;
271
282
  "onUpdate:text-color-menu-open"?: ((v: boolean) => any) | undefined;
272
283
  "onUpdate:fill-color-menu-open"?: ((v: boolean) => any) | undefined;
273
284
  "onUpdate:border-menu-open"?: ((v: boolean) => any) | undefined;
274
285
  "onUpdate:sort-menu-open"?: ((v: boolean) => any) | undefined;
286
+ "onUpdate:outline-menu-open"?: ((v: boolean) => any) | undefined;
275
287
  "onUpdate:merge-menu-open"?: ((v: boolean) => any) | undefined;
276
288
  "onUpdate:calc-menu-open"?: ((v: boolean) => any) | undefined;
277
- "onBorder-change"?: ((v: import("@/components/spreader/components/pickers/borderPicker.vue").BorderType) => any) | undefined;
289
+ "onBorder-change"?: ((v: import("@/components/spreader/components/pickers/border-picker.vue").BorderType) => any) | undefined;
278
290
  "onSort-change"?: ((v: import("@/components/spreader/core/sort-core.ts").SortOrder) => any) | undefined;
279
291
  "onMerge-change"?: ((v: import("../core/constants").MergeType) => any) | undefined;
280
292
  "onCf-clear"?: ((scope: "sheet" | "selection") => any) | undefined;
@@ -1,6 +1,6 @@
1
1
  import type { FontOption, MergeType } from '../core/constants';
2
2
  import { type NFOption } from '../core/number-format';
3
- import { type BorderType } from './pickers/borderPicker.vue';
3
+ import { type BorderType } from './pickers/border-picker.vue';
4
4
  import type { SortOrder } from '../core/sort-core';
5
5
  declare function closeOverflow(): void;
6
6
  type __VLS_Props = {
@@ -30,6 +30,7 @@ type __VLS_Props = {
30
30
  sortMenuOpen: boolean;
31
31
  cachedSortOrder: SortOrder;
32
32
  canSort: boolean;
33
+ outlineMenuOpen: boolean;
33
34
  /** 工具栏「筛选」按钮是否可用(单选合并单元格且无筛选态时禁用) */
34
35
  canFilter: boolean;
35
36
  /** 当前 worksheet 是否已启用筛选 */
@@ -82,6 +83,7 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {
82
83
  "toggle-filter": () => void;
83
84
  "cf-new-rule": () => void;
84
85
  "cf-manage": () => void;
86
+ "open-data-validation": () => void;
85
87
  "font-family-change": (v: string | number) => void;
86
88
  "font-size-change": (v: string | number) => void;
87
89
  "h-align-change": (v: string | number) => void;
@@ -92,12 +94,14 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {
92
94
  "number-format-change": (v: string) => void;
93
95
  "freeze-change": (v: string) => void;
94
96
  "cf-preset": (v: string) => void;
97
+ "outline-action": (v: string) => void;
95
98
  "font-size-keydown": (ev: KeyboardEvent) => void;
96
99
  "update:font-size-menu-open": (v: boolean) => void;
97
100
  "update:text-color-menu-open": (v: boolean) => void;
98
101
  "update:fill-color-menu-open": (v: boolean) => void;
99
102
  "update:border-menu-open": (v: boolean) => void;
100
103
  "update:sort-menu-open": (v: boolean) => void;
104
+ "update:outline-menu-open": (v: boolean) => void;
101
105
  "update:merge-menu-open": (v: boolean) => void;
102
106
  "update:calc-menu-open": (v: boolean) => void;
103
107
  "border-change": (v: BorderType) => void;
@@ -132,6 +136,7 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {
132
136
  "onToggle-filter"?: (() => any) | undefined;
133
137
  "onCf-new-rule"?: (() => any) | undefined;
134
138
  "onCf-manage"?: (() => any) | undefined;
139
+ "onOpen-data-validation"?: (() => any) | undefined;
135
140
  "onFont-family-change"?: ((v: string | number) => any) | undefined;
136
141
  "onFont-size-change"?: ((v: string | number) => any) | undefined;
137
142
  "onH-align-change"?: ((v: string | number) => any) | undefined;
@@ -142,12 +147,14 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {
142
147
  "onNumber-format-change"?: ((v: string) => any) | undefined;
143
148
  "onFreeze-change"?: ((v: string) => any) | undefined;
144
149
  "onCf-preset"?: ((v: string) => any) | undefined;
150
+ "onOutline-action"?: ((v: string) => any) | undefined;
145
151
  "onFont-size-keydown"?: ((ev: KeyboardEvent) => any) | undefined;
146
152
  "onUpdate:font-size-menu-open"?: ((v: boolean) => any) | undefined;
147
153
  "onUpdate:text-color-menu-open"?: ((v: boolean) => any) | undefined;
148
154
  "onUpdate:fill-color-menu-open"?: ((v: boolean) => any) | undefined;
149
155
  "onUpdate:border-menu-open"?: ((v: boolean) => any) | undefined;
150
156
  "onUpdate:sort-menu-open"?: ((v: boolean) => any) | undefined;
157
+ "onUpdate:outline-menu-open"?: ((v: boolean) => any) | undefined;
151
158
  "onUpdate:merge-menu-open"?: ((v: boolean) => any) | undefined;
152
159
  "onUpdate:calc-menu-open"?: ((v: boolean) => any) | undefined;
153
160
  "onBorder-change"?: ((v: BorderType) => any) | undefined;
@@ -163,6 +170,7 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {
163
170
  mergeArrowRef: HTMLButtonElement;
164
171
  calcArrowRef: HTMLButtonElement;
165
172
  sortArrowRef: HTMLButtonElement;
173
+ outlineArrowRef: HTMLButtonElement;
166
174
  moreBtnEl: HTMLButtonElement;
167
175
  overflowMenuEl: HTMLDivElement;
168
176
  }, any>;
@@ -1,7 +1,7 @@
1
1
  import { type Ref } from 'vue';
2
2
  import type { CoreState } from './core-state';
3
3
  import type { UndoStylesState } from './undo-styles';
4
- import type { BorderType } from '../components/pickers/borderPicker.vue';
4
+ import type { BorderType } from '../components/pickers/border-picker.vue';
5
5
  import type { MergeType } from '../core/constants';
6
6
  import type { SelectionRange, BorderStyle, BorderSide } from '../core/types';
7
7
  export interface BordersMergeState {
@@ -25,6 +25,8 @@ export interface BordersMergeState {
25
25
  copySourceRange: SelectionRange | null;
26
26
  copySourceStyles: number[][];
27
27
  captureStyles: (cS: number, cE: number, rS: number, rE: number) => void;
28
+ /** 复制选区与源选区相交的数据验证规则(相对源选区左上角归一化,便于粘贴平移) */
29
+ captureDataValidations: (cS: number, cE: number, rS: number, rE: number) => void;
28
30
  setCellWithStyle: (c: number, r: number, val: string, styleId: number | null) => void;
29
31
  copyToClipboard: () => void;
30
32
  copyRowCol: () => void;
@@ -1,7 +1,8 @@
1
1
  import { type ComputedRef, type Ref } from 'vue';
2
2
  import { FormulaDeps } from '../core/formula';
3
- import type { CellCoord, CellData, CellStyle, SelectionRange, BorderStyle, BorderSide, FreezePane, ViewportRegion, SheetFilter, FilterColumn, ConditionalFormattingRule, ConditionalFormattingFormat } from '../core/types';
3
+ import type { CellCoord, CellData, CellStyle, SelectionRange, BorderStyle, BorderSide, FreezePane, ViewportRegion, SheetFilter, FilterColumn, ConditionalFormattingRule, ConditionalFormattingFormat, DataValidationRule, DataValidationResult, DataValidationSeverity, DimensionOutline } from '../core/types';
4
4
  import { type FilterCandidates } from '../core/filter-core';
5
+ import { type OutlineValidationResult } from '../core/outline-core';
5
6
  /** 选区触发方式,影响「合并单元格是否扩大选区」。
6
7
  * - 'cell':单元格点击/拖动(默认)→ 保持现有 expandSelectionForMerges 行为
7
8
  * - 'row' :行头点击/拖动 → Excel 风格「穿透」合并,选区矩形保持用户点击的行范围
@@ -9,6 +10,20 @@ import { type FilterCandidates } from '../core/filter-core';
9
10
  * - 'all' :左上角全选按钮 → expandSelectionForMerges 行为
10
11
  */
11
12
  export type SelectionMode = 'cell' | 'row' | 'col' | 'all';
13
+ /** 数据验证出错警告的用户选择:
14
+ * - 'continue':确认继续(写入非法值);
15
+ * - 'retry' :返回编辑态修正(stop 专用);
16
+ * - 'cancel' :放弃本次输入(不写入)。
17
+ */
18
+ export type DataValidationAlertAction = 'continue' | 'retry' | 'cancel';
19
+ /** 出错警告弹窗需要展示的信息 */
20
+ export interface DataValidationAlertPayload {
21
+ severity: DataValidationSeverity;
22
+ title: string;
23
+ message: string;
24
+ col: number;
25
+ row: number;
26
+ }
12
27
  /** AutoFill 拖拽状态 */
13
28
  export interface AutoFillState {
14
29
  active: boolean;
@@ -88,6 +103,45 @@ export interface CoreState {
88
103
  getVisibleRowCount: () => number;
89
104
  getVisibleRowAt: (index: number) => number;
90
105
  getVisibleRowIndex: (row: number) => number;
106
+ /** 当前工作表行分组集合(稳定 ID,展开/折叠状态) */
107
+ getRowOutlines: () => DimensionOutline[];
108
+ /** 当前工作表列分组集合 */
109
+ getColumnOutlines: () => DimensionOutline[];
110
+ /** 在某组上创建分组;返回是否成功(失败给出提示码) */
111
+ addRowGroup: (start: number, end: number) => OutlineValidationResult;
112
+ addColumnGroup: (start: number, end: number) => OutlineValidationResult;
113
+ /** 取消组合:完整覆盖某个分组才允许 Ungroup,返回是否成功 */
114
+ removeOutline: (id: string) => boolean;
115
+ clearRowGroups: () => void;
116
+ clearColumnGroups: () => void;
117
+ clearAllOutlines: () => void;
118
+ /** 设置分组折叠状态(一次一个视图状态操作,不影响结构) */
119
+ setOutlineCollapsed: (id: string, collapsed: boolean, silent?: boolean) => void;
120
+ toggleOutline: (id: string) => void;
121
+ /** 当前最大分组层级(0 = 无分组) */
122
+ getOutlineLevel: (axis: 'row' | 'column') => number;
123
+ /** Outline gutter 总尺寸:无分组为 0;否则 = 等级控件条 + 层级数×单级宽度。axis='row' → 左侧 gutter 宽,axis='column' → 顶部 gutter 高 */
124
+ getOutlineGutterSize: (axis: 'row' | 'column') => number;
125
+ /** 逻辑行/列当前所处的分组层级(0 = 不在任何分组内) */
126
+ getRowOutlineLevel: (row: number) => number;
127
+ getColumnOutlineLevel: (col: number) => number;
128
+ /** 逻辑行/列当前是否被折叠分组隐藏 */
129
+ isRowCollapsed: (row: number) => boolean;
130
+ isColumnCollapsed: (col: number) => boolean;
131
+ /** 逻辑行列是否可见(Filter + Outline 组合) */
132
+ isRowVisible: (row: number) => boolean;
133
+ isColumnVisible: (col: number) => boolean;
134
+ /** 列是否隐藏(当前仅由列分组折叠决定) */
135
+ isColHidden: (col: number) => boolean;
136
+ /** 逻辑列宽(折叠列返回 0,供布局/命中测试使用) */
137
+ getColWidth: (c: number) => number;
138
+ /** outline 引擎注入:行列增删后的结构调整(由 sheets-ops 调用) */
139
+ adjustOutlinesForInsertRows: (index: number, count: number) => void;
140
+ adjustOutlinesForDeleteRows: (index: number, count: number) => void;
141
+ adjustOutlinesForInsertCols: (index: number, count: number) => void;
142
+ adjustOutlinesForDeleteCols: (index: number, count: number) => void;
143
+ /** 设置当前工作表的分组集合(加载/撤销恢复等受控场景;同时重算层级) */
144
+ syncOutlines: (rowOutlines: DimensionOutline[] | undefined, columnOutlines: DimensionOutline[] | undefined) => void;
91
145
  /** 基于真实行高/列宽计算冻结区域尺寸 */
92
146
  getFrozenMetrics: () => {
93
147
  frozenRowsHeight: number;
@@ -157,7 +211,9 @@ export interface CoreState {
157
211
  setCellValue: (c: number, r: number, v: string | null | undefined) => void;
158
212
  clearCellsInRange: (cS: number, cE: number, rS: number, rE: number) => void;
159
213
  startEdit: (initialValue?: string) => void;
160
- commitEdit: () => void;
214
+ /** 提交编辑:数据验证发生在写入之前。
215
+ * @returns Promise<boolean> —— true 表示已写入;false 表示被数据验证拦截(仍保持编辑态) */
216
+ commitEdit: () => Promise<boolean>;
161
217
  cancelEdit: () => void;
162
218
  moveActive: (dC: number, dR: number) => void;
163
219
  ensureVisible: (c: number, r: number) => void;
@@ -203,6 +259,69 @@ export interface CoreState {
203
259
  clampScroll?: (sx: number | null, sy: number | null) => void;
204
260
  /** 查找高亮钩子:返回某单元格当前的高亮类型(由 find-replace 模块注入) */
205
261
  findHighlight?: (col: number, row: number) => 'active' | 'match' | null;
262
+ /** 当前工作表的全部数据验证规则(规则属于 Sheet,不属于 Cell) */
263
+ dataValidations: DataValidationRule[];
264
+ /** 命中该单元格的全部规则(经空间索引,不遍历整表) */
265
+ getDataValidationRules: (row: number, col: number) => DataValidationRule[];
266
+ /** 命中该单元格的首条规则(用于「打开数据验证对话框」时回填已有规则) */
267
+ getDataValidationRule: (row: number, col: number) => DataValidationRule | null;
268
+ hasDataValidation: (row: number, col: number) => boolean;
269
+ /** 命中且需要显示下拉箭头的 list 规则 */
270
+ getListValidation: (row: number, col: number) => DataValidationRule | null;
271
+ /** 下拉列表数据(去重、跟随源区域变化) */
272
+ getValidationDropdown: (row: number, col: number) => {
273
+ rule: DataValidationRule;
274
+ items: string[];
275
+ } | null;
276
+ /** 选中单元格时展示的输入信息 */
277
+ getValidationInputMessage: (row: number, col: number) => {
278
+ title: string;
279
+ message: string;
280
+ } | null;
281
+ /** 核心校验入口:编辑 / 粘贴 / AutoFill 等所有输入链路统一调用 */
282
+ validateCell: (row: number, col: number, value: string | null | undefined) => DataValidationResult;
283
+ /** 批量校验(粘贴 / 填充等原子操作):返回最严重的那条失败结果 */
284
+ validateCells: (entries: {
285
+ col: number;
286
+ row: number;
287
+ value: string | null | undefined;
288
+ }[]) => DataValidationResult;
289
+ /** 新建规则(一个完整的规则级 Undo) */
290
+ createDataValidation: (rule: DataValidationRule, opts?: {
291
+ silent?: boolean;
292
+ }) => DataValidationRule;
293
+ /** 更新既有规则(按 id),不存在则视为新建 */
294
+ updateDataValidation: (id: string, patch: Partial<DataValidationRule>, opts?: {
295
+ silent?: boolean;
296
+ }) => void;
297
+ removeDataValidation: (id: string, opts?: {
298
+ silent?: boolean;
299
+ }) => void;
300
+ /** 清除指定范围的验证(规则的 Range 会被拆分/缩小);range 为 null 时清除整个工作表 */
301
+ clearDataValidation: (range: SelectionRange | null, opts?: {
302
+ silent?: boolean;
303
+ }) => void;
304
+ /** 与给定矩形相交的规则(复制携带规则时用) */
305
+ getDataValidationsInRange: (range: SelectionRange) => {
306
+ rule: DataValidationRule;
307
+ range: SelectionRange;
308
+ }[];
309
+ /** 将一批规则应用到目标矩形(先扣除目标区域已有规则,再写入) */
310
+ applyDataValidationsToRange: (rules: DataValidationRule[], target: SelectionRange, opts?: {
311
+ silent?: boolean;
312
+ }) => void;
313
+ /** 规则集合 / 源区域数据变化后失效索引与列表缓存 */
314
+ invalidateDataValidationCache: () => void;
315
+ /** 出错警告弹窗是否打开(编辑器 blur 提交需要避让,避免重入) */
316
+ isValidationAlertOpen: () => boolean;
317
+ /** 出错警告弹窗钩子(由 spreader.vue 注入;未注入时按「stop 拒绝 / 其它放行」处理) */
318
+ showValidationAlert?: (payload: DataValidationAlertPayload) => Promise<DataValidationAlertAction>;
319
+ /** 复用出错警告流程:传入校验失败结果,返回用户决策(供下拉选择等非编辑器入口调用) */
320
+ confirmInvalidValue: (res: DataValidationResult, col: number, row: number) => Promise<DataValidationAlertAction>;
321
+ /** 按 id 或名称取其它工作表的 cells(list 跨表引用,由 spreader.vue 注入) */
322
+ getSheetCellsById?: (sheetId: string) => Record<string, CellData> | null;
323
+ /** 请求打开「数据验证」对话框(由 spreader.vue 注入,供右键菜单 / 工具栏使用) */
324
+ requestDataValidationDialog?: () => void;
206
325
  /** AutoFill 拖拽状态:active 时 render 绘制预览,mouseup 触发 applyAutoFill */
207
326
  autoFillState: Ref<AutoFillState>;
208
327
  /** 一次性提交 AutoFill:saveUndo → ensureCapacity → applyAutoFillPlan → 写入 cells + formulaDeps → selectRange → scheduleRender */
@@ -3,7 +3,20 @@ import type { CoreState } from './core-state';
3
3
  import type { UndoStylesState } from './undo-styles';
4
4
  import type { BordersMergeState } from './borders-merge';
5
5
  import type { SheetsOpsState } from './sheets-ops';
6
- import type { ContextMenuItem } from '../core/types';
6
+ import type { ContextMenuItem, DataValidationRule } from '../core/types';
7
+ /** 数据验证下拉弹窗状态:anchor 为单元格在视口中的位置(client 坐标) */
8
+ export interface ValidationDropdownState {
9
+ col: number;
10
+ row: number;
11
+ rule: DataValidationRule;
12
+ items: string[];
13
+ /** 当前单元格原始值(用于标记选中项) */
14
+ current: string;
15
+ x: number;
16
+ y: number;
17
+ width: number;
18
+ height: number;
19
+ }
7
20
  export interface InteractionsState {
8
21
  scheduleRender: () => void;
9
22
  render: () => void;
@@ -47,6 +60,15 @@ export interface InteractionsState {
47
60
  isFilterButtonHit: (x: number, y: number) => number;
48
61
  openFilterPopup: (col: number) => void;
49
62
  closeFilterPopup: () => void;
63
+ validationDropdown: Ref<ValidationDropdownState | null>;
64
+ /** 屏幕坐标(client)是否命中某单元格的下拉箭头;命中返回该单元格坐标(合并格返回左上角) */
65
+ isValidationDropdownHit: (x: number, y: number) => {
66
+ col: number;
67
+ row: number;
68
+ } | null;
69
+ openValidationDropdown: (col: number, row: number) => boolean;
70
+ closeValidationDropdown: () => void;
71
+ onValidationDropdownSelect: (value: string) => void;
50
72
  dimInputRef: Ref<HTMLInputElement | null>;
51
73
  dimPanel: Ref<{
52
74
  type: 'row' | 'col';
@@ -61,6 +83,16 @@ export interface InteractionsState {
61
83
  onDimBlur: () => void;
62
84
  applyDimPanel: () => void;
63
85
  closeDimPanel: () => void;
86
+ outlineGroupRows: () => boolean;
87
+ outlineGroupCols: () => boolean;
88
+ outlineUngroupRows: () => boolean;
89
+ outlineUngroupCols: () => boolean;
90
+ outlineExpandRows: () => void;
91
+ outlineCollapseRows: () => void;
92
+ outlineExpandCols: () => void;
93
+ outlineCollapseCols: () => void;
94
+ /** 分组校验提示:由宿主注入的应用内对话框;未注入时回退 window.alert */
95
+ showOutlineAlert?: (message: string) => void;
64
96
  editInputStyle: ComputedRef<Record<string, string | number | undefined>>;
65
97
  hScrollbarW: ComputedRef<number>;
66
98
  vScrollbarH: ComputedRef<number>;
@@ -104,12 +104,21 @@ export declare function computeTargetRange(sourceRange: SelectionRange, draggedC
104
104
  * 平移公式引用以适配目标单元格($ 绝对引用由 shiftFormulaRefs 处理)。
105
105
  */
106
106
  export declare function translateFormulaForTarget(sourceFormula: string, sourceCell: CellCoord, targetCell: CellCoord, colCount: number, rowCount: number, colToLabelFn: (col: number) => string): string;
107
+ /** 合并兼容性校验结果:
108
+ * - 'ok':兼容;
109
+ * - 'source-overlap':源区域与某合并格部分相交(既非完全包含也非不相交);
110
+ * - 'target-merge-size':目标区域命中的合并格跨距不一致(对齐 Excel「要执行此操作,所有合并单元格必须大小相同」)。
111
+ */
112
+ export type MergeCompatibilityResult = {
113
+ ok: boolean;
114
+ reason: 'ok' | 'source-overlap' | 'target-merge-size';
115
+ };
107
116
  /**
108
117
  * 校验源/目标区域与已有合并的兼容性:
109
118
  * - 源区域不能与任何 merge 部分相交(要么完全包含,要么不相交);
110
- * - 目标区域不能与任何已有 merge 相交(会破坏 merge)。
119
+ * - 目标区域与「未被源完全包含」的 merge 相交时,要求这些 merge 行/列跨距全部一致。
111
120
  */
112
- export declare function validateMergeCompatibility(sourceRange: SelectionRange, targetRange: SelectionRange, merges: Record<string, SelectionRange>): boolean;
121
+ export declare function validateMergeCompatibility(sourceRange: SelectionRange, targetRange: SelectionRange, merges: Record<string, SelectionRange>): MergeCompatibilityResult;
113
122
  /**
114
123
  * 自动填充核心:根据源/目标区域与方向生成填充增量(纯函数,不 mutate sheet)。
115
124
  * - down/right:正向延伸;up/left:反向延伸。
@@ -13,6 +13,8 @@ export declare const SCROLL_STEP = 50;
13
13
  export declare const FILL_HANDLE_SIZE = 6;
14
14
  export declare const FILL_HANDLE_HIT_PADDING = 4;
15
15
  export declare const UNDO_MAX = 50;
16
+ export declare const OUTLINE_PANEL = 16;
17
+ export declare const OUTLINE_GUTTER = 15;
16
18
  export declare const i18n: Record<string, Record<string, string>>;
17
19
  export declare function t(locale: string, key: string): string;
18
20
  export declare const DEFAULT_FONT_FAMILY = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Microsoft YaHei\", sans-serif";
@@ -0,0 +1,119 @@
1
+ import type { CellData, SelectionRange, DataValidationRule, DataValidationResult, DataValidationOperator, DataValidationSeverity } from './types';
2
+ /** 数据验证求值上下文(由 core-state 注入,引擎本身保持纯净) */
3
+ export interface DataValidationContext {
4
+ /** 当前工作表原始单元格数据(供公式引擎引用) */
5
+ cells: Record<string, CellData>;
6
+ colCount: number;
7
+ rowCount: number;
8
+ locale: string;
9
+ /** 取单元格计算后的值(公式已求值) */
10
+ getCellValue: (col: number, row: number) => string;
11
+ /** 按 id 或名称取其它工作表的 cells;用于 list 类型跨表引用。取不到返回 null */
12
+ getSheetCells?: (sheetId: string) => Record<string, CellData> | null;
13
+ }
14
+ /** 生成稳定唯一 ID(供新建规则使用,禁止依赖数组 index) */
15
+ export declare function genDataValidationId(): string;
16
+ /** 单元格是否落在任一应用范围之内(含边界) */
17
+ export declare function dvCellInRanges(ranges: SelectionRange[], col: number, row: number): boolean;
18
+ /** 规则范围左上角(所有 ranges 的最小 startCol/startRow),作为相对引用的基准 */
19
+ export declare function dvRuleAnchor(rule: DataValidationRule): {
20
+ col: number;
21
+ row: number;
22
+ };
23
+ /**
24
+ * 空值判定:null / undefined / '' / 纯空白 均视为空。
25
+ * 与「不存在的单元格」在业务上等价(本项目单元格缺省即空字符串)。
26
+ */
27
+ export declare function isBlankValue(value: unknown): boolean;
28
+ /** 文本长度:按 Unicode 码点统计(中文 / Emoji 不会像 UTF-16 那样被算成 2) */
29
+ export declare function dvTextLength(value: string): number;
30
+ /**
31
+ * 将「操作数」(单元格输入值或规则条件值)解析为数值。
32
+ * - 前导 '=' 视为公式,求值后取数值结果(如 =TODAY());公式错误返回 null;
33
+ * - 复用项目 parseNumericText 处理千分位 / 百分比 / 货币符号;
34
+ * - 其余走严格数值正则,避免 Number('0x10') 之类的误判。
35
+ */
36
+ export declare function parseNumericOperand(raw: string, ctx: DataValidationContext): number | null;
37
+ /**
38
+ * 将操作数解析为「日期序列号」。
39
+ * - 纯数值 → 直接作为序列号(Excel 日期序列);
40
+ * - 日期/日期时间字符串 → 复用项目 parseDateTimeInput(与单元格输入识别同源);
41
+ * - 纯时间字符串(如 12:30)不属于日期 → null。
42
+ */
43
+ export declare function parseDateOperand(raw: string, ctx: DataValidationContext): number | null;
44
+ /**
45
+ * 将操作数解析为「时间」(一天的分数,0 <= x < 1)。
46
+ * - 纯时间字符串 → 复用 parseDateTimeInput;
47
+ * - 日期时间字符串 → 取小数部分;
48
+ * - 数值 → 取小数部分(Excel 时间即序列的小数部分)。
49
+ */
50
+ export declare function parseTimeOperand(raw: string, ctx: DataValidationContext): number | null;
51
+ /** 通用比较:op 作用于 (value, v1[, v2]);操作数解析失败或比较数缺失一律视为不通过(返回 false) */
52
+ export declare function compareWithOperator(op: DataValidationOperator, value: number, v1: number | null, v2: number | null): boolean;
53
+ /**
54
+ * 解析 list 规则的候选项(去重、保序)。
55
+ * - values:直接使用常量列表;
56
+ * - range:读取目标区域(默认当前表,可通过 sheetId 跨表),空单元格按 allowBlank 决定是否保留。
57
+ * 本函数不做缓存(缓存与失效由调用方 core-state 负责)。
58
+ */
59
+ export declare function resolveListItems(rule: DataValidationRule, ctx: DataValidationContext): string[];
60
+ /** list 规则是否显示单元格内下拉箭头 */
61
+ export declare function hasDropdownIndicator(rule: DataValidationRule | null | undefined): boolean;
62
+ /** 单条规则求值:value 为待写入的原始文本(不含隐式类型转换),col/row 为目标单元格坐标 */
63
+ export declare function evaluateDataValidationRule(rule: DataValidationRule, value: string | null | undefined, col: number, row: number, ctx: DataValidationContext): boolean;
64
+ /** errorStyle → severity(未配置默认 stop) */
65
+ export declare function dvSeverityOf(rule: DataValidationRule): DataValidationSeverity;
66
+ /**
67
+ * 校验某个单元格的候选值。
68
+ * 规则:命中范围内的**所有**规则必须全部通过(rules.every);
69
+ * 失败时取严重级别最高(stop > warning > information)的那条规则生成提示。
70
+ */
71
+ export declare function validateCellValue(value: string | null | undefined, col: number, row: number, rules: DataValidationRule[], ctx: DataValidationContext): DataValidationResult;
72
+ /**
73
+ * 生成默认错误提示文案(用户未自定义 title/message 时使用)。
74
+ * 文案按类型描述约束条件,便于用户理解「为什么非法」。
75
+ */
76
+ export declare function defaultDataValidationText(rule: DataValidationRule, locale: string): {
77
+ title: string;
78
+ message: string;
79
+ };
80
+ /**
81
+ * 数据验证规则空间索引。
82
+ * - rebuild(rules):重建(随规则变化调用,成本 O(区域总数));
83
+ * - getRules(col,row):返回命中该单元格且启用的规则列表。
84
+ */
85
+ export declare class DataValidationIndex {
86
+ private bands;
87
+ private dirty;
88
+ private source;
89
+ constructor(rules?: DataValidationRule[]);
90
+ /** 规则集合发生变化(增/删/改/切表/撤销恢复)后调用 */
91
+ invalidate(): void;
92
+ private ensure;
93
+ /** 命中该单元格的规则(保持源数组顺序) */
94
+ getRules(col: number, row: number, rules?: DataValidationRule[]): DataValidationRule[];
95
+ /** 该单元格是否存在任何数据验证 */
96
+ has(col: number, row: number, rules?: DataValidationRule[]): boolean;
97
+ /**
98
+ * 命中该单元格且满足 predicate 的首条规则。
99
+ * 渲染热路径(每格判定是否画下拉箭头)专用:不分配数组,零垃圾。
100
+ */
101
+ findRule(col: number, row: number, predicate: (rule: DataValidationRule) => boolean, rules?: DataValidationRule[]): DataValidationRule | null;
102
+ /** 取某坐标所在行带的候选条目(必要时先重建索引) */
103
+ private bandsOf;
104
+ }
105
+ /** 删除带(axis='row' 删行 / 'col' 删列)后收缩单个 Range;整段落在删除带内返回 null */
106
+ export declare function adjustDvRangeForDelete(r: SelectionRange, axis: 'row' | 'col', idx: number, count: number): SelectionRange | null;
107
+ /** 插入带(axis='row' 插行 / 'col' 插列)后放大单个 Range */
108
+ export declare function adjustDvRangeForInsert(r: SelectionRange, axis: 'row' | 'col', idx: number, count: number): SelectionRange;
109
+ /** 规则区域是否与给定矩形相交 */
110
+ export declare function dvRangeIntersects(a: SelectionRange, b: SelectionRange): boolean;
111
+ /**
112
+ * 从规则中扣除给定矩形(用于「清除所选区域的验证」):
113
+ * 支持矩形差集拆分,剩余为空时返回空数组。
114
+ */
115
+ export declare function subtractDvRange(range: SelectionRange, cut: SelectionRange): SelectionRange[];
116
+ /** 平移规则的某个区域(复制粘贴携带规则时使用) */
117
+ export declare function translateDvRange(r: SelectionRange, dCol: number, dRow: number): SelectionRange;
118
+ /** 求两个矩形的交集;不相交返回 null */
119
+ export declare function intersectDvRange(a: SelectionRange, b: SelectionRange): SelectionRange | null;