Skip to content

Commit

Permalink
add: new layout system
Browse files Browse the repository at this point in the history
  • Loading branch information
cjhih456 committed Jan 8, 2025
1 parent 4268c74 commit eda086b
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/components/ui/Form/InputLayout/InputLayoutContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createContext } from 'react'

export interface InputLayoutContextValues {
isValid: boolean,
isError: boolean,
}

const InputLayoutContext = createContext<InputLayoutContextValues>({
isValid: false,
isError: false,
})
export default InputLayoutContext
9 changes: 9 additions & 0 deletions src/components/ui/Form/InputLayout/atom/InputError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ReactNode } from 'react';

interface InputErrorProps {
children?: ReactNode
}

export default function InputError({ children, ...props }: InputErrorProps & CssProps) {
return <div {...props}>{children}</div>
}
16 changes: 16 additions & 0 deletions src/components/ui/Form/InputLayout/atom/InputLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { ReactNode } from 'react';
import { isValidElement } from 'react';

interface InputLabelProps {
children?: ReactNode
htmlFor?: string
}

export default function InputLabel({ children, ...props }: InputLabelProps & CssProps) {
return <label {...props}>{children}</label>
}
export const InputLabelTypeCheck = (el: ReactNode) => {
if (!isValidElement(el)) return false
if (el.type === InputLabel({}).type) return true
return false
}
9 changes: 9 additions & 0 deletions src/components/ui/Form/InputLayout/atom/InputOutline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ReactNode } from 'react';

interface InputOutlineProps {
children?: ReactNode
}

export default function InputOutline({ children, ...props }: InputOutlineProps & CssProps) {
return <div {...props}>{children}</div>
}
15 changes: 15 additions & 0 deletions src/components/ui/Form/InputLayout/atom/InputPostfix.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ReactNode } from 'react';
import { isValidElement } from 'react';

interface InputPostfixProps {
children?: ReactNode
}

export default function InputPostfix({ children, ...props }: InputPostfixProps & CssProps) {
return <div {...props}>{children}</div>
}
export const TypeCheck = (el: ReactNode) => {
if (!isValidElement(el)) return false
if (el.type === InputPostfix({}).type) return true
return false
}
15 changes: 15 additions & 0 deletions src/components/ui/Form/InputLayout/atom/InputPrefix.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ReactNode } from 'react';
import { isValidElement } from 'react';

interface InputPrefixProps {
children?: ReactNode
}

export default function InputPrefix({ children, ...props }: InputPrefixProps & CssProps) {
return <div {...props}>{children}</div>
}
export const TypeCheck = (el: ReactNode) => {
if (!isValidElement(el)) return false
if (el.type === InputPrefix({}).type) return true
return false
}
15 changes: 15 additions & 0 deletions src/components/ui/Form/InputLayout/atom/InputShell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ReactNode } from 'react';
import { isValidElement } from 'react';

interface InputShellProps {
children?: ReactNode
}

export default function InputShell({ children, ...props }: InputShellProps & CssProps) {
return <div {...props}>{children}</div>
}
export const TypeCheck = (el: ReactNode) => {
if (!isValidElement(el)) return false
if (el.type === InputShell({}).type) return true
return false
}
11 changes: 11 additions & 0 deletions src/components/ui/Form/InputLayout/atom/InputTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ReactElement } from 'react';
import { cloneElement } from 'react';

interface InputTagProps {
children?: ReactElement
}

export default function InputTag({ children, ...props }: InputTagProps & CssProps) {
const input = cloneElement(children || <input />, props)
return <>{input}</>
}
47 changes: 47 additions & 0 deletions src/components/ui/Form/InputLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { InputLayoutContextValues } from './InputLayoutContext';
import type { ReactNode } from 'react';
import { cloneElement } from 'react';
import InputLayoutContext from './InputLayoutContext';
import InputError from './atom/InputError';
import InputLabel from './atom/InputLabel';
import InputOutline from './atom/InputOutline';
import InputPostfix from './atom/InputPostfix';
import InputPrefix from './atom/InputPrefix';
import InputShell from './atom/InputShell';
import InputTag from './atom/InputTag';
import { getFilteredElement } from './utils/getFilteredElement';

interface InputLayoutProps {
children: ReactNode
}
const InputLayout = ({ children, className, ...props }: InputLayoutProps & CssProps & InputLayoutContextValues) => {
const errorElements = getFilteredElement(children, InputError)
const labelElements = getFilteredElement(children, InputLabel)
const outlineElement = getFilteredElement(children, InputOutline)
const postfixElement = getFilteredElement(children, InputPostfix)
const prefixElement = getFilteredElement(children, InputPrefix)
const shellElement = getFilteredElement(children, InputShell)
const inputElement = getFilteredElement(children, InputTag)

const displayShellElement = cloneElement(
shellElement[0] || <div />,
[prefixElement, inputElement, postfixElement, labelElements, outlineElement].flat().filter(Boolean)
)
console.log(shellElement)
return <InputLayoutContext.Provider value={props}>
<div className={className}>
{displayShellElement}
{errorElements}
</div>
</InputLayoutContext.Provider>
}

InputLayout.Error = InputError
InputLayout.Label = InputLabel
InputLayout.Outline = InputOutline
InputLayout.Postfix = InputPostfix
InputLayout.Prefix = InputPrefix
InputLayout.Shell = InputShell
InputLayout.Tag = InputTag

export default InputLayout
7 changes: 7 additions & 0 deletions src/components/ui/Form/InputLayout/useInputLayoutContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useContext } from 'react';
import InputLayoutContext from './InputLayoutContext';

export default function useInputLayout() {
const context = useContext(InputLayoutContext) || {}
return context
}
9 changes: 9 additions & 0 deletions src/components/ui/Form/InputLayout/utils/TypeCheck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { FC, ReactElement, ReactNode } from 'react';
import { isValidElement } from 'react'

export type TypeCheckFuncType = (_el: ReactNode, _El: FC) => _el is ReactElement<object>

export const TypeCheck: TypeCheckFuncType = (el, El): el is ReactElement<object> => {
if (!isValidElement(el)) return false
return el.type === El
}
11 changes: 11 additions & 0 deletions src/components/ui/Form/InputLayout/utils/getFilteredElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { FC, ReactElement, ReactNode } from 'react';
import { Children } from 'react'
import { TypeCheck } from './TypeCheck';

export const getFilteredElement = (nodes: ReactNode, type: FC): ReactElement[] => {
const childrenArray = Children.toArray(nodes)
const filteredArray = childrenArray.filter(
child => TypeCheck(child, type)
)
return filteredArray
}
File renamed without changes.
1 change: 1 addition & 0 deletions types/emotion/cssprops.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Theme } from '@emotion/react';
declare global {
interface CssProps {
css?: Interpolation<Theme>
className?: string
}
}
export { }

0 comments on commit eda086b

Please sign in to comment.