import type { PropType, ExtractPropTypes, CSSProperties } 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 useConfigInject from '../_util/hooks/useConfigInject'; import useFlexGapSupport from '../_util/hooks/useFlexGapSupport'; import classNames from '../_util/classNames'; export type SpaceSize = SizeType | number; const spaceSize = { small: 8, middle: 16, large: 24, }; export const spaceProps = () => ({ 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')), wrap: { type: Boolean, default: undefined }, }); export type SpaceProps = Partial>>; function getNumberSize(size: SpaceSize) { return typeof size === 'string' ? spaceSize[size] : size || 0; } const Space = defineComponent({ name: 'ASpace', props: spaceProps(), slots: ['split'], setup(props, { slots }) { const { prefixCls, space, direction: directionConfig } = useConfigInject('space', props); const supportFlexGap = useFlexGapSupport(); const size = computed(() => props.size ?? space.value?.size ?? 'small'); const horizontalSize = ref(); const verticalSize = ref(); watch( size, () => { [horizontalSize.value, verticalSize.value] = ( (Array.isArray(size.value) ? size.value : [size.value, size.value]) as [ SpaceSize, SpaceSize, ] ).map(item => getNumberSize(item)); }, { immediate: true }, ); const mergedAlign = computed(() => props.align === undefined && props.direction === 'horizontal' ? 'center' : props.align, ); const cn = computed(() => { return classNames(prefixCls.value, `${prefixCls.value}-${props.direction}`, { [`${prefixCls.value}-rtl`]: directionConfig.value === 'rtl', [`${prefixCls.value}-align-${mergedAlign.value}`]: mergedAlign.value, }); }); const marginDirection = computed(() => directionConfig.value === 'rtl' ? 'marginLeft' : 'marginRight', ); const style = computed(() => { const gapStyle: CSSProperties = {}; if (supportFlexGap.value) { gapStyle.columnGap = `${horizontalSize.value}px`; gapStyle.rowGap = `${verticalSize.value}px`; } return { ...gapStyle, ...(props.wrap && { flexWrap: 'wrap', marginBottom: `${-verticalSize.value}px` }), } as CSSProperties; }); return () => { const { wrap, direction = 'horizontal' } = props; const items = filterEmpty(slots.default?.()); const len = items.length; if (len === 0) { return null; } const split = slots.split?.(); const itemClassName = `${prefixCls.value}-item`; const horizontalSizeVal = horizontalSize.value; const latestIndex = len - 1; return (
{items.map((child, index) => { let itemStyle: CSSProperties = {}; if (!supportFlexGap.value) { if (direction === 'vertical') { if (index < latestIndex) { itemStyle = { marginBottom: `${horizontalSizeVal / (split ? 2 : 1)}px` }; } } else { itemStyle = { ...(index < latestIndex && { [marginDirection.value]: `${horizontalSizeVal / (split ? 2 : 1)}px`, }), ...(wrap && { paddingBottom: `${verticalSize.value}px` }), }; } } return ( <>
{child}
{index < latestIndex && split && ( {split} )} ); })}
); }; }, }); export default withInstall(Space);