From f0649999fb0b860ba64876f2203ee361881ee56f Mon Sep 17 00:00:00 2001 From: bqy_fe <1743369777@qq.com> Date: Sun, 12 Feb 2023 09:26:39 +0800 Subject: [PATCH] refactor(Space): less to cssinjs & add compact mode (#6229) * refactor(Space): less to cssinjs & add compact mode * chore(space): update md * chore(space): add demo * chore(space): add some demo * feat(button): add compact mode * fix: reactivity lose * docs: fix props version --------- Co-authored-by: tangjinzhou <415800467@qq.com> --- components/button/button.tsx | 39 +-- components/space/Compact.tsx | 135 +++++++++++ .../space/demo/compact-button-vertical.vue | 43 ++++ components/space/demo/compact-buttons.vue | 151 ++++++++++++ components/space/demo/compact.vue | 227 ++++++++++++++++++ components/space/demo/index.vue | 15 ++ components/space/demo/split.vue | 33 +++ components/space/demo/wrap.vue | 30 +++ components/space/index.en-US.md | 26 +- components/space/index.tsx | 36 ++- components/space/index.zh-CN.md | 36 ++- components/space/style/compact.tsx | 29 +++ components/space/style/index.less | 39 --- components/space/style/index.tsx | 56 ++++- components/space/style/rtl.less | 10 - components/style.ts | 2 +- components/theme/interface/components.ts | 4 +- 17 files changed, 827 insertions(+), 84 deletions(-) create mode 100644 components/space/Compact.tsx create mode 100644 components/space/demo/compact-button-vertical.vue create mode 100644 components/space/demo/compact-buttons.vue create mode 100644 components/space/demo/compact.vue create mode 100644 components/space/demo/split.vue create mode 100644 components/space/demo/wrap.vue create mode 100644 components/space/style/compact.tsx delete mode 100644 components/space/style/index.less delete mode 100644 components/space/style/rtl.less diff --git a/components/button/button.tsx b/components/button/button.tsx index f7a25a94d..311249ab2 100644 --- a/components/button/button.tsx +++ b/components/button/button.tsx @@ -19,6 +19,7 @@ import useStyle from './style'; import type { ButtonType } from './buttonTypes'; import type { VNode, Ref } from 'vue'; import { GroupSizeContext } from './button-group'; +import { useCompactItemContext } from '../space/Compact'; type Loading = boolean | number; @@ -49,6 +50,7 @@ export default defineComponent({ const hasTwoCNChar = ref(false); const autoInsertSpace = computed(() => autoInsertSpaceInButton.value !== false); + const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction); // =============== Update Loading =============== const loadingOrDelay = computed(() => @@ -79,22 +81,25 @@ export default defineComponent({ const pre = prefixCls.value; const sizeClassNameMap = { large: 'lg', small: 'sm', middle: undefined }; - const sizeFullname = groupSize?.value || size.value; + const sizeFullname = compactSize.value || groupSize?.value || size.value; const sizeCls = sizeFullname ? sizeClassNameMap[sizeFullname] || '' : ''; - return { - [hashId.value]: true, - [`${pre}`]: true, - [`${pre}-${shape}`]: shape !== 'default' && shape, - [`${pre}-${type}`]: type, - [`${pre}-${sizeCls}`]: sizeCls, - [`${pre}-loading`]: innerLoading.value, - [`${pre}-background-ghost`]: ghost && !isUnBorderedButtonType(type), - [`${pre}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value, - [`${pre}-block`]: block, - [`${pre}-dangerous`]: !!danger, - [`${pre}-rtl`]: direction.value === 'rtl', - }; + return [ + compactItemClassnames.value, + { + [hashId.value]: true, + [`${pre}`]: true, + [`${pre}-${shape}`]: shape !== 'default' && shape, + [`${pre}-${type}`]: type, + [`${pre}-${sizeCls}`]: sizeCls, + [`${pre}-loading`]: innerLoading.value, + [`${pre}-background-ghost`]: ghost && !isUnBorderedButtonType(type), + [`${pre}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value, + [`${pre}-block`]: block, + [`${pre}-dangerous`]: !!danger, + [`${pre}-rtl`]: direction.value === 'rtl', + }, + ]; }); const fixTwoCNChar = () => { @@ -209,7 +214,11 @@ export default defineComponent({ ); if (!isUnBorderedButtonType(type)) { - buttonNode = {buttonNode}; + buttonNode = ( + + {buttonNode} + + ); } return wrapSSR(buttonNode); diff --git a/components/space/Compact.tsx b/components/space/Compact.tsx new file mode 100644 index 000000000..2b07a7c36 --- /dev/null +++ b/components/space/Compact.tsx @@ -0,0 +1,135 @@ +import classNames from '../_util/classNames'; +import type { DirectionType, SizeType } from '../config-provider'; +import createContext from '../_util/createContext'; +import useConfigInject from '../config-provider/hooks/useConfigInject'; + +import useStyle from './style'; +import { computed, defineComponent } from 'vue'; +import type { PropType, ExtractPropTypes, Ref } from 'vue'; +import PropTypes from '../_util/vue-types'; +import { booleanType, tuple } from '../_util/type'; +import { isEmpty } from 'lodash-es'; + +export const spaceCompactItemProps = () => ({ + compactSize: String as PropType, + compactDirection: PropTypes.oneOf(tuple('horizontal', 'vertical')).def('horizontal'), + isFirstItem: booleanType(), + isLastItem: booleanType(), +}); + +export type SpaceCompactItemContextType = Partial< + ExtractPropTypes> +>; + +export const SpaceCompactItemContext = createContext(null); + +export const useCompactItemContext = (prefixCls: Ref, direction: Ref) => { + const compactItemContext = SpaceCompactItemContext.useInject(); + + const compactItemClassnames = computed(() => { + if (!compactItemContext || isEmpty(compactItemContext)) return ''; + + const { compactDirection, isFirstItem, isLastItem } = compactItemContext; + const separator = compactDirection === 'vertical' ? '-vertical-' : '-'; + + return classNames({ + [`${prefixCls.value}-compact${separator}item`]: true, + [`${prefixCls.value}-compact${separator}first-item`]: isFirstItem, + [`${prefixCls.value}-compact${separator}last-item`]: isLastItem, + [`${prefixCls.value}-compact${separator}item-rtl`]: direction.value === 'rtl', + }); + }); + + return { + compactSize: computed(() => compactItemContext?.compactSize), + compactDirection: computed(() => compactItemContext?.compactDirection), + compactItemClassnames, + }; +}; + +export const NoCompactStyle = defineComponent({ + name: 'NoCompactStyle', + setup(_, { slots }) { + SpaceCompactItemContext.useProvide(null); + return () => { + return slots.default?.(); + }; + }, +}); + +export const spaceCompactProps = () => ({ + prefixCls: String, + size: { + type: [String, Number, Array] as PropType, + }, + direction: PropTypes.oneOf(tuple('horizontal', 'vertical')).def('horizontal'), + align: PropTypes.oneOf(tuple('start', 'end', 'center', 'baseline')), + block: { type: Boolean, default: undefined }, +}); + +export type SpaceCompactProps = Partial>>; + +const CompactItem = defineComponent({ + name: 'CompactItem', + props: spaceCompactItemProps(), + setup(props, { slots }) { + SpaceCompactItemContext.useProvide(props); + + return () => slots.default?.(); + }, +}); + +const Compact = defineComponent({ + name: 'ASpaceCompact', + inheritAttrs: false, + props: spaceCompactProps(), + setup(props, { attrs, slots }) { + const { prefixCls, direction: directionConfig } = useConfigInject('space-compact', props); + const compactItemContext = SpaceCompactItemContext.useInject(); + + const [wrapSSR, hashId] = useStyle(prefixCls); + + const clx = computed(() => { + return classNames(prefixCls.value, hashId.value, { + [`${prefixCls.value}-rtl`]: directionConfig.value === 'rtl', + [`${prefixCls.value}-block`]: props.block, + [`${prefixCls.value}-vertical`]: props.direction === 'vertical', + }); + }); + + return () => { + // =========================== Render =========================== + if (slots.default?.()?.length === 0) { + return null; + } + + const childNodes = slots.default?.() || []; + + return wrapSSR( +
+ {childNodes.map((child, i) => { + const key = (child && child.key) || `${prefixCls.value}-item-${i}`; + const noCompactItemContext = !compactItemContext || isEmpty(compactItemContext); + + return ( + + {child} + + ); + })} +
, + ); + }; + }, +}); + +export default Compact; diff --git a/components/space/demo/compact-button-vertical.vue b/components/space/demo/compact-button-vertical.vue new file mode 100644 index 000000000..9d7530e46 --- /dev/null +++ b/components/space/demo/compact-button-vertical.vue @@ -0,0 +1,43 @@ + +--- +order: 8 +title: + zh-CN: 垂直方向紧凑布局 + en-US: Vertical Compact Mode +--- + +## zh-CN + +垂直方向的紧凑布局,目前仅支持 Button 组合。 + +## en-US + +Vertical Mode for Space.Compact, support Button only. + + + + + + diff --git a/components/space/demo/compact-buttons.vue b/components/space/demo/compact-buttons.vue new file mode 100644 index 000000000..3e926e5a3 --- /dev/null +++ b/components/space/demo/compact-buttons.vue @@ -0,0 +1,151 @@ + +--- +order: 7 +title: + zh-CN: Button 紧凑布局 + en-US: Button Compact Mode +--- + +## zh-CN + +Button 组件紧凑排列的示例。 + +## en-US + +Button component compact example. + + + + + + diff --git a/components/space/demo/compact.vue b/components/space/demo/compact.vue new file mode 100644 index 000000000..17b7a8526 --- /dev/null +++ b/components/space/demo/compact.vue @@ -0,0 +1,227 @@ + +--- +order: 6 +title: + zh-CN: 紧凑布局组合 + en-US: Compact Mode for form component +--- + +## zh-CN + +使用 Space.Compact 让表单组件之间紧凑连接且合并边框。 + +## en-US + +Compact Mode for form component. + + + + + + diff --git a/components/space/demo/index.vue b/components/space/demo/index.vue index 40b7ef474..675a37928 100644 --- a/components/space/demo/index.vue +++ b/components/space/demo/index.vue @@ -4,6 +4,11 @@ + + + + + diff --git a/components/space/demo/wrap.vue b/components/space/demo/wrap.vue new file mode 100644 index 000000000..52653d26d --- /dev/null +++ b/components/space/demo/wrap.vue @@ -0,0 +1,30 @@ + +--- +order: 0 +title: + zh-CN: 自动换行 + en-US: Basic Usage +--- + +## zh-CN + +自动换行。 + +## en-US + +Wrap + + + + + diff --git a/components/space/index.en-US.md b/components/space/index.en-US.md index b63da5c81..92d8eaf03 100644 --- a/components/space/index.en-US.md +++ b/components/space/index.en-US.md @@ -10,12 +10,36 @@ Set components spacing. ## When To Use -Avoid components clinging together and set a unified space. +- Avoid components clinging together and set a unified space. +- Use Space.Compact when child form components are compactly connected and the border is collapsed (After version `ant-design-vue@4.0.0` Supported). ## API +### Space + | Property | Description | Type | Default | Version | | --- | --- | --- | --- | --- | | align | Align items | `start` \| `end` \|`center` \|`baseline` | - | 1.6.5 | | direction | The space direction | `vertical` \| `horizontal` | `horizontal` | 1.6.5 | | size | The space size | `small` \| `middle` \| `large` \| `number` | `small` | 1.6.5 | +| split | Set split | VueNode \| v-slot | - | 2.2.0 | +| wrap | Auto wrap line, when `horizontal` effective | boolean | false | 2.2.0 | + +### Space.Compact + +Use Space.Compact when child form components are compactly connected and the border is collapsed. The supported components are: + +- Button +- AutoComplete +- Cascader +- DatePicker +- Input/Input.Search +- Select +- TimePicker +- TreeSelect + +| Property | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| block | Option to fit width to its parent\'s width | boolean | false | 4.0.0 | +| direction | Set direction of layout | `vertical` \| `horizontal` | `horizontal` | 4.0.0 | +| size | Set child component size | `large` \| `middle` \| `small` | `middle` | 4.0.0 | diff --git a/components/space/index.tsx b/components/space/index.tsx index 2cd384dd5..b480a333e 100644 --- a/components/space/index.tsx +++ b/components/space/index.tsx @@ -1,12 +1,15 @@ -import type { PropType, ExtractPropTypes, CSSProperties } from 'vue'; +import type { PropType, ExtractPropTypes, CSSProperties, Plugin, App } from 'vue'; import { defineComponent, computed, ref, watch } from 'vue'; import PropTypes from '../_util/vue-types'; import { filterEmpty } from '../_util/props-util'; import type { SizeType } from '../config-provider'; -import { tuple, withInstall } from '../_util/type'; +import { booleanType, tuple } from '../_util/type'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import useFlexGapSupport from '../_util/hooks/useFlexGapSupport'; import classNames from '../_util/classNames'; +import Compact from './Compact'; + +import useStyle from './style'; export type SpaceSize = SizeType | number; const spaceSize = { @@ -21,7 +24,7 @@ export const spaceProps = () => ({ }, direction: PropTypes.oneOf(tuple('horizontal', 'vertical')).def('horizontal'), align: PropTypes.oneOf(tuple('start', 'end', 'center', 'baseline')), - wrap: { type: Boolean, default: undefined }, + wrap: booleanType(), }); export type SpaceProps = Partial>>; @@ -33,10 +36,12 @@ function getNumberSize(size: SpaceSize) { const Space = defineComponent({ compatConfig: { MODE: 3 }, name: 'ASpace', + inheritAttrs: false, props: spaceProps(), slots: ['split'], - setup(props, { slots }) { + setup(props, { slots, attrs }) { const { prefixCls, space, direction: directionConfig } = useConfigInject('space', props); + const [wrapSSR, hashId] = useStyle(prefixCls); const supportFlexGap = useFlexGapSupport(); const size = computed(() => props.size ?? space?.value?.size ?? 'small'); const horizontalSize = ref(); @@ -58,7 +63,7 @@ const Space = defineComponent({ props.align === undefined && props.direction === 'horizontal' ? 'center' : props.align, ); const cn = computed(() => { - return classNames(prefixCls.value, `${prefixCls.value}-${props.direction}`, { + return classNames(prefixCls.value, hashId.value, `${prefixCls.value}-${props.direction}`, { [`${prefixCls.value}-rtl`]: directionConfig.value === 'rtl', [`${prefixCls.value}-align-${mergedAlign.value}`]: mergedAlign.value, }); @@ -92,7 +97,7 @@ const Space = defineComponent({ const horizontalSizeVal = horizontalSize.value; const latestIndex = len - 1; return ( -
+
{items.map((child, index) => { let itemStyle: CSSProperties = {}; if (!supportFlexGap.value) { @@ -110,7 +115,7 @@ const Space = defineComponent({ } } - return ( + return wrapSSR( <>
{child} @@ -120,7 +125,7 @@ const Space = defineComponent({ {split} )} - + , ); })}
@@ -129,4 +134,17 @@ const Space = defineComponent({ }, }); -export default withInstall(Space); +Space.Compact = Compact; + +Space.install = function (app: App) { + app.component(Space.name, Space); + app.component(Compact.name, Compact); + return app; +}; + +export { Compact }; + +export default Space as typeof Space & + Plugin & { + readonly Compact: typeof Compact; + }; diff --git a/components/space/index.zh-CN.md b/components/space/index.zh-CN.md index 05359aed7..59a965372 100644 --- a/components/space/index.zh-CN.md +++ b/components/space/index.zh-CN.md @@ -15,11 +15,37 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/wc6%263gJ0Y8/Space.svg - 适合行内元素的水平间距。 - 可以设置各种水平对齐方式。 +- 需要表单组件之间紧凑连接且合并边框时,使用 Space.Compact(自 `ant-design-vue@4.0.0` 版本开始提供该组件)。 ## API -| 参数 | 说明 | 类型 | 默认值 | 版本 | -| --------- | -------- | ------------------------------------------ | ------------ | ----- | -| align | 对齐方式 | `start` \| `end` \|`center` \|`baseline` | - | 1.6.5 | -| direction | 间距方向 | `vertical` \| `horizontal` | `horizontal` | 1.6.5 | -| size | 间距大小 | `small` \| `middle` \| `large` \| `number` | `small` | 1.6.5 | +### Space + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| align | 对齐方式 | `start` \| `end` \|`center` \|`baseline` | - | 1.6.5 | +| direction | 间距方向 | `vertical` \| `horizontal` | `horizontal` | 1.6.5 | +| size | 间距大小 | `small` \| `middle` \| `large` \| `number` | `small` | 1.6.5 | +| split | 设置拆分 | VueNode \| v-slot | - | 2.2.0 | +| wrap | 是否自动换行,仅在 `horizontal` 时有效 | boolean | false | 2.2.0 | + +### Space.Compact + +> 自 ant-design-vue@4.0.0 版本开始提供该组件。 + +需要表单组件之间紧凑连接且合并边框时,使用 Space.Compact。支持的组件有: + +- Button +- AutoComplete +- Cascader +- DatePicker +- Input/Input.Search +- Select +- TimePicker +- TreeSelect + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --------- | ---------------------------- | ------------------------------ | ------------ | ----- | +| block | 将宽度调整为父元素宽度的选项 | boolean | false | 4.0.0 | +| direction | 指定排列方向 | `vertical` \| `horizontal` | `horizontal` | 4.0.0 | +| size | 子组件大小 | `large` \| `middle` \| `small` | `middle` | 4.0.0 | diff --git a/components/space/style/compact.tsx b/components/space/style/compact.tsx new file mode 100644 index 000000000..bffe711d4 --- /dev/null +++ b/components/space/style/compact.tsx @@ -0,0 +1,29 @@ +import type { FullToken, GenerateStyle } from '../../theme/internal'; +/** Component only token. Which will handle additional calculation of alias token */ +export interface ComponentToken { + // Component token here +} + +interface SpaceToken extends FullToken<'Space'> { + // Custom token here +} + +const genSpaceCompactStyle: GenerateStyle = token => { + const { componentCls } = token; + + return { + [componentCls]: { + display: 'inline-flex', + '&-block': { + display: 'flex', + width: '100%', + }, + '&-vertical': { + flexDirection: 'column', + }, + }, + }; +}; + +// ============================== Export ============================== +export default genSpaceCompactStyle; diff --git a/components/space/style/index.less b/components/space/style/index.less deleted file mode 100644 index 842554ca2..000000000 --- a/components/space/style/index.less +++ /dev/null @@ -1,39 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@space-prefix-cls: ~'@{ant-prefix}-space'; -@space-item-prefix-cls: ~'@{ant-prefix}-space-item'; - -.@{space-prefix-cls} { - display: inline-flex; - - &-vertical { - flex-direction: column; - } - - &-align { - &-center { - align-items: center; - } - - &-start { - align-items: flex-start; - } - - &-end { - align-items: flex-end; - } - - &-baseline { - align-items: baseline; - } - } -} - -.@{space-item-prefix-cls} { - &:empty { - display: none; - } -} - -@import './rtl'; diff --git a/components/space/style/index.tsx b/components/space/style/index.tsx index 3a3ab0de5..b10d56ce3 100644 --- a/components/space/style/index.tsx +++ b/components/space/style/index.tsx @@ -1,2 +1,54 @@ -import '../../style/index.less'; -import './index.less'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook } from '../../theme/internal'; +import genSpaceCompactStyle from './compact'; + +/** Component only token. Which will handle additional calculation of alias token */ +export interface ComponentToken { + // Component token here +} + +interface SpaceToken extends FullToken<'Space'> { + // Custom token here +} + +const genSpaceStyle: GenerateStyle = token => { + const { componentCls } = token; + + return { + [componentCls]: { + display: 'inline-flex', + '&-rtl': { + direction: 'rtl', + }, + '&-vertical': { + flexDirection: 'column', + }, + '&-align': { + flexDirection: 'column', + '&-center': { + alignItems: 'center', + }, + '&-start': { + alignItems: 'flex-start', + }, + '&-end': { + alignItems: 'flex-end', + }, + '&-baseline': { + alignItems: 'baseline', + }, + }, + [`${componentCls}-space-item`]: { + '&:empty': { + display: 'none', + }, + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Space', token => [ + genSpaceStyle(token), + genSpaceCompactStyle(token), +]); diff --git a/components/space/style/rtl.less b/components/space/style/rtl.less deleted file mode 100644 index 75aa411b7..000000000 --- a/components/space/style/rtl.less +++ /dev/null @@ -1,10 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@space-prefix-cls: ~'@{ant-prefix}-space'; - -.@{space-prefix-cls} { - &-rtl { - direction: rtl; - } -} diff --git a/components/style.ts b/components/style.ts index 23700052a..73a4299e6 100644 --- a/components/style.ts +++ b/components/style.ts @@ -58,7 +58,7 @@ import './result/style'; // import './descriptions/style'; // import './page-header/style'; import './form/style'; -import './space/style'; +// import './space/style'; import './image/style'; import './typography/style'; // import './color-picker/style'; diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index cfd7a4500..842013e7f 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -34,7 +34,7 @@ import type { ComponentToken as ProgressComponentToken } from '../../progress/st // import type { ComponentToken as SelectComponentToken } from '../../select/style'; import type { ComponentToken as SkeletonComponentToken } from '../../skeleton/style'; // import type { ComponentToken as SliderComponentToken } from '../../slider/style'; -// import type { ComponentToken as SpaceComponentToken } from '../../space/style'; +import type { ComponentToken as SpaceComponentToken } from '../../space/style'; import type { ComponentToken as SpinComponentToken } from '../../spin/style'; // import type { ComponentToken as StepsComponentToken } from '../../steps/style'; // import type { ComponentToken as TableComponentToken } from '../../table/style'; @@ -111,7 +111,7 @@ export interface ComponentTokenMap { Tooltip?: TooltipComponentToken; // Table?: TableComponentToken; // Space?: SpaceComponentToken; - Progress?: ProgressComponentToken; + // Progress?: ProgressComponentToken; // Tour?: TourComponentToken; // QRCode?: QRCodeComponentToken; // App?: AppComponentToken;