Skip to content

Commit

Permalink
feat(ui/system): slottable 개념 도입 (#199)
Browse files Browse the repository at this point in the history
* feat(ui/system): Slottable 컴포넌트 추가

* feat(ui/system/slottable.tsx): Slottable 컴포넌트 type 분리

* feat(ui/system/create-polymorphic-component.ts): Slottable 상황 추가
  • Loading branch information
sukvvon authored Jun 20, 2024
1 parent f349c1b commit 3a46978
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 0 deletions.
46 changes: 46 additions & 0 deletions packages/ui/system/src/create-polymorphic-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,27 @@ import {
Children,
type ComponentPropsWithoutRef,
type ElementType,
type ReactElement,
type ReactNode,
type Ref,
cloneElement,
createElement,
isValidElement,
} from 'react';
import { forwardRef } from './forward-ref';
import { Slottable, type SlottableProps } from './slottable';
import {
type ComponentWithPolymorphic,
type JsxElements,
type PolymorphicProps,
} from './types';

export function isSlottable(
child: ReactNode,
): child is ReactElement<SlottableProps> {
return isValidElement(child) && child.type === Slottable;
}

export type FavolinkComponent<Component extends ElementType> =
ComponentWithPolymorphic<Component, object>;

Expand All @@ -36,6 +45,43 @@ export function createPolymorphicComponent<Component extends ElementType>(
);
}

const childrenArray = Children.toArray(children);
const slottable = childrenArray.find(isSlottable);

if (slottable) {
const newElement = slottable.props.children;
const newChildren = childrenArray.map((child) => {
if (child === slottable) {
if (Children.count(newElement) > 1) {
return Children.only(null);
}

return isValidElement(newElement)
? ((newElement as ReactElement<{ children: ReactNode }>).props
.children as ReactNode)
: null;
} else {
return child;
}
});

return isValidElement(newElement)
? cloneElement(
newElement as ReactElement,
{
...mergeProps(restProps, newElement.props),
ref: ref
? composeRefs(
ref,
(newElement as ReactElement & { ref: Ref<any> }).ref,
)
: (newElement as ReactElement & { ref: Ref<any> }).ref,
},
newChildren,
)
: null;
}

const onlyChild = Children.only(children);

return isValidElement(onlyChild)
Expand Down
1 change: 1 addition & 0 deletions packages/ui/system/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from './create-polymorphic-component';
export * from './create-raw-style-props';
export * from './factory';
export * from './forward-ref';
export * from './slottable';
export * from './types';
11 changes: 11 additions & 0 deletions packages/ui/system/src/slottable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { type ReactNode } from 'react';

export type SlottableProps = {
children: ReactNode;
};

export function Slottable(props: SlottableProps) {
const { children } = props;

return children;
}

0 comments on commit 3a46978

Please sign in to comment.