mirror of
				https://github.com/antd-tiny-vue/antd-tiny-vue.git
				synced 2025-10-31 16:51:45 +08:00 
			
		
		
		
	feat: add space
This commit is contained in:
		| @@ -1,41 +0,0 @@ | ||||
| import { classNames, createInjectionState } from '@v-c/utils' | ||||
| import type { Ref } from 'vue' | ||||
| import { computed } from 'vue' | ||||
| import type { DirectionType } from '../config-provider' | ||||
|  | ||||
| const spaceCompactItem = () => { | ||||
|   return { | ||||
|     compactDirection: computed(() => null), | ||||
|     isFirstItem: computed(() => null), | ||||
|     isLastItem: computed(() => null), | ||||
|     compactSize: computed(() => null) | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const [useSpaceCompactProvider, useSpaceCompactInject] = createInjectionState(spaceCompactItem) | ||||
|  | ||||
| export const useSpaceCompactItemState = (): ReturnType<typeof spaceCompactItem> | undefined => useSpaceCompactInject() | ||||
|  | ||||
| export const useCompactItemContext = (prefixCls: Ref<string>, direction: Ref<DirectionType>) => { | ||||
|   const compactItemContext = useSpaceCompactItemState() | ||||
|  | ||||
|   const compactItemClassnames = computed(() => { | ||||
|     if (!compactItemContext) return '' | ||||
|  | ||||
|     const { compactDirection, isFirstItem, isLastItem } = compactItemContext | ||||
|     const separator = compactDirection.value === 'vertical' ? '-vertical-' : '-' | ||||
|  | ||||
|     return classNames({ | ||||
|       [`${prefixCls.value}-compact${separator}item`]: true, | ||||
|       [`${prefixCls.value}-compact${separator}first-item`]: isFirstItem.value, | ||||
|       [`${prefixCls.value}-compact${separator}last-item`]: isLastItem.value, | ||||
|       [`${prefixCls.value}-compact${separator}item-rtl`]: direction.value === 'rtl' | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   return { | ||||
|     compactSize: compactItemContext?.compactSize, | ||||
|     compactDirection: compactItemContext?.compactDirection, | ||||
|     compactItemClassnames | ||||
|   } | ||||
| } | ||||
							
								
								
									
										142
									
								
								components/space/compact.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								components/space/compact.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| import { | ||||
|   anyType, | ||||
|   booleanType, | ||||
|   classNames, | ||||
|   createInjectionState, | ||||
|   filterEmpty, | ||||
|   isObject, | ||||
|   stringType | ||||
| } from '@v-c/utils' | ||||
| import type { Ref } from 'vue' | ||||
| import { computed, defineComponent } from 'vue' | ||||
| import type { DirectionType } from '../config-provider' | ||||
| import type { SizeType } from '../config-provider/context' | ||||
| import { useProviderConfigState } from '../config-provider/context' | ||||
| import useStyle from './style' | ||||
|  | ||||
| const spaceCompactItem = (props: any) => { | ||||
|   return { | ||||
|     compactDirection: computed(() => props.compactDirection), | ||||
|     isFirstItem: computed(() => props.isFirstItem), | ||||
|     isLastItem: computed(() => props.isLastItem), | ||||
|     compactSize: computed(() => props.compactSize) | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const [useSpaceCompactProvider, useSpaceCompactInject] = | ||||
|   createInjectionState(spaceCompactItem) | ||||
|  | ||||
| export const useSpaceCompactItemState = (): | ||||
|   | ReturnType<typeof spaceCompactItem> | ||||
|   | undefined => useSpaceCompactInject() | ||||
|  | ||||
| export const useCompactItemContext = ( | ||||
|   prefixCls: Ref<string>, | ||||
|   direction: Ref<DirectionType> | ||||
| ) => { | ||||
|   const compactItemContext = useSpaceCompactItemState() | ||||
|  | ||||
|   const compactItemClassnames = computed(() => { | ||||
|     if (!compactItemContext) return '' | ||||
|  | ||||
|     const { compactDirection, isFirstItem, isLastItem } = compactItemContext | ||||
|     const separator = compactDirection.value === 'vertical' ? '-vertical-' : '-' | ||||
|  | ||||
|     return classNames({ | ||||
|       [`${prefixCls.value}-compact${separator}item`]: true, | ||||
|       [`${prefixCls.value}-compact${separator}first-item`]: isFirstItem.value, | ||||
|       [`${prefixCls.value}-compact${separator}last-item`]: isLastItem.value, | ||||
|       [`${prefixCls.value}-compact${separator}item-rtl`]: | ||||
|         direction.value === 'rtl' | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   return { | ||||
|     compactSize: compactItemContext?.compactSize, | ||||
|     compactDirection: compactItemContext?.compactDirection, | ||||
|     compactItemClassnames | ||||
|   } | ||||
| } | ||||
|  | ||||
| const CompactItem = defineComponent({ | ||||
|   name: 'CompactItem', | ||||
|   inheritAttrs: false, | ||||
|   props: { | ||||
|     compactSize: anyType<SizeType>(), | ||||
|     compactDirection: stringType<'horizontal' | 'vertical'>(), | ||||
|     isFirstItem: booleanType(), | ||||
|     isLastItem: booleanType() | ||||
|   }, | ||||
|   setup(props, { slots }) { | ||||
|     useSpaceCompactProvider(props) | ||||
|     return () => slots.default?.() | ||||
|   } | ||||
| }) | ||||
|  | ||||
| export const spaceCompactProps = { | ||||
|   prefixCls: stringType(), | ||||
|   size: anyType<SizeType>('middle'), | ||||
|   direction: stringType<'horizontal' | 'vertical'>(), | ||||
|   block: anyType<boolean>(), | ||||
|   rootClassName: stringType() | ||||
| } | ||||
|  | ||||
| const Compact = defineComponent({ | ||||
|   name: 'Compact', | ||||
|   inheritAttrs: false, | ||||
|   props: spaceCompactProps, | ||||
|   setup(props, { slots, attrs }) { | ||||
|     const { getPrefixCls, direction: directionConfig } = | ||||
|       useProviderConfigState() | ||||
|     const prefixCls = computed(() => | ||||
|       getPrefixCls('space-compact', props.prefixCls) | ||||
|     ) | ||||
|     const [wrapSSR, hashId] = useStyle(prefixCls) | ||||
|     const compactItemContext = useSpaceCompactItemState() | ||||
|     return () => { | ||||
|       const childNodes = filterEmpty(slots.default?.()) | ||||
|       if (childNodes.length === 0) return null | ||||
|       const { rootClassName, size, direction } = props | ||||
|       const cls = classNames( | ||||
|         prefixCls.value, | ||||
|         hashId.value, | ||||
|         { | ||||
|           [`${prefixCls.value}-rtl`]: directionConfig.value === 'rtl', | ||||
|           [`${prefixCls.value}-block`]: props.block, | ||||
|           [`${prefixCls.value}-vertical`]: props.direction === 'vertical' | ||||
|         }, | ||||
|         attrs.class, | ||||
|         rootClassName | ||||
|       ) | ||||
|       const nodes = childNodes.map((child, index) => { | ||||
|         const key = | ||||
|           (isObject(child) && (child as any).key) || | ||||
|           `${prefixCls.value}-item-${index}` | ||||
|         return ( | ||||
|           <CompactItem | ||||
|             key={key} | ||||
|             compactSize={size} | ||||
|             compactDirection={direction} | ||||
|             isFirstItem={ | ||||
|               (index === 0 && !compactItemContext) || | ||||
|               compactItemContext?.isFirstItem.value | ||||
|             } | ||||
|             isLastItem={ | ||||
|               index === childNodes.length - 1 && | ||||
|               (!compactItemContext || compactItemContext?.isLastItem.value) | ||||
|             } | ||||
|           > | ||||
|             {child} | ||||
|           </CompactItem> | ||||
|         ) | ||||
|       }) | ||||
|       return wrapSSR( | ||||
|         <div {...attrs} class={cls}> | ||||
|           {nodes} | ||||
|         </div> | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
| }) | ||||
|  | ||||
| export default Compact | ||||
							
								
								
									
										157
									
								
								components/space/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								components/space/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| import { | ||||
|   anyType, | ||||
|   booleanType, | ||||
|   classNames, | ||||
|   createInjectionState, | ||||
|   filterEmpty, | ||||
|   isObject, | ||||
|   stringType, | ||||
|   vNodeType | ||||
| } from '@v-c/utils' | ||||
| import type { App, CSSProperties } from 'vue' | ||||
| import { computed, defineComponent, shallowRef } from 'vue' | ||||
| import type { SizeType } from '../config-provider/context' | ||||
| import { useProviderConfigState } from '../config-provider/context' | ||||
| import useFlexGapSupport from '../_util/hooks/flex-gap-support' | ||||
| import useStyle from './style' | ||||
| import Item from './item' | ||||
| import Compact from './compact' | ||||
|  | ||||
| export type SpaceSize = SizeType | number | ||||
|  | ||||
| const spaceState = function ({ sizes, supportFlexGap, latestIndex }: any) { | ||||
|   return { | ||||
|     latestIndex: computed(() => latestIndex.value), | ||||
|     horizontalSize: computed(() => sizes[0]), | ||||
|     verticalSize: computed(() => sizes[1]), | ||||
|     supportFlexGap: computed(() => supportFlexGap.value) | ||||
|   } | ||||
| } | ||||
|  | ||||
| export const [useSpaceProvider, useSpaceInject] = | ||||
|   createInjectionState(spaceState) | ||||
|  | ||||
| export const useSpaceContextState = () => | ||||
|   useSpaceInject() ?? { | ||||
|     latestIndex: computed(() => 0), | ||||
|     horizontalSize: computed(() => 0), | ||||
|     verticalSize: computed(() => 0), | ||||
|     supportFlexGap: computed(() => false) | ||||
|   } | ||||
|  | ||||
| export const spaceProps = { | ||||
|   prefixCls: stringType(), | ||||
|   rootClassName: stringType(), | ||||
|   size: anyType<SizeType | [SpaceSize, SpaceSize]>('small'), | ||||
|   direction: anyType<'horizontal' | 'vertical'>('horizontal'), | ||||
|   align: stringType<'start' | 'end' | 'center' | 'baseline'>(), | ||||
|   split: vNodeType(), | ||||
|   wrap: booleanType(false) | ||||
| } | ||||
|  | ||||
| const spaceSize = { | ||||
|   small: 8, | ||||
|   middle: 16, | ||||
|   large: 24 | ||||
| } | ||||
|  | ||||
| function getNumberSize(size: SpaceSize) { | ||||
|   return typeof size === 'string' ? spaceSize[size] : size || 0 | ||||
| } | ||||
| const Space = defineComponent({ | ||||
|   name: 'ASpace', | ||||
|   inheritAttrs: false, | ||||
|   props: spaceProps, | ||||
|   setup(props, { attrs, slots }) { | ||||
|     const { getPrefixCls, direction: directionConfig } = | ||||
|       useProviderConfigState() | ||||
|     const supportFlexGap = useFlexGapSupport() | ||||
|     const prefixCls = computed(() => getPrefixCls('space', props.prefixCls)) | ||||
|     const [wrapSSR, hashId] = useStyle(prefixCls) | ||||
|     const sizes = computed<[SpaceSize, SpaceSize]>(() => { | ||||
|       const { size } = props | ||||
|       if (Array.isArray(size)) { | ||||
|         return size.map(getNumberSize) as [SpaceSize, SpaceSize] | ||||
|       } | ||||
|       return [getNumberSize(size), getNumberSize(size)] as [ | ||||
|         SpaceSize, | ||||
|         SpaceSize | ||||
|       ] | ||||
|     }) | ||||
|     const latestIndex = shallowRef(0) | ||||
|  | ||||
|     useSpaceProvider({ sizes, supportFlexGap, latestIndex }) | ||||
|  | ||||
|     return () => { | ||||
|       const { align, direction, rootClassName, split, wrap } = props | ||||
|       const childNodes = filterEmpty(slots.default?.()) | ||||
|       const mergedAlign = | ||||
|         align === undefined && direction === 'horizontal' ? 'center' : align | ||||
|       const cn = classNames( | ||||
|         prefixCls.value, | ||||
|         hashId.value, | ||||
|         `${prefixCls.value}-${direction}`, | ||||
|         { | ||||
|           [`${prefixCls.value}-rtl`]: directionConfig.value === 'rtl', | ||||
|           [`${prefixCls.value}-align-${mergedAlign}`]: mergedAlign | ||||
|         }, | ||||
|         attrs.class, | ||||
|         rootClassName | ||||
|       ) | ||||
|       const itemClassName = `${prefixCls.value}-item` | ||||
|       const marginDirection = | ||||
|         directionConfig.value === 'rtl' ? 'marginLeft' : 'marginRight' | ||||
|       const nodes = childNodes.map((child, i) => { | ||||
|         if (child !== null && child !== undefined) { | ||||
|           latestIndex.value = i | ||||
|         } | ||||
|         const key = | ||||
|           (isObject(child) && (child as any).key) || `${itemClassName}-${i}` | ||||
|         return ( | ||||
|           <Item | ||||
|             class={itemClassName} | ||||
|             key={key} | ||||
|             direction={direction} | ||||
|             index={i} | ||||
|             marginDirection={marginDirection} | ||||
|             split={split} | ||||
|             wrap={wrap} | ||||
|           > | ||||
|             {child} | ||||
|           </Item> | ||||
|         ) | ||||
|       }) | ||||
|  | ||||
|       // =========================== Render =========================== | ||||
|       if (childNodes.length === 0) { | ||||
|         return null | ||||
|       } | ||||
|       const gapStyle: CSSProperties = {} | ||||
|       if (wrap) { | ||||
|         gapStyle.flexWrap = 'wrap' | ||||
|         if (!supportFlexGap.value) { | ||||
|           gapStyle.marginBottom = `-${sizes.value[1]}px` | ||||
|         } | ||||
|       } | ||||
|       if (supportFlexGap.value) { | ||||
|         gapStyle.columnGap = `${sizes.value[0]}px` | ||||
|         gapStyle.rowGap = `${sizes.value[1]}px` | ||||
|       } | ||||
|       return wrapSSR( | ||||
|         <div {...attrs} class={cn} style={[gapStyle, (attrs as any).style]}> | ||||
|           {nodes} | ||||
|         </div> | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
| }) | ||||
|  | ||||
| Space.install = function (app: App) { | ||||
|   app.component('ASpace', Space) | ||||
| } | ||||
|  | ||||
| Space.Compact = Compact | ||||
| export default Space as typeof Space & | ||||
|   Plugin & { | ||||
|     readonly Compact: typeof Compact | ||||
|   } | ||||
							
								
								
									
										74
									
								
								components/space/item.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								components/space/item.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| import { | ||||
|   booleanType, | ||||
|   filterEmpty, | ||||
|   numberType, | ||||
|   someType, | ||||
|   stringType, | ||||
|   vNodeType | ||||
| } from '@v-c/utils' | ||||
| import type { CSSProperties, ExtractPropTypes, VNodeChild } from 'vue' | ||||
| import { defineComponent } from 'vue' | ||||
| import { useSpaceContextState } from './index' | ||||
|  | ||||
| export const itemProps = { | ||||
|   className: stringType(), | ||||
|   children: vNodeType(), | ||||
|   index: numberType(), | ||||
|   direction: stringType<'horizontal' | 'vertical'>(), | ||||
|   marginDirection: stringType<'marginLeft' | 'marginRight'>(), | ||||
|   split: someType<string | (() => VNodeChild) | VNodeChild>([ | ||||
|     String, | ||||
|     Function, | ||||
|     Object | ||||
|   ]), | ||||
|   wrap: booleanType() | ||||
| } | ||||
|  | ||||
| export type ItemProps = ExtractPropTypes<typeof itemProps> | ||||
|  | ||||
| const Item = defineComponent({ | ||||
|   name: 'VcSpaceItem', | ||||
|   props: itemProps, | ||||
|   setup(props, { attrs, slots }) { | ||||
|     const { supportFlexGap, latestIndex, verticalSize, horizontalSize } = | ||||
|       useSpaceContextState() | ||||
|     return () => { | ||||
|       const { direction, index, marginDirection, split, wrap } = props | ||||
|       const children = slots.default?.() | ||||
|       if (!children || filterEmpty(children).length === 0) { | ||||
|         return null | ||||
|       } | ||||
|       let style: CSSProperties = {} | ||||
|       if (!supportFlexGap.value) { | ||||
|         if (direction === 'vertical') { | ||||
|           if (index < latestIndex.value) { | ||||
|             style.marginBottom = `${horizontalSize.value / (split ? 2 : 1)}px` | ||||
|           } | ||||
|         } else { | ||||
|           style = { | ||||
|             ...(index < latestIndex.value && | ||||
|               ({ | ||||
|                 [marginDirection]: `${horizontalSize.value / (split ? 2 : 1)}px` | ||||
|               } as CSSProperties)), | ||||
|             ...(wrap && { paddingBottom: `${verticalSize.value}px` }) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return ( | ||||
|         <> | ||||
|           <div class={attrs.class} style={style}> | ||||
|             {children} | ||||
|           </div> | ||||
|           {index < latestIndex.value && split && ( | ||||
|             <span class={`${attrs.class}-split`} style={style}> | ||||
|               {split} | ||||
|             </span> | ||||
|           )} | ||||
|         </> | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
| }) | ||||
|  | ||||
| export default Item | ||||
		Reference in New Issue
	
	Block a user