@jongh/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Accordion/Accordion.tsx +274 -0
- package/dist/components/Accordion/index.tsx +37 -0
- package/dist/components/Accordion/useAccordionHeight.ts +28 -0
- package/dist/components/Avatar/Avatar.tsx +18 -0
- package/dist/components/Avatar/useAvatar.ts +73 -0
- package/dist/components/Button/index.tsx +66 -0
- package/dist/components/Slider/Slider.tsx +140 -0
- package/dist/components/Slider/style.ts +42 -0
- package/dist/components/Tab/Tab.tsx +35 -0
- package/dist/components/Tab/TabContent.tsx +60 -0
- package/dist/components/Tab/TabIndicator.tsx +19 -0
- package/dist/components/Tab/TabList.tsx +118 -0
- package/dist/components/Tab/index.ts +4 -0
- package/dist/components/Tab/style.ts +9 -0
- package/dist/components/Tab/useRovingTabIndex.tsx +172 -0
- package/dist/components/Tab/useTabContext.ts +11 -0
- package/dist/components/TagButton/TagButton.tsx +75 -0
- package/dist/components/TagButton/style.ts +35 -0
- package/dist/components/core/Polymorphic/index.ts +26 -0
- package/dist/components/index.ts +5 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.mjs +2 -0
- package/package.json +34 -0
- package/park-ui.json +3 -0
- package/src/fetchComponent.ts +27 -0
- package/src/getConfig.ts +53 -0
- package/src/index.ts +46 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +14 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { forwardRef, useId, type ReactNode } from "react"
|
|
2
|
+
import { useControlledState } from "../../hooks/useControllableState"
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
4
|
+
import { TabProvider } from "./useTabContext"
|
|
5
|
+
|
|
6
|
+
export interface TabProps {
|
|
7
|
+
children: ReactNode
|
|
8
|
+
selected?: string
|
|
9
|
+
defaultValue?: string
|
|
10
|
+
onSelect?: (value: string) => void
|
|
11
|
+
asChild?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
export const Tab = forwardRef<any, TabProps>(
|
|
16
|
+
({ children, selected, defaultValue, onSelect, asChild }, ref) => {
|
|
17
|
+
const Element = asChild ? Slot : "div"
|
|
18
|
+
|
|
19
|
+
const [value, setValue] = useControlledState({
|
|
20
|
+
prop: selected,
|
|
21
|
+
onChange: onSelect,
|
|
22
|
+
defaultProp: defaultValue,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const onSelectItem = (value: string) => {
|
|
26
|
+
setValue(value)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<TabProvider selected={value} onSelect={onSelectItem} tabId={useId()}>
|
|
31
|
+
<Element ref={ref}>{children}</Element>
|
|
32
|
+
</TabProvider>
|
|
33
|
+
)
|
|
34
|
+
},
|
|
35
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ReactNode } from "react"
|
|
2
|
+
import { useTabContext } from "./useTabContext"
|
|
3
|
+
import { motion } from "framer-motion"
|
|
4
|
+
|
|
5
|
+
interface TabContentProps {
|
|
6
|
+
children: ReactNode
|
|
7
|
+
value: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const tabContentVariant = {
|
|
11
|
+
active: {
|
|
12
|
+
display: "block",
|
|
13
|
+
transition: {
|
|
14
|
+
staggerChildren: 0.2,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
inactive: {
|
|
18
|
+
display: "none",
|
|
19
|
+
},
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
const cardVariant = {
|
|
23
|
+
active: {
|
|
24
|
+
opacity: 1,
|
|
25
|
+
x: 0,
|
|
26
|
+
transition: {
|
|
27
|
+
duration: 0.3,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
inactive: {
|
|
31
|
+
opacity: 0,
|
|
32
|
+
x: 10,
|
|
33
|
+
transition: {
|
|
34
|
+
duration: 0.3,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
export const TabContent = ({ children, value }: TabContentProps) => {
|
|
39
|
+
const { selected, tabId } = useTabContext("tab")
|
|
40
|
+
|
|
41
|
+
const isSelected = selected === value
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<motion.div
|
|
45
|
+
role="tabpanel"
|
|
46
|
+
tabIndex={0}
|
|
47
|
+
id={tabId + "-tabpanel-" + value}
|
|
48
|
+
data-state={isSelected ? "active" : "inactive"}
|
|
49
|
+
aria-labelledby={tabId + "-tabitem-" + value}
|
|
50
|
+
key={tabId + "-tabpanel-" + value}
|
|
51
|
+
variants={tabContentVariant}
|
|
52
|
+
animate={isSelected ? "active" : "inactive"}
|
|
53
|
+
initial="inactive"
|
|
54
|
+
>
|
|
55
|
+
<motion.div key={value} variants={cardVariant}>
|
|
56
|
+
{isSelected && children}
|
|
57
|
+
</motion.div>
|
|
58
|
+
</motion.div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { css } from "@styled-system/css"
|
|
2
|
+
import { motion } from "framer-motion"
|
|
3
|
+
|
|
4
|
+
export const TabIndicator = () => {
|
|
5
|
+
return (
|
|
6
|
+
<motion.div
|
|
7
|
+
className={css({
|
|
8
|
+
width: "var(--indicator-width)",
|
|
9
|
+
height: "2px",
|
|
10
|
+
transform: "translateZ(0px)",
|
|
11
|
+
position: "absolute",
|
|
12
|
+
bottom: 0,
|
|
13
|
+
left: `var(--indicator-left)`,
|
|
14
|
+
background: "red_300",
|
|
15
|
+
borderRadius: "rounded",
|
|
16
|
+
})}
|
|
17
|
+
></motion.div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ReactNode,
|
|
3
|
+
type ReactElement,
|
|
4
|
+
type ComponentPropsWithRef,
|
|
5
|
+
forwardRef,
|
|
6
|
+
} from "react"
|
|
7
|
+
import { useTabContext } from "./useTabContext"
|
|
8
|
+
|
|
9
|
+
import { css, cx } from "@styled-system/css"
|
|
10
|
+
import { RovingTabIndexRoot, useRovingTabIndex } from "./useRovingTabIndex"
|
|
11
|
+
import isHotkey from "is-hotkey"
|
|
12
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
13
|
+
import { composeRefs } from "../../hooks/useComposedRefs"
|
|
14
|
+
import {
|
|
15
|
+
getNextFocusableId,
|
|
16
|
+
getPrevFocusableId,
|
|
17
|
+
} from "../../utils/getFocusableId"
|
|
18
|
+
|
|
19
|
+
interface TabListProps {
|
|
20
|
+
children: ReactNode
|
|
21
|
+
className?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const setIndicatorStyle = (target: HTMLElement) => {
|
|
25
|
+
const targetRect = target.getBoundingClientRect()
|
|
26
|
+
const parentRect = target.parentElement?.getBoundingClientRect()
|
|
27
|
+
if (!targetRect || !parentRect) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
document.documentElement.style.setProperty(
|
|
31
|
+
"--indicator-left",
|
|
32
|
+
`${Math.abs(parentRect.left - targetRect.left)}px`,
|
|
33
|
+
)
|
|
34
|
+
document.documentElement.style.setProperty(
|
|
35
|
+
"--indicator-width",
|
|
36
|
+
`${Math.abs(targetRect.width)}px`,
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const TabList = ({ children, className }: TabListProps) => {
|
|
41
|
+
const { selected } = useTabContext("tab")
|
|
42
|
+
return (
|
|
43
|
+
<RovingTabIndexRoot as="div" active={selected}>
|
|
44
|
+
<div
|
|
45
|
+
role="tablist"
|
|
46
|
+
className={cx(
|
|
47
|
+
className,
|
|
48
|
+
css({ position: "relative", display: "flex" }),
|
|
49
|
+
)}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</div>
|
|
53
|
+
</RovingTabIndexRoot>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
type TabItemProps = ComponentPropsWithRef<"button"> & {
|
|
57
|
+
value: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const RovingItem = ({
|
|
61
|
+
value,
|
|
62
|
+
children,
|
|
63
|
+
}: {
|
|
64
|
+
value: string
|
|
65
|
+
children: ReactElement
|
|
66
|
+
}) => {
|
|
67
|
+
const { getOrderedItems, getRovingProps } = useRovingTabIndex(value)
|
|
68
|
+
return (
|
|
69
|
+
<Slot
|
|
70
|
+
{...getRovingProps<"button">({
|
|
71
|
+
onKeyDown: (e) => {
|
|
72
|
+
const items = getOrderedItems()
|
|
73
|
+
let nextItem
|
|
74
|
+
if (isHotkey("right", e)) {
|
|
75
|
+
nextItem = getNextFocusableId(items, value)
|
|
76
|
+
} else if (isHotkey("left", e)) {
|
|
77
|
+
nextItem = getPrevFocusableId(items, value)
|
|
78
|
+
}
|
|
79
|
+
nextItem?.element.focus()
|
|
80
|
+
},
|
|
81
|
+
})}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</Slot>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const TabItem = forwardRef<HTMLButtonElement, TabItemProps>(
|
|
89
|
+
({ children, className, value, ...props }: TabItemProps, forwardRef) => {
|
|
90
|
+
const { selected, onSelect, tabId } = useTabContext("tab")
|
|
91
|
+
const isSelected = selected === value
|
|
92
|
+
|
|
93
|
+
const handleSelect = () => {
|
|
94
|
+
onSelect?.(value)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<RovingItem value={value}>
|
|
99
|
+
<button
|
|
100
|
+
ref={composeRefs((node) => {
|
|
101
|
+
if (isSelected && node) {
|
|
102
|
+
setIndicatorStyle(node as HTMLElement)
|
|
103
|
+
}
|
|
104
|
+
}, forwardRef)}
|
|
105
|
+
onClick={handleSelect}
|
|
106
|
+
onFocus={handleSelect}
|
|
107
|
+
role="tab"
|
|
108
|
+
aria-selected={isSelected}
|
|
109
|
+
id={tabId + "-tabitem-" + value}
|
|
110
|
+
aria-controls={tabId + "-tabpanel-" + value}
|
|
111
|
+
{...props}
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
</button>
|
|
115
|
+
</RovingItem>
|
|
116
|
+
)
|
|
117
|
+
},
|
|
118
|
+
)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import isHotkey from "is-hotkey"
|
|
2
|
+
import {
|
|
3
|
+
ReactNode,
|
|
4
|
+
useCallback,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
FocusEvent,
|
|
8
|
+
MouseEvent,
|
|
9
|
+
KeyboardEvent,
|
|
10
|
+
ComponentPropsWithoutRef,
|
|
11
|
+
ElementType,
|
|
12
|
+
MutableRefObject,
|
|
13
|
+
} from "react"
|
|
14
|
+
import { useControlledState } from "../../hooks/useControllableState"
|
|
15
|
+
import { createContext } from "../../hooks/createContext"
|
|
16
|
+
import { composeRefs } from "../../hooks/useComposedRefs"
|
|
17
|
+
|
|
18
|
+
export type RovingTabIndexItem = {
|
|
19
|
+
value: string
|
|
20
|
+
element: HTMLElement
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function onFocusFirst(candidates: HTMLElement[]) {
|
|
24
|
+
const previousFocus = document.activeElement
|
|
25
|
+
while (document.activeElement === previousFocus && candidates.length > 0) {
|
|
26
|
+
candidates.shift()?.focus()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type RovingTabIndexContext = {
|
|
31
|
+
currentFocusedValue: string | null
|
|
32
|
+
setFocusableId: (id: string) => void
|
|
33
|
+
onShiftTab: () => void
|
|
34
|
+
getOrderedItems: () => RovingTabIndexItem[]
|
|
35
|
+
elements: MutableRefObject<Map<string, HTMLElement>>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const [RovingTabIndexProvider, useRovingTabIndexContext] =
|
|
39
|
+
createContext<RovingTabIndexContext>("rovingTabIndex")
|
|
40
|
+
|
|
41
|
+
const NODE_SELECTOR = "data-roving-tabindex-node"
|
|
42
|
+
const ROOT_SELECTOR = "data-roving-tabindex-root"
|
|
43
|
+
const NOT_FOCUSABLE_SELECTOR = "data-roving-tabindex-not-focusable"
|
|
44
|
+
const SELECTOR_ACTIVE = `:where([${NODE_SELECTOR}=true]):not(:where([${NOT_FOCUSABLE_SELECTOR}=true] *))`
|
|
45
|
+
|
|
46
|
+
type RovingTabIndexRootBaseProps<T> = {
|
|
47
|
+
children: ReactNode | ReactNode[]
|
|
48
|
+
active?: string
|
|
49
|
+
setActive?: (value: string) => void
|
|
50
|
+
as?: T
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type RovingTabIndexRootProps<T extends ElementType> =
|
|
54
|
+
RovingTabIndexRootBaseProps<T> &
|
|
55
|
+
Omit<ComponentPropsWithoutRef<T>, keyof RovingTabIndexRootBaseProps<T>>
|
|
56
|
+
|
|
57
|
+
export const RovingTabIndexRoot = <T extends ElementType>({
|
|
58
|
+
children,
|
|
59
|
+
active,
|
|
60
|
+
setActive,
|
|
61
|
+
as,
|
|
62
|
+
ref,
|
|
63
|
+
...props
|
|
64
|
+
}: RovingTabIndexRootProps<T>) => {
|
|
65
|
+
const Component = as || "div"
|
|
66
|
+
const [isShiftTabbing, setIsShiftTabbing] = useState(false)
|
|
67
|
+
|
|
68
|
+
const [value = null, setValue] = useControlledState({
|
|
69
|
+
prop: active,
|
|
70
|
+
onChange: setActive,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const rootRef = useRef<HTMLDivElement | null>(null)
|
|
74
|
+
const elementsRef = useRef<Map<string, HTMLElement>>(new Map())
|
|
75
|
+
const getOrderedItems = useCallback(() => {
|
|
76
|
+
if (!rootRef.current) return []
|
|
77
|
+
const activeElements = Array.from(
|
|
78
|
+
rootRef.current.querySelectorAll(SELECTOR_ACTIVE),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return Array.from(elementsRef.current)
|
|
82
|
+
.sort(
|
|
83
|
+
(a, b) => activeElements.indexOf(a[1]) - activeElements.indexOf(b[1]),
|
|
84
|
+
)
|
|
85
|
+
.map(([value, element]) => ({ value, element }))
|
|
86
|
+
}, [])
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<RovingTabIndexProvider
|
|
90
|
+
setFocusableId={(value: string) => {
|
|
91
|
+
setValue(value)
|
|
92
|
+
}}
|
|
93
|
+
onShiftTab={() => {
|
|
94
|
+
setIsShiftTabbing(true)
|
|
95
|
+
}}
|
|
96
|
+
currentFocusedValue={value}
|
|
97
|
+
getOrderedItems={getOrderedItems}
|
|
98
|
+
elements={elementsRef}
|
|
99
|
+
>
|
|
100
|
+
<Component
|
|
101
|
+
{...{ [ROOT_SELECTOR]: true }}
|
|
102
|
+
// tabIndex={isShiftTabbing ? -1 : 0}
|
|
103
|
+
onFocus={(e) => {
|
|
104
|
+
console.log("parent focus")
|
|
105
|
+
if (e.target !== e.currentTarget) return
|
|
106
|
+
if (isShiftTabbing) return
|
|
107
|
+
const orderedItems = getOrderedItems()
|
|
108
|
+
if (orderedItems.length === 0) return
|
|
109
|
+
|
|
110
|
+
const candidates = [
|
|
111
|
+
elementsRef.current.get(value ?? ""),
|
|
112
|
+
...orderedItems.map((i) => i.element),
|
|
113
|
+
].filter((element): element is HTMLElement => element != null)
|
|
114
|
+
|
|
115
|
+
onFocusFirst(candidates)
|
|
116
|
+
}}
|
|
117
|
+
onBlur={() => setIsShiftTabbing(false)}
|
|
118
|
+
ref={composeRefs(ref, rootRef)}
|
|
119
|
+
{...props}
|
|
120
|
+
>
|
|
121
|
+
{children}
|
|
122
|
+
</Component>
|
|
123
|
+
</RovingTabIndexProvider>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const useRovingTabIndex = (value: string) => {
|
|
128
|
+
const {
|
|
129
|
+
currentFocusedValue,
|
|
130
|
+
setFocusableId,
|
|
131
|
+
onShiftTab,
|
|
132
|
+
getOrderedItems,
|
|
133
|
+
elements,
|
|
134
|
+
} = useRovingTabIndexContext("rovingTabIndex")
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
getOrderedItems,
|
|
138
|
+
isFocusable: currentFocusedValue === value,
|
|
139
|
+
getRovingProps: <T extends ElementType>(
|
|
140
|
+
props?: ComponentPropsWithoutRef<T>,
|
|
141
|
+
) => ({
|
|
142
|
+
...props,
|
|
143
|
+
ref: (element: HTMLElement | null) => {
|
|
144
|
+
if (element) {
|
|
145
|
+
elements.current.set(value, element)
|
|
146
|
+
} else {
|
|
147
|
+
elements.current.delete(value)
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
onMouseDown: (e: MouseEvent) => {
|
|
151
|
+
props?.onMouseDown?.(e)
|
|
152
|
+
if (e.target !== e.currentTarget) return
|
|
153
|
+
setFocusableId(value)
|
|
154
|
+
},
|
|
155
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
156
|
+
props?.onKeyDown?.(e)
|
|
157
|
+
if (e.target !== e.currentTarget) return
|
|
158
|
+
if (isHotkey("shift+tab", e)) {
|
|
159
|
+
onShiftTab()
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
onFocus: (e: FocusEvent) => {
|
|
164
|
+
props?.onFocus?.(e)
|
|
165
|
+
if (e.target !== e.currentTarget) return
|
|
166
|
+
setFocusableId(value)
|
|
167
|
+
},
|
|
168
|
+
[NODE_SELECTOR]: true,
|
|
169
|
+
tabIndex: currentFocusedValue === value ? 0 : -1,
|
|
170
|
+
}),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createContext } from "../../hooks/createContext"
|
|
2
|
+
|
|
3
|
+
type Value = string
|
|
4
|
+
|
|
5
|
+
export interface TabContext {
|
|
6
|
+
selected?: Value
|
|
7
|
+
onSelect?: (index: Value) => void
|
|
8
|
+
tabId: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const [TabProvider, useTabContext] = createContext<TabContext>("tab")
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// import { cx } from "@styled-system/css"
|
|
2
|
+
// // import { tagButton, type TagButtonVariant } from "@styled-system/recipes"
|
|
3
|
+
// import { forwardRef } from "react"
|
|
4
|
+
// import { useControlledState } from "../../hooks/useControllableState"
|
|
5
|
+
|
|
6
|
+
// type TagButtonProps = Partial<TagButtonVariant> & {
|
|
7
|
+
// /** 버튼 내부에 표시될 텍스트 */
|
|
8
|
+
// children: string
|
|
9
|
+
|
|
10
|
+
// /** 버튼의 비활성화 여부를 지정합니다. true일 경우 버튼이 비활성화됩니다. */
|
|
11
|
+
// disabled?: boolean
|
|
12
|
+
|
|
13
|
+
// /** 버튼에 적용할 추가적인 CSS 클래스명 */
|
|
14
|
+
// className?: string
|
|
15
|
+
|
|
16
|
+
// /** 버튼의 고유 식별자 */
|
|
17
|
+
// id?: string
|
|
18
|
+
|
|
19
|
+
// /**
|
|
20
|
+
// * 버튼 클릭 시 실행될 함수
|
|
21
|
+
// * @param e 선택적 boolean 매개변수
|
|
22
|
+
// */
|
|
23
|
+
// onClick?: (e?: boolean) => void
|
|
24
|
+
|
|
25
|
+
// /** 버튼의 클릭 상태를 나타냅니다. true일 경우 클릭된 상태를 의미합니다. */
|
|
26
|
+
// isClicked?: boolean
|
|
27
|
+
|
|
28
|
+
// /** 버튼의 초기 클릭 상태를 설정합니다. true일 경우 처음부터 클릭된 상태로 시작합니다. */
|
|
29
|
+
// defaultClick?: boolean
|
|
30
|
+
// }
|
|
31
|
+
|
|
32
|
+
// export const TagButton = forwardRef<HTMLSpanElement, TagButtonProps>(
|
|
33
|
+
// (
|
|
34
|
+
// {
|
|
35
|
+
// children,
|
|
36
|
+
// disabled = false,
|
|
37
|
+
// className,
|
|
38
|
+
// id,
|
|
39
|
+
// onClick,
|
|
40
|
+
// isClicked,
|
|
41
|
+
// defaultClick = false,
|
|
42
|
+
// ...rest
|
|
43
|
+
// },
|
|
44
|
+
// ref,
|
|
45
|
+
// ) => {
|
|
46
|
+
// const [clicked, setClicked] = useControlledState({
|
|
47
|
+
// prop: isClicked,
|
|
48
|
+
// defaultProp: defaultClick,
|
|
49
|
+
// onChange: onClick,
|
|
50
|
+
// })
|
|
51
|
+
// //
|
|
52
|
+
// return (
|
|
53
|
+
// <span
|
|
54
|
+
// role="button"
|
|
55
|
+
// ref={ref}
|
|
56
|
+
// id={id}
|
|
57
|
+
// className={cx(tagButton({ ...rest }), className)}
|
|
58
|
+
// aria-disabled={disabled}
|
|
59
|
+
// aria-pressed={clicked}
|
|
60
|
+
// {...(disabled && { "data-invalid": "true" })}
|
|
61
|
+
// data-testid={id}
|
|
62
|
+
// onClick={() => {
|
|
63
|
+
// if (disabled) {
|
|
64
|
+
// return
|
|
65
|
+
// }
|
|
66
|
+
// setClicked((prev) => !prev)
|
|
67
|
+
// }}
|
|
68
|
+
// >
|
|
69
|
+
// {children}
|
|
70
|
+
// </span>
|
|
71
|
+
// )
|
|
72
|
+
// },
|
|
73
|
+
// )
|
|
74
|
+
|
|
75
|
+
// TagButton.displayName = "TagButton"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { define } from "../../../dev"
|
|
2
|
+
export const tagButtonRecipe = define.recipe({
|
|
3
|
+
className: "tagButton",
|
|
4
|
+
base: {
|
|
5
|
+
textAlign: "center",
|
|
6
|
+
borderRadius: "rounded",
|
|
7
|
+
padding: "4px 10px",
|
|
8
|
+
fontWeight: "normal",
|
|
9
|
+
cursor: "pointer",
|
|
10
|
+
border: "1px solid black",
|
|
11
|
+
color: "text_primary",
|
|
12
|
+
_hover: { boxShadow: "0px 0px 3px", boxShadowColor: "grey_300" },
|
|
13
|
+
_invalid: {
|
|
14
|
+
cursor: "not-allowed",
|
|
15
|
+
},
|
|
16
|
+
_pressed: {
|
|
17
|
+
borderWidth: "2px",
|
|
18
|
+
borderColor: "blue_300",
|
|
19
|
+
},
|
|
20
|
+
maxWidth: "screen",
|
|
21
|
+
},
|
|
22
|
+
variants: {
|
|
23
|
+
size: {
|
|
24
|
+
small: {
|
|
25
|
+
fontSize: "sm",
|
|
26
|
+
},
|
|
27
|
+
large: {
|
|
28
|
+
fontSize: "lg",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
size: "small",
|
|
34
|
+
},
|
|
35
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// polymorphic.d.ts
|
|
2
|
+
import {
|
|
3
|
+
type ComponentPropsWithRef,
|
|
4
|
+
type ComponentPropsWithoutRef,
|
|
5
|
+
type ElementType,
|
|
6
|
+
} from "react"
|
|
7
|
+
|
|
8
|
+
type AsProp<C extends ElementType> = {
|
|
9
|
+
as?: C
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type AsRef<C extends ElementType> = ComponentPropsWithRef<C>["ref"]
|
|
13
|
+
|
|
14
|
+
export type AsComponentProps<
|
|
15
|
+
C extends ElementType,
|
|
16
|
+
Props = object,
|
|
17
|
+
> = AsProp<C> &
|
|
18
|
+
ComponentPropsWithoutRef<C> &
|
|
19
|
+
Props & {
|
|
20
|
+
ref?: AsRef<C>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type AsComponentPropsWithRef<
|
|
24
|
+
C extends ElementType,
|
|
25
|
+
Props = object,
|
|
26
|
+
> = Props & { ref?: AsRef<C> }
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";var w=Object.create;var p=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var P=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var O=(o,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of x(t))!j.call(o,a)&&a!==n&&p(o,a,{get:()=>t[a],enumerable:!(i=C(t,a))||i.enumerable});return o};var r=(o,t,n)=>(n=o!=null?w(P(o)):{},O(t||!o||!o.__esModule?p(n,"default",{value:o,enumerable:!0}):n,o));var g=r(require("yargs")),y=require("yargs/helpers");var m=r(require("path")),c=r(require("fs-extra")),l=require("pkg-dir"),e=r(require("@clack/prompts"));async function f(){let o=(0,l.packageDirectorySync)()||process.cwd(),t=m.default.join(o,"park-ui.json");try{return await c.default.readJSON(t)}catch{e.note("\uC124\uC815 \uD30C\uC77C\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");let n=await b();return await c.default.outputJSON(t,{...n},{spaces:2}),n}}var b=async()=>e.group({outputPath:()=>e.text({message:"\uCEF4\uD3EC\uB10C\uD2B8 \uC800\uC7A5 \uACBD\uB85C\uB97C \uC124\uC815\uD574\uC8FC\uC138\uC694",initialValue:"./src/components/ui",validate:o=>{if(!o)return"Please enter a path.";if(!o.startsWith("."))return"Please enter a relative path to the project root."}})},{onCancel:()=>{e.cancel("Operation cancelled."),process.exit(0)}});var h=r(require("@clack/prompts"));var s=r(require("path")),d=r(require("fs-extra")),S=s.default.join(__dirname,"./components/Button/index.tsx");async function u(o,t){try{let n=S,i=s.default.join(process.cwd(),t,`${o}.tsx`);await d.default.copy(n,i),console.log(`${o} copied to ${t}`)}catch(n){throw new Error(`Failed to copy component: ${n}`)}}var $=async()=>{await(0,g.default)((0,y.hideBin)(process.argv)).command("components add [components..]","Add components to your project",o=>o.positional("components",{describe:"List of components to add",type:"string",array:!0,default:[]}).option("all",{type:"boolean",description:"Add all components",default:!1}),async o=>{if(o.components.length===0&&!o.all){h.note("Error: You need to specify at least one component or use the --all flag"),console.log('Run "cli --help" for more information');return}let t=await f();await u(o.components[0],t.outputPath)}).demandCommand(1,"You need at least one command before moving on").strict().help().argv};$();
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import g from"yargs";import{hideBin as y}from"yargs/helpers";import m from"node:path";import r from"fs-extra";import{packageDirectorySync as l}from"pkg-dir";import*as t from"@clack/prompts";async function a(){let o=l()||process.cwd(),n=m.join(o,"park-ui.json");try{return await r.readJSON(n)}catch{t.note("\uC124\uC815 \uD30C\uC77C\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");let e=await f();return await r.outputJSON(n,{...e},{spaces:2}),e}}var f=async()=>t.group({outputPath:()=>t.text({message:"\uCEF4\uD3EC\uB10C\uD2B8 \uC800\uC7A5 \uACBD\uB85C\uB97C \uC124\uC815\uD574\uC8FC\uC138\uC694",initialValue:"./src/components/ui",validate:o=>{if(!o)return"Please enter a path.";if(!o.startsWith("."))return"Please enter a relative path to the project root."}})},{onCancel:()=>{t.cancel("Operation cancelled."),process.exit(0)}});import*as s from"@clack/prompts";import i from"node:path";import d from"fs-extra";var u=i.join(__dirname,"./components/Button/index.tsx");async function c(o,n){try{let e=u,p=i.join(process.cwd(),n,`${o}.tsx`);await d.copy(e,p),console.log(`${o} copied to ${n}`)}catch(e){throw new Error(`Failed to copy component: ${e}`)}}var h=async()=>{await g(y(process.argv)).command("components add [components..]","Add components to your project",o=>o.positional("components",{describe:"List of components to add",type:"string",array:!0,default:[]}).option("all",{type:"boolean",description:"Add all components",default:!1}),async o=>{if(o.components.length===0&&!o.all){s.note("Error: You need to specify at least one component or use the --all flag"),console.log('Run "cli --help" for more information');return}let n=await a();await c(o.components[0],n.outputPath)}).demandCommand(1,"You need at least one command before moving on").strict().help().argv};h();
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jongh/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": "./dist/index.js",
|
|
7
|
+
"keywords": [],
|
|
8
|
+
"author": "",
|
|
9
|
+
"license": "ISC",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@clack/prompts": "^0.7.0",
|
|
15
|
+
"@effect/platform": "^0.69.13",
|
|
16
|
+
"@effect/schema": "^0.75.5",
|
|
17
|
+
"effect": "^3.10.8",
|
|
18
|
+
"fs-extra": "^11.2.0",
|
|
19
|
+
"pkg-dir": "^8.0.0",
|
|
20
|
+
"yargs": "^17.7.2"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/fs-extra": "^11.0.4",
|
|
24
|
+
"@types/node": "^22.8.6",
|
|
25
|
+
"@types/yargs": "^17.0.33",
|
|
26
|
+
"ts-node": "^10.9.2",
|
|
27
|
+
"tsup": "^8.3.0",
|
|
28
|
+
"tsx": "^4.19.2"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"release": "pnpm publish -no-git-checks"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/park-ui.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/copy-components.ts
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import fs from "fs-extra"
|
|
4
|
+
|
|
5
|
+
// __dirname은 현재 실행되는 스크립트의 디렉토리 경로 (우리 패키지 내부)
|
|
6
|
+
const COMPONENTS_DIR = path.join(__dirname, "./components/Button/index.tsx")
|
|
7
|
+
|
|
8
|
+
export async function copyComponent(componentName: string, outputPath: string) {
|
|
9
|
+
try {
|
|
10
|
+
// 1. 우리 패키지 안의 컴포넌트 경로
|
|
11
|
+
const sourceFile = COMPONENTS_DIR
|
|
12
|
+
|
|
13
|
+
// 2. 유저 프로젝트의 목적지 경로 (CLI 실행 위치 기준)
|
|
14
|
+
const targetFile = path.join(
|
|
15
|
+
process.cwd(),
|
|
16
|
+
outputPath,
|
|
17
|
+
`${componentName}.tsx`,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
// 3. 복사 실행
|
|
21
|
+
await fs.copy(sourceFile, targetFile)
|
|
22
|
+
|
|
23
|
+
console.log(`${componentName} copied to ${outputPath}`)
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new Error(`Failed to copy component: ${error}`)
|
|
26
|
+
}
|
|
27
|
+
}
|