From 189d80371c06fc362e504cd733787ed7fe7605fa Mon Sep 17 00:00:00 2001 From: hangboss1761 <1240123692@qq.com> Date: Fri, 18 Mar 2022 18:58:26 +0800 Subject: [PATCH] feat(comp: select): maxLabelCount support responsive (#756) --- .../__snapshots__/overflow.spec.ts.snap | 6 +- .../overflow/__tests__/overflow.spec.ts | 21 ++-- .../components/_private/overflow/index.ts | 10 +- .../components/_private/overflow/src/Item.tsx | 8 +- .../_private/overflow/src/Overflow.tsx | 91 ++++++-------- .../_private/overflow/src/itemTypes.ts | 34 ----- .../components/_private/overflow/src/types.ts | 31 +++-- .../__snapshots__/pagination.spec.ts.snap | 15 ++- .../__snapshots__/select.spec.ts.snap | 118 ++++++++++++++++-- .../select/__tests__/select.spec.ts | 15 ++- .../components/select/demo/CustomLabel.vue | 9 +- packages/components/select/demo/MaxLabel.md | 13 ++ packages/components/select/demo/MaxLabel.vue | 47 +++++++ packages/components/select/docs/Index.zh.md | 4 +- .../components/select/src/trigger/Item.tsx | 8 +- .../select/src/trigger/Selector.tsx | 93 +++++++------- packages/components/select/src/types.ts | 3 +- .../components/select/style/multiple.less | 4 + 18 files changed, 335 insertions(+), 195 deletions(-) delete mode 100644 packages/components/_private/overflow/src/itemTypes.ts create mode 100644 packages/components/select/demo/MaxLabel.md create mode 100644 packages/components/select/demo/MaxLabel.vue diff --git a/packages/components/_private/overflow/__tests__/__snapshots__/overflow.spec.ts.snap b/packages/components/_private/overflow/__tests__/__snapshots__/overflow.spec.ts.snap index f33e093c8..d89cca49e 100644 --- a/packages/components/_private/overflow/__tests__/__snapshots__/overflow.spec.ts.snap +++ b/packages/components/_private/overflow/__tests__/__snapshots__/overflow.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Overflow maxLabelCount responsive work 1`] = ` +exports[`Overflow maxLabel responsive work 1`] = ` "
0
1
@@ -27,7 +27,7 @@ exports[`Overflow maxLabelCount responsive work 1`] = `
" `; -exports[`Overflow maxLabelCount responsive work 2`] = ` +exports[`Overflow maxLabel responsive work 2`] = ` "
0
1
@@ -49,7 +49,7 @@ exports[`Overflow maxLabelCount responsive work 2`] = `
17
18
19
-
+ 19 ...
+
+ 19 ...
" `; diff --git a/packages/components/_private/overflow/__tests__/overflow.spec.ts b/packages/components/_private/overflow/__tests__/overflow.spec.ts index 845181741..81567b66e 100644 --- a/packages/components/_private/overflow/__tests__/overflow.spec.ts +++ b/packages/components/_private/overflow/__tests__/overflow.spec.ts @@ -21,7 +21,7 @@ describe('Overflow', () => { ...(merge( { props: { - itemKey: 'key', + getKey: (item: OverflowData) => item.key, prefixCls: 'ix-test', dataSource: overfolwData, }, @@ -33,39 +33,40 @@ describe('Overflow', () => { } renderWork(Overflow, { - props: { maxLabelCount: 4 }, + props: { maxLabel: 4 }, }) renderWork(Overflow, { - props: { maxLabelCount: 'responsive' }, + props: { maxLabel: 'responsive' }, }) - test('maxLabelCount work', async () => { + test('maxLabel work', async () => { const wrapper = OverflowMount() let items = wrapper.findAll('.ix-overflow-item') expect(items.length).toBe(totalLen) - await wrapper.setProps({ maxLabelCount: 3 }) + await wrapper.setProps({ maxLabel: 3 }) // [0, 1, 2, + 17 ...] items = wrapper.findAll('.ix-overflow-item') expect(items.length).toBe(4) expect(items[3].text()).toBe('+ 17 ...') + expect(items[3].attributes('style')).toEqual(expect.not.stringContaining('display: none')) }) - test('maxLabelCount responsive work', async () => { + test('maxLabel responsive work', async () => { const wrapper = OverflowMount() expect(wrapper.html()).toMatchSnapshot() - await wrapper.setProps({ maxLabelCount: 'responsive' }) + await wrapper.setProps({ maxLabel: 'responsive' }) expect(wrapper.html()).toMatchSnapshot() }) test('item slot work', async () => { - const wrapper = OverflowMount({ props: { maxLabelCount: 2 } }) + const wrapper = OverflowMount({ props: { maxLabel: 2 } }) const items = wrapper.findAll('.ix-overflow-item') @@ -74,7 +75,7 @@ describe('Overflow', () => { }) test('rest slot work', async () => { const wrapper = OverflowMount({ - props: { maxLabelCount: 2 }, + props: { maxLabel: 2 }, slots: { rest: `` }, }) @@ -84,7 +85,7 @@ describe('Overflow', () => { }) test('suffix slot work', async () => { const wrapper = OverflowMount({ - props: { maxLabelCount: 2 }, + props: { maxLabel: 2 }, slots: { suffix: `x` }, }) diff --git a/packages/components/_private/overflow/index.ts b/packages/components/_private/overflow/index.ts index c425842a1..06de7a5bd 100644 --- a/packages/components/_private/overflow/index.ts +++ b/packages/components/_private/overflow/index.ts @@ -9,8 +9,12 @@ import type { OverflowComponent } from './src/types' import Overflow from './src/Overflow' -const IxOverflow = Overflow as unknown as OverflowComponent +const ɵOverflow = Overflow as unknown as OverflowComponent -export { IxOverflow } +export { ɵOverflow } -export type { OverflowInstance, OverflowComponent, OverflowPublicProps as OverflowProps } from './src/types' +export type { + OverflowInstance as ɵOverflowInstance, + OverflowComponent as ɵOverflowComponent, + OverflowPublicProps as ɵOverflowProps, +} from './src/types' diff --git a/packages/components/_private/overflow/src/Item.tsx b/packages/components/_private/overflow/src/Item.tsx index 8c506ce00..0daa5f4ac 100644 --- a/packages/components/_private/overflow/src/Item.tsx +++ b/packages/components/_private/overflow/src/Item.tsx @@ -7,18 +7,16 @@ import { defineComponent, onBeforeUnmount, onMounted, ref } from 'vue' -import { offResize, onResize } from '@idux/cdk/utils' +import { callEmit, offResize, onResize } from '@idux/cdk/utils' -import { overflowItemProps } from './itemTypes' +import { overflowItemProps } from './types' export default defineComponent({ name: 'IxOverflowItem', props: overflowItemProps, setup(props, { slots }) { const itemElRef = ref() - const handleResize = (entry: ResizeObserverEntry) => { - props.onSizeChange?.(entry.target, props.itemKey ?? '') - } + const handleResize = (entry: ResizeObserverEntry) => callEmit(props.onSizeChange, entry.target, props.itemKey ?? '') onMounted(() => onResize(itemElRef.value, handleResize)) onBeforeUnmount(() => { diff --git a/packages/components/_private/overflow/src/Overflow.tsx b/packages/components/_private/overflow/src/Overflow.tsx index 720ef0128..036d07706 100644 --- a/packages/components/_private/overflow/src/Overflow.tsx +++ b/packages/components/_private/overflow/src/Overflow.tsx @@ -5,12 +5,11 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import type { OverflowProps } from './types' import type { VKey } from '@idux/cdk/utils' -import { ComputedRef, Ref, computed, defineComponent, onBeforeUnmount, onMounted, ref, watch } from 'vue' +import { Ref, computed, defineComponent, onBeforeUnmount, onMounted, ref, watch } from 'vue' -import { isNumber, isString } from 'lodash-es' +import { isNumber } from 'lodash-es' import { offResize, onResize, throwError } from '@idux/cdk/utils' import { useGlobalConfig } from '@idux/components/config' @@ -25,50 +24,6 @@ const restNodeKey = '__IDUX_OVERFLOW_REST' const suffixNodeKey = '__IDUX_OVERFLOW_SUFFIX' as VKey const responsive = 'responsive' -const useContainerSize = (containerElRef: Ref) => { - const containerWidth = ref(0) - const setContainerWidth = () => { - containerWidth.value = containerElRef.value?.clientWidth ?? 0 - } - - return { - containerWidth, - setContainerWidth, - } -} - -const useItemSize = () => { - const itemsWidthMap = ref>(new Map()) - const setItemWidth = (key: VKey, itemEl?: Element) => { - if (!itemEl && itemsWidthMap.value.get(key)) { - itemsWidthMap.value.delete(key) - } else { - itemEl?.clientWidth && itemsWidthMap.value.set(key, itemEl?.clientWidth ?? 0) - } - } - - return { - itemsWidthMap, - setItemWidth, - } -} - -export type GetKey = (item: unknown) => VKey - -const useGetKeys = (props: OverflowProps): ComputedRef => { - return computed(() => { - const itemKey = props.itemKey - if (isString(itemKey)) { - return (item: unknown) => { - const key = (item as SafeAny)[itemKey] - - return key - } - } - return itemKey - }) -} - export default defineComponent({ name: 'IxOverflow', props: overflowProps, @@ -82,17 +37,16 @@ export default defineComponent({ const restWidth = ref(0) const suffixWidth = ref(0) - const getKey = useGetKeys(props) const displayCount = ref(props.dataSource.length) - const isResposive = computed(() => props.maxLabelCount === responsive) + const isResposive = computed(() => props.maxLabel === responsive) const restReady = ref(false) const showRest = computed( - () => isResposive.value || (isNumber(props.maxLabelCount) && props.dataSource.length > props.maxLabelCount), + () => isResposive.value || (isNumber(props.maxLabel) && props.dataSource.length > props.maxLabel), ) const mergedData = computed(() => { if (!isResposive.value) { - return props.dataSource.slice(0, props.maxLabelCount as number) + return props.dataSource.slice(0, props.maxLabel as number) } return props.dataSource }) @@ -113,12 +67,13 @@ export default defineComponent({ } if (!isResposive.value) { - displayCount.value = Math.min(props.maxLabelCount as number, len) + displayCount.value = Math.min(props.maxLabel as number, len) + restReady.value = true return } for (let i = 0; i < len; i++) { - const getItemWidth = (index: number) => itemsWidthMap.value.get(getKey.value(data[index])) ?? 0 + const getItemWidth = (index: number) => itemsWidthMap.value.get(props.getKey(data[index])) ?? 0 const internalContainerWidth = containerWidth.value - suffixWidth.value const curItemWidth = getItemWidth(i) @@ -168,7 +123,7 @@ export default defineComponent({ return ( setItemWidth(key!, itemEl)} > @@ -216,3 +171,31 @@ export default defineComponent({ } }, }) + +const useContainerSize = (containerElRef: Ref) => { + const containerWidth = ref(0) + const setContainerWidth = () => { + containerWidth.value = containerElRef.value?.clientWidth ?? 0 + } + + return { + containerWidth, + setContainerWidth, + } +} + +const useItemSize = () => { + const itemsWidthMap = ref>(new Map()) + const setItemWidth = (key: VKey, itemEl?: Element) => { + if (!itemEl && itemsWidthMap.value.get(key)) { + itemsWidthMap.value.delete(key) + } else { + itemEl?.clientWidth && itemsWidthMap.value.set(key, itemEl?.clientWidth ?? 0) + } + } + + return { + itemsWidthMap, + setItemWidth, + } +} diff --git a/packages/components/_private/overflow/src/itemTypes.ts b/packages/components/_private/overflow/src/itemTypes.ts deleted file mode 100644 index 4af421d95..000000000 --- a/packages/components/_private/overflow/src/itemTypes.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @license - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE - */ - -import type { IxInnerPropTypes, IxPublicPropTypes, VKey } from '@idux/cdk/utils' -import type { DefineComponent, HTMLAttributes } from 'vue' - -import { IxPropTypes, vKeyPropDef } from '@idux/cdk/utils' - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type SafeAny = any - -export interface ItemData { - key: VKey - [propName: string]: SafeAny -} - -export const overflowItemProps = { - prefixCls: IxPropTypes.string.isRequired, - display: IxPropTypes.bool.def(true), - itemKey: vKeyPropDef.isRequired, - data: IxPropTypes.object(), - onSizeChange: IxPropTypes.func<(itemEl: Element, key?: VKey) => void>(), -} - -export type overflowItemProps = IxInnerPropTypes -export type OverflowItemPublicProps = IxPublicPropTypes -export type OverflowItemComponent = DefineComponent< - Omit & OverflowItemPublicProps -> -export type OverflowItemInstance = InstanceType> diff --git a/packages/components/_private/overflow/src/types.ts b/packages/components/_private/overflow/src/types.ts index b99655350..3fce42352 100644 --- a/packages/components/_private/overflow/src/types.ts +++ b/packages/components/_private/overflow/src/types.ts @@ -5,21 +5,36 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import type { IxInnerPropTypes, IxPublicPropTypes } from '@idux/cdk/utils' +import type { ExtractInnerPropTypes, ExtractPublicPropTypes, VKey } from '@idux/cdk/utils' import type { DefineComponent, HTMLAttributes } from 'vue' -import { IxPropTypes } from '@idux/cdk/utils' +import { IxPropTypes, vKeyPropDef } from '@idux/cdk/utils' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type SafeAny = any + +export interface ItemData { + key: VKey + [propName: string]: SafeAny +} + +export const overflowItemProps = { + prefixCls: IxPropTypes.string.isRequired, + display: IxPropTypes.bool.def(true), + itemKey: vKeyPropDef.isRequired, + data: IxPropTypes.object(), + onSizeChange: IxPropTypes.func<(itemEl: Element, key?: VKey) => void>(), +} export const overflowProps = { - maxLabelCount: IxPropTypes.oneOfType([IxPropTypes.number, IxPropTypes.oneOf(['responsive'])]).def( - Number.MAX_SAFE_INTEGER, - ), - itemKey: IxPropTypes.string.isRequired, + maxLabel: IxPropTypes.oneOfType([IxPropTypes.number, IxPropTypes.oneOf(['responsive'])]).def(Number.MAX_SAFE_INTEGER), + getKey: IxPropTypes.func<(item: SafeAny) => VKey>().isRequired, prefixCls: IxPropTypes.string.isRequired, dataSource: IxPropTypes.array().def(() => []), } -export type OverflowProps = IxInnerPropTypes -export type OverflowPublicProps = IxPublicPropTypes +export type OverflowProps = ExtractInnerPropTypes +export type OverflowItemProps = ExtractInnerPropTypes +export type OverflowPublicProps = ExtractPublicPropTypes export type OverflowComponent = DefineComponent & OverflowPublicProps> export type OverflowInstance = InstanceType> diff --git a/packages/components/pagination/__tests__/__snapshots__/pagination.spec.ts.snap b/packages/components/pagination/__tests__/__snapshots__/pagination.spec.ts.snap index 42ba165e9..435d44488 100644 --- a/packages/components/pagination/__tests__/__snapshots__/pagination.spec.ts.snap +++ b/packages/components/pagination/__tests__/__snapshots__/pagination.spec.ts.snap @@ -38,11 +38,18 @@ exports[`Pagination render work 3`] = `
-
10 条/页 - -
-
+
+
+
10 条/页 + +
+
+
+
+ +
+
diff --git a/packages/components/select/__tests__/__snapshots__/select.spec.ts.snap b/packages/components/select/__tests__/__snapshots__/select.spec.ts.snap index e72f084ac..5e68127fd 100644 --- a/packages/components/select/__tests__/__snapshots__/select.spec.ts.snap +++ b/packages/components/select/__tests__/__snapshots__/select.spec.ts.snap @@ -1,13 +1,93 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Select multiple work maxLabel responsive work 1`] = ` +"
+ +
+
+
+
A0 + +
+
+
+
A1
+
+
+
A2
+
+
+
A4
+
+
+
A5
+
+
+
+ 4 ... + +
+
+
+
+
+
+ +
+ +
+
+" +`; + +exports[`Select multiple work maxLabel responsive work 2`] = ` +"
+ +
+
+
+
A0 + +
+
+
+
A1
+
+
+
+ 3 ... + +
+
+
+
+
+
+ +
+ +
+
+" +`; + exports[`Select multiple work render work 1`] = ` "
-
0
-
1
-
2
-
+
+
+
0
+
+
+
1
+
+
+
2
+
+ +
+
+
+
@@ -20,11 +100,18 @@ exports[`Select single work render work 1`] = ` "
-
Tom - -
-
+
+
+
Tom + +
+
+
+
+ +
+
@@ -38,11 +125,18 @@ exports[`Select template work render work 1`] = ` "
-
Tom - -
-
+
+
+
Tom + +
+
+
+
+ +
+
diff --git a/packages/components/select/__tests__/select.spec.ts b/packages/components/select/__tests__/select.spec.ts index cd980ae40..2e905ca61 100644 --- a/packages/components/select/__tests__/select.spec.ts +++ b/packages/components/select/__tests__/select.spec.ts @@ -503,8 +503,8 @@ describe('Select', () => { expect(options[4].attributes('title')).toBe('') }) - test('maxLabelCount work', async () => { - const wrapper = SelectMount({ props: { maxLabelCount: 3, value: [0, 1, 2, 4, 5] } }) + test('maxLabel work', async () => { + const wrapper = SelectMount({ props: { maxLabel: 3, value: [0, 1, 2, 4, 5] } }) let items = wrapper.findAll('.ix-select-selector-item') @@ -513,7 +513,7 @@ describe('Select', () => { expect(items[2].text()).toBe('A2') expect(items[3].text()).toBe('+ 2 ...') - await wrapper.setProps({ maxLabelCount: 2 }) + await wrapper.setProps({ maxLabel: 2 }) items = wrapper.findAll('.ix-select-selector-item') @@ -521,6 +521,15 @@ describe('Select', () => { expect(items[1].text()).toBe('A1') expect(items[2].text()).toBe('+ 3 ...') }) + + test('maxLabel responsive work', async () => { + const wrapper = SelectMount({ props: { maxLabel: 'responsive', value: [0, 1, 2, 4, 5] } }) + + expect(wrapper.html()).toMatchSnapshot() + + await wrapper.setProps({ maxLabel: 2 }) + expect(wrapper.html()).toMatchSnapshot() + }) }) describe('template work', () => { diff --git a/packages/components/select/demo/CustomLabel.vue b/packages/components/select/demo/CustomLabel.vue index 10c41ec8e..be5bac998 100644 --- a/packages/components/select/demo/CustomLabel.vue +++ b/packages/components/select/demo/CustomLabel.vue @@ -1,12 +1,5 @@