diff --git a/__tests__/unit/util/padding.spec.ts b/__tests__/unit/util/padding.spec.ts new file mode 100644 index 000000000..5cfbf6398 --- /dev/null +++ b/__tests__/unit/util/padding.spec.ts @@ -0,0 +1,27 @@ +import { normalPadding } from '../../../src/util'; + +describe('padding', () => { + test('undefined', async () => { + expect(normalPadding(undefined)).toStrictEqual([0, 0, 0, 0]); + }); + + test('number', async () => { + expect(normalPadding(1)).toStrictEqual([1, 1, 1, 1]); + }); + + test('array1', async () => { + expect(normalPadding([2])).toStrictEqual([2, 2, 2, 2]); + }); + + test('array2', async () => { + expect(normalPadding([1, 2])).toStrictEqual([1, 2, 1, 2]); + }); + + test('array3', async () => { + expect(normalPadding([1, 2, 3])).toStrictEqual([1, 2, 3, 2]); + }); + + test('array4', async () => { + expect(normalPadding([1, 2, 3, 4])).toStrictEqual([1, 2, 3, 4]); + }); +}); diff --git a/__tests__/unit/util/style.spec.ts b/__tests__/unit/util/style.spec.ts new file mode 100644 index 000000000..29140c65c --- /dev/null +++ b/__tests__/unit/util/style.spec.ts @@ -0,0 +1,35 @@ +import { getDefaultStyle, getStateStyle } from '../../../src/util'; + +const defaultStyle = { + fill: 'red', + stroke: 'green', + lineWidth: 10, +}; +const active = { + fill: 'green', + stroke: 'blue', +}; + +const inactive = { + fill: 'gray', +}; + +describe('getStyle', () => { + test('getDefaultStyle', async () => { + expect(getDefaultStyle(defaultStyle)).toStrictEqual(defaultStyle); + expect(getDefaultStyle({ ...defaultStyle, active })).toStrictEqual(defaultStyle); + expect(getDefaultStyle({ ...defaultStyle, active, inactive })).toStrictEqual(defaultStyle); + }); + + test('getStateStyle', async () => { + expect(getStateStyle(defaultStyle)).toStrictEqual(defaultStyle); + expect(getStateStyle({ ...defaultStyle, active })).toStrictEqual(defaultStyle); + expect(getStateStyle({ ...defaultStyle, active }, 'active')).toStrictEqual(active); + expect(getStateStyle({ ...defaultStyle, inactive }, 'inactive')).toStrictEqual(inactive); + expect(getStateStyle({ ...defaultStyle, active }, 'active', true)).toStrictEqual({ ...defaultStyle, ...active }); + expect(getStateStyle({ ...defaultStyle, inactive }, 'inactive', true)).toStrictEqual({ + ...defaultStyle, + ...inactive, + }); + }); +}); diff --git a/src/types.ts b/src/types.ts index 872598193..bf762c438 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1 +1,2 @@ export { ShapeAttrs, ShapeCfg, CustomElement, DisplayObject } from '@antv/g'; +export { MixAttrs } from './util/style'; diff --git a/src/util/index.ts b/src/util/index.ts index 4df96ee2b..7196da897 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -1,3 +1,5 @@ export { svg2marker } from './svg2marker'; export { measureTextWidth, getEllipsisText } from './text'; export { toPrecision } from './utils'; +export { normalPadding } from './padding'; +export { getDefaultStyle, getStateStyle } from './style'; diff --git a/src/util/padding.ts b/src/util/padding.ts new file mode 100644 index 000000000..605de1a50 --- /dev/null +++ b/src/util/padding.ts @@ -0,0 +1,29 @@ +import { isNumber, isArray } from '@antv/util'; + +type NormalPaddingType = [number, number, number, number]; + +/** + * 规范化padding + */ +export function normalPadding(padding: number | number[]): NormalPaddingType { + if (isNumber(padding)) { + return [padding, padding, padding, padding]; + } + if (isArray(padding)) { + const len = (padding as number[]).length; + + if (len === 1) { + return [padding[0], padding[0], padding[0], padding[0]]; + } + if (len === 2) { + return [padding[0], padding[1], padding[0], padding[1]]; + } + if (len === 3) { + return [padding[0], padding[1], padding[2], padding[1]]; + } + if (len === 4) { + return padding as NormalPaddingType; + } + } + return [0, 0, 0, 0]; +} diff --git a/src/util/style.ts b/src/util/style.ts new file mode 100644 index 000000000..f52d5fce1 --- /dev/null +++ b/src/util/style.ts @@ -0,0 +1,44 @@ +import { clone, deepMix, get } from '@antv/util'; +import { ShapeAttrs } from '@antv/g'; + +const STATE_LIST = ['active', 'inactive', 'checked', 'unchecked', 'enable', 'disabled'] as const; +export type StyleState = typeof STATE_LIST[number]; + +export type MixAttrs = ShapeAttrs & + { + [state in StyleState]?: ShapeAttrs; + }; + +/** + * 从带状态样式中返回移除了状态样式的默认样式 + */ +export function getDefaultStyle(style: MixAttrs): ShapeAttrs { + const duplicateStyle = clone(style); + // 移除其他带状态的样式得到默认样式 + STATE_LIST.forEach((state) => { + if (state in duplicateStyle) delete duplicateStyle[state]; + }); + return duplicateStyle; +} + +/** + * 对于格式为: + * style: ShapeAttrs & { + * [state: string]?: ShapeAttrs, + * } + * 的带状态样式,根据状态提取出样式 + * 默认返回默认样式 + * @param style 混合样式 + * @param state 状态 + * @param isMerge 是否将状态样式与默认样式合并 + */ +export function getStateStyle(style: MixAttrs, state?: StyleState, isMerge: boolean = false): ShapeAttrs { + if (!state) { + return getDefaultStyle(style); + } + const stateStyle = get(style, state); + if (isMerge) { + return deepMix({}, getDefaultStyle(style), stateStyle); + } + return stateStyle; +}