Skip to content

Commit 2f12d98

Browse files
committed
feat: squash merge s2-vue-components
1 parent 085a35e commit 2f12d98

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+4402
-175
lines changed

.agent/skills/s2-lint/SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ description: After modifying S2 project code, you must run lint to ensure there
1010
**After modifying any code files in the `packages/` directory, you must use this skill before finishing the task.**
1111

1212
This includes but is not limited to:
13+
1314
- Modifying `.ts`, `.tsx`, `.vue` files
1415
- Adding new source code files
1516
- Modifying type definition files (`.d.ts`)
@@ -26,10 +27,11 @@ pnpm lint
2627
```
2728

2829
This command runs the following checks sequentially:
30+
2931
- `lint:type` - TypeScript type checking
3032
- `lint:script` - ESLint code style checking
3133
- `lint:style` - Stylelint CSS/LESS checking
32-
- `lint:docs` - Markdownlint documentation checking
34+
- `lint:docs` - MarkdownLint documentation checking
3335
- `lint:word` - Case-police word casing checking
3436

3537
## Handling Errors

.agent/skills/s2-unit-test/SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ description: Guidelines for writing and maintaining unit tests in the S2 project
88
## When to Use This Skill
99

1010
Use this skill when you:
11+
1112
- Modify code under `packages/*/src/`
1213
- Fix bugs (especially with issue numbers)
1314
- Add new features or functions
@@ -20,7 +21,7 @@ Use this skill when you:
2021

2122
Search for `__tests__` directories to find where tests for the modified file already exist:
2223

23-
```
24+
```text
2425
packages/s2-core/__tests__/unit/ # Unit tests organized by module
2526
packages/s2-core/__tests__/bugs/ # Bug regression tests with issue numbers
2627
packages/s2-core/__tests__/spreadsheet/ # Integration-level spreadsheet tests
@@ -150,6 +151,7 @@ pnpm --filter @antv/s2 test:coverage
150151
## Goal
151152

152153
The primary goal of unit tests is to:
154+
153155
- **Increase line coverage** - Every line of src code should be exercised
154156
- **Increase branch coverage** - Test all conditional paths
155157
- **Prevent regressions** - Ensure bugs don't reappear

packages/s2-core/src/renderer/BaseRenderer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { DisplayObjectConfig, Path } from '@antv/g';
2+
import flru, { flruCache } from 'flru';
23
import type { BaseCell } from '../cell';
34
import { CellClipBox } from '../common/interface';
45
import { CustomRendererConfig } from '../common/interface/renderer';
56
import { SimpleBBox } from '../engine';
67
import { getPreparedText } from '../utils/cell/customRenderer';
78

89
export abstract class BaseRenderer {
9-
static mediaCache = new Map<string, HTMLElement | null>();
10+
static mediaCache: flruCache<HTMLElement | null> = flru(200);
1011

1112
abstract prepare(
1213
renderer: CustomRendererConfig,

packages/s2-core/src/utils/cell/customRenderer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import type { flruCache } from 'flru';
2+
13
const loadError = new Error('Failed to load image and fallback');
24

35
export function asyncDrawImage(options: {
46
src: string;
57
fallback?: string;
68
timeout?: number;
7-
mediaCache?: Map<string, HTMLElement | null>;
9+
mediaCache?: flruCache<HTMLElement | null>;
810
crossOrigin?: string | null;
911
}): Promise<HTMLImageElement> {
1012
const {

packages/s2-vue/package.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,12 @@
5555
"tsc": "vue-tsc --noEmit"
5656
},
5757
"dependencies": {
58+
"@ant-design/icons-vue": "^6.1.0",
5859
"@vueuse/core": "^10.5.0",
59-
"lodash": "^4.17.21"
60+
"lodash": "^4.17.21",
61+
"tinycolor2": "^1.4.2",
62+
"tinygradient": "^1.1.5",
63+
"vuedraggable": "^4.1.0"
6064
},
6165
"devDependencies": {
6266
"@antv/event-emitter": "^0.1.3",
@@ -85,11 +89,14 @@
8589
{
8690
"path": "./dist/s2-vue.min.js",
8791
"import": "{ createComponent }",
88-
"limit": "38 kB",
92+
"limit": "65 kB",
8993
"ignore": [
9094
"S2",
9195
"Vue",
92-
"antd"
96+
"ant-design-vue",
97+
"lodash",
98+
"@vueuse/core",
99+
"@ant-design/icons-vue"
93100
]
94101
},
95102
{

packages/s2-vue/playground/App.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
// Import playground components
3434
import BigDataSheetDemo from './components/BigDataSheet.vue';
3535
import ChartSheetDemo from './components/ChartSheet.vue';
36+
import ComponentsPlaygroundDemo from './components/ComponentsPlayground.vue';
3637
import CustomGridDemo from './components/CustomGrid.vue';
3738
import CustomTreeDemo from './components/CustomTree.vue';
3839
import EditableSheetDemo from './components/EditableSheet.vue';
@@ -635,6 +636,9 @@ const setThemeCfg = (cb: any) => {
635636
<a-tab-pane key="bigData" tab="100万数据">
636637
<BigDataSheetDemo />
637638
</a-tab-pane>
639+
<a-tab-pane key="components" tab="组件扩展">
640+
<ComponentsPlaygroundDemo />
641+
</a-tab-pane>
638642
</a-tabs>
639643
</div>
640644
</template>
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
<script setup lang="ts">
2+
/* eslint-disable no-console */
3+
import {
4+
type SpreadSheet,
5+
type ThemeCfg,
6+
type ThemeName,
7+
type S2Options,
8+
} from '@antv/s2';
9+
import { Button, Space, Popover } from 'ant-design-vue';
10+
import { ref, shallowRef, computed } from 'vue';
11+
import {
12+
SheetComponent,
13+
ThemePanel,
14+
TextAlignPanel,
15+
FrozenPanel,
16+
Switcher,
17+
AdvancedSort,
18+
DrillDown,
19+
StrategyExport,
20+
} from '../../src';
21+
import type {
22+
ThemePanelOptions,
23+
TextAlignPanelOptions,
24+
FrozenPanelOptions,
25+
} from '../../src';
26+
import { pivotSheetDataCfg, defaultOptions } from '../config';
27+
28+
const s2Ref = shallowRef<SpreadSheet>();
29+
const themeCfg = ref<ThemeCfg>({
30+
name: 'default',
31+
});
32+
const options = ref<S2Options>({
33+
...defaultOptions,
34+
width: 800,
35+
height: 600,
36+
});
37+
38+
const onSheetMounted = (instance: SpreadSheet) => {
39+
s2Ref.value = instance;
40+
console.log('onMounted:', instance);
41+
};
42+
43+
const onThemeChange = (panelOptions: ThemePanelOptions, theme: any) => {
44+
themeCfg.value = {
45+
name: panelOptions.themeType as ThemeName,
46+
theme,
47+
};
48+
if (s2Ref.value) {
49+
s2Ref.value.setOptions({
50+
hierarchyType: panelOptions.hierarchyType,
51+
});
52+
s2Ref.value.render(false);
53+
}
54+
55+
console.log('ThemePanel onChange:', panelOptions, theme);
56+
};
57+
58+
const onThemeReset = (
59+
panelOptions: ThemePanelOptions,
60+
prevOptions: ThemePanelOptions,
61+
theme: any,
62+
) => {
63+
console.log('ThemePanel onReset:', panelOptions, prevOptions, theme);
64+
};
65+
66+
const onTextAlignChange = (panelOptions: TextAlignPanelOptions, theme: any) => {
67+
themeCfg.value = {
68+
...themeCfg.value,
69+
theme,
70+
};
71+
if (s2Ref.value) {
72+
s2Ref.value.render(false);
73+
}
74+
75+
console.log('TextAlignPanel onChange:', panelOptions, theme);
76+
};
77+
78+
const onTextAlignReset = (
79+
panelOptions: TextAlignPanelOptions,
80+
prevOptions: TextAlignPanelOptions,
81+
theme: any,
82+
) => {
83+
console.log('TextAlignPanel onReset:', panelOptions, prevOptions, theme);
84+
};
85+
86+
const onFrozenChange = (panelOptions: FrozenPanelOptions) => {
87+
const [rowCount = 0, trailingRowCount = 0] = panelOptions.frozenRow;
88+
const [colCount = 0, trailingColCount = 0] = panelOptions.frozenCol;
89+
90+
if (s2Ref.value) {
91+
s2Ref.value.setOptions({
92+
frozen: {
93+
rowHeader: panelOptions.frozenRowHeader,
94+
rowCount,
95+
colCount,
96+
trailingRowCount,
97+
trailingColCount,
98+
},
99+
});
100+
s2Ref.value.render(false);
101+
}
102+
103+
console.log('FrozenPanel onChange:', panelOptions);
104+
};
105+
106+
const onFrozenReset = (
107+
panelOptions: FrozenPanelOptions,
108+
prevOptions: FrozenPanelOptions,
109+
) => {
110+
console.log('FrozenPanel onReset:', panelOptions, prevOptions);
111+
};
112+
113+
const toSwitcherItems = (fields: any[] = []) => {
114+
return fields.map((f) => {
115+
return typeof f === 'string' ? { id: f } : { id: f.field, ...f };
116+
});
117+
};
118+
119+
// Switcher Logic
120+
const switcherFields = ref({
121+
rows: pivotSheetDataCfg.fields.rows,
122+
columns: pivotSheetDataCfg.fields.columns,
123+
values: pivotSheetDataCfg.fields.values,
124+
});
125+
126+
const dataCfg = computed(() => {
127+
return {
128+
...pivotSheetDataCfg,
129+
fields: {
130+
...pivotSheetDataCfg.fields,
131+
...switcherFields.value,
132+
},
133+
};
134+
});
135+
136+
const onSwitcherSubmit = ({ rows, columns, values }: any) => {
137+
console.log('Switcher onSubmit:', rows, columns, values);
138+
switcherFields.value = {
139+
rows: rows.items.map((i: any) => i.id),
140+
columns: columns.items.map((i: any) => i.id),
141+
values: values.items.map((i: any) => i.id),
142+
};
143+
if (s2Ref.value) {
144+
s2Ref.value.render(false);
145+
}
146+
};
147+
148+
// Advanced Sort Logic
149+
const onSortConfirm = (ruleValues: any[], sortParams: any[]) => {
150+
console.log('AdvancedSort onConfirm:', ruleValues, sortParams);
151+
if (s2Ref.value) {
152+
s2Ref.value.setDataCfg({
153+
...s2Ref.value.dataCfg,
154+
sortParams,
155+
});
156+
s2Ref.value.render(false);
157+
}
158+
};
159+
160+
// Drill Down Logic
161+
const drillFields = ref<string[]>([]);
162+
</script>
163+
164+
<template>
165+
<div class="components-playground">
166+
<Space direction="horizontal" class="config-panels">
167+
<ThemePanel
168+
title="主题配置"
169+
:disableCustomPrimaryColorPicker="false"
170+
:defaultCollapsed="false"
171+
@change="onThemeChange"
172+
@reset="onThemeReset"
173+
/>
174+
<TextAlignPanel
175+
title="文字对齐"
176+
:defaultCollapsed="false"
177+
@change="onTextAlignChange"
178+
@reset="onTextAlignReset"
179+
/>
180+
<FrozenPanel
181+
title="冻结行列头"
182+
:defaultCollapsed="false"
183+
:inputNumberProps="{
184+
size: 'small',
185+
step: 1,
186+
}"
187+
:defaultOptions="{
188+
frozenRow: [1, 2],
189+
}"
190+
@change="onFrozenChange"
191+
@reset="onFrozenReset"
192+
/>
193+
<Switcher
194+
title="行列切换"
195+
:rows="{ items: toSwitcherItems(dataCfg.fields.rows) }"
196+
:columns="{ items: toSwitcherItems(dataCfg.fields.columns) }"
197+
:values="{ items: toSwitcherItems(dataCfg.fields.values) }"
198+
@submit="onSwitcherSubmit"
199+
/>
200+
<AdvancedSort
201+
v-if="s2Ref"
202+
:sheetInstance="s2Ref"
203+
@sort-confirm="onSortConfirm"
204+
/>
205+
<Popover trigger="click" placement="bottomLeft">
206+
<template #content>
207+
<DrillDown
208+
:dataSet="[
209+
{ name: '地区', value: 'area', type: 'location' },
210+
{ name: '城市', value: 'city', type: 'location' },
211+
{ name: '日期', value: 'date', type: 'date' },
212+
]"
213+
v-model:drillFields="drillFields"
214+
/>
215+
</template>
216+
<Button>下钻</Button>
217+
</Popover>
218+
<StrategyExport v-if="s2Ref" :sheetInstance="s2Ref" />
219+
</Space>
220+
<SheetComponent
221+
:dataCfg="dataCfg"
222+
:options="options"
223+
:themeCfg="themeCfg"
224+
sheetType="pivot"
225+
@mounted="onSheetMounted"
226+
adaptive
227+
/>
228+
</div>
229+
</template>
230+
231+
<style lang="less" scoped>
232+
.components-playground {
233+
display: flex;
234+
flex-direction: column;
235+
gap: 20px;
236+
237+
.config-panels {
238+
display: flex;
239+
flex-wrap: wrap;
240+
align-items: flex-start;
241+
}
242+
}
243+
</style>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './theme';

0 commit comments

Comments
 (0)