@kimdw-rtk/ui 0.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.
Files changed (158) hide show
  1. package/.babelrc +12 -0
  2. package/.turbo/turbo-check-types.log +2 -0
  3. package/.turbo/turbo-lint.log +12 -0
  4. package/.turbo/turbo-test.log +4084 -0
  5. package/.vscode/settings.json +4 -0
  6. package/eslint.config.mjs +4 -0
  7. package/jest.config.json +10 -0
  8. package/jest.setup.js +2 -0
  9. package/package.json +53 -0
  10. package/src/components/Accordion/Accordion.css.ts +29 -0
  11. package/src/components/Accordion/Accordion.spec.tsx +6 -0
  12. package/src/components/Accordion/Accordion.tsx +44 -0
  13. package/src/components/Accordion/AccordionContent.css.ts +29 -0
  14. package/src/components/Accordion/AccordionContent.tsx +87 -0
  15. package/src/components/Accordion/AccordionContext.ts +9 -0
  16. package/src/components/Accordion/AccordionTrigger.css.ts +46 -0
  17. package/src/components/Accordion/AccordionTrigger.tsx +41 -0
  18. package/src/components/Accordion/index.ts +3 -0
  19. package/src/components/Alert/index.tsx +25 -0
  20. package/src/components/Box/Box.css.ts +18 -0
  21. package/src/components/Box/Box.spec.tsx +6 -0
  22. package/src/components/Box/index.tsx +41 -0
  23. package/src/components/Button/Button.css.ts +241 -0
  24. package/src/components/Button/Button.spec.tsx +30 -0
  25. package/src/components/Button/index.tsx +60 -0
  26. package/src/components/Card/Card.css.ts +93 -0
  27. package/src/components/Card/Card.spec.tsx +24 -0
  28. package/src/components/Card/Card.tsx +41 -0
  29. package/src/components/Card/CardContent.css.ts +8 -0
  30. package/src/components/Card/CardContent.tsx +23 -0
  31. package/src/components/Card/CardInteraction.css.ts +11 -0
  32. package/src/components/Card/CardInteraction.tsx +36 -0
  33. package/src/components/Card/CardThumbnail.css.ts +6 -0
  34. package/src/components/Card/CardThumbnail.tsx +23 -0
  35. package/src/components/Card/index.ts +4 -0
  36. package/src/components/Chip/Chip.css.ts +75 -0
  37. package/src/components/Chip/Chip.spec.tsx +6 -0
  38. package/src/components/Chip/Chip.tsx +37 -0
  39. package/src/components/Chip/index.ts +1 -0
  40. package/src/components/Confirm/index.tsx +44 -0
  41. package/src/components/Dialog/Dialog.css.ts +25 -0
  42. package/src/components/Dialog/Dialog.spec.tsx +26 -0
  43. package/src/components/Dialog/Dialog.tsx +30 -0
  44. package/src/components/Dialog/DialogContent.css.ts +16 -0
  45. package/src/components/Dialog/DialogContent.tsx +26 -0
  46. package/src/components/Dialog/DialogFooter.css.ts +20 -0
  47. package/src/components/Dialog/DialogFooter.tsx +26 -0
  48. package/src/components/Dialog/DialogHeader.css.ts +31 -0
  49. package/src/components/Dialog/DialogHeader.tsx +37 -0
  50. package/src/components/Dialog/index.ts +4 -0
  51. package/src/components/Navigation/Navigation.spec.tsx +19 -0
  52. package/src/components/Navigation/NavigationAside.css.ts +7 -0
  53. package/src/components/Navigation/NavigationAside.tsx +23 -0
  54. package/src/components/Navigation/NavigationBar.css.ts +42 -0
  55. package/src/components/Navigation/NavigationBar.tsx +25 -0
  56. package/src/components/Navigation/NavigationContainer.css.ts +11 -0
  57. package/src/components/Navigation/NavigationContainer.tsx +26 -0
  58. package/src/components/Navigation/NavigationDrawer.css.ts +61 -0
  59. package/src/components/Navigation/NavigationDrawer.tsx +67 -0
  60. package/src/components/Navigation/NavigationItem.css.ts +43 -0
  61. package/src/components/Navigation/NavigationItem.tsx +24 -0
  62. package/src/components/Navigation/NavigationLogo.css.ts +5 -0
  63. package/src/components/Navigation/NavigationLogo.tsx +28 -0
  64. package/src/components/Navigation/NavigationMenu.css.ts +23 -0
  65. package/src/components/Navigation/NavigationMenu.tsx +25 -0
  66. package/src/components/Navigation/index.ts +7 -0
  67. package/src/components/Range/Range.css.ts +132 -0
  68. package/src/components/Range/Range.spec.tsx +6 -0
  69. package/src/components/Range/Range.tsx +90 -0
  70. package/src/components/Range/index.ts +1 -0
  71. package/src/components/ScrollArea/ScrollArea.css.ts +40 -0
  72. package/src/components/ScrollArea/ScrollArea.spec.tsx +6 -0
  73. package/src/components/ScrollArea/ScrollArea.tsx +68 -0
  74. package/src/components/ScrollArea/index.ts +1 -0
  75. package/src/components/Select/Select.css.ts +22 -0
  76. package/src/components/Select/Select.spec.tsx +65 -0
  77. package/src/components/Select/Select.tsx +111 -0
  78. package/src/components/Select/SelectContext.ts +59 -0
  79. package/src/components/Select/SelectOption.css.ts +14 -0
  80. package/src/components/Select/SelectOption.tsx +40 -0
  81. package/src/components/Select/SelectOptionList.css.ts +68 -0
  82. package/src/components/Select/SelectOptionList.tsx +59 -0
  83. package/src/components/Select/SelectTrigger.css.ts +73 -0
  84. package/src/components/Select/SelectTrigger.tsx +49 -0
  85. package/src/components/Select/index.tsx +2 -0
  86. package/src/components/Skeleton/Skeleton.css.ts +26 -0
  87. package/src/components/Skeleton/Skeleton.spec.tsx +6 -0
  88. package/src/components/Skeleton/index.tsx +27 -0
  89. package/src/components/Table/Table.css.ts +10 -0
  90. package/src/components/Table/Table.spec.tsx +12 -0
  91. package/src/components/Table/Table.tsx +27 -0
  92. package/src/components/Table/TableBody.tsx +14 -0
  93. package/src/components/Table/TableCell.css.ts +43 -0
  94. package/src/components/Table/TableCell.tsx +30 -0
  95. package/src/components/Table/TableHead.css.ts +10 -0
  96. package/src/components/Table/TableHead.tsx +30 -0
  97. package/src/components/Table/TableHeader.tsx +14 -0
  98. package/src/components/Table/TableRow.css.ts +3 -0
  99. package/src/components/Table/TableRow.tsx +24 -0
  100. package/src/components/Table/index.ts +6 -0
  101. package/src/components/Tabs/Tabs.spec.tsx +46 -0
  102. package/src/components/Tabs/Tabs.tsx +34 -0
  103. package/src/components/Tabs/TabsContent.tsx +32 -0
  104. package/src/components/Tabs/TabsList.css.ts +11 -0
  105. package/src/components/Tabs/TabsList.tsx +25 -0
  106. package/src/components/Tabs/TabsProvider.tsx +17 -0
  107. package/src/components/Tabs/TabsTrigger.css.ts +38 -0
  108. package/src/components/Tabs/TabsTrigger.tsx +43 -0
  109. package/src/components/Tabs/index.ts +4 -0
  110. package/src/components/TextField/TextField.css.ts +81 -0
  111. package/src/components/TextField/TextField.spec.tsx +6 -0
  112. package/src/components/TextField/index.tsx +38 -0
  113. package/src/components/Toast/Toast.css.ts +79 -0
  114. package/src/components/Toast/Toast.spec.tsx +6 -0
  115. package/src/components/Toast/index.tsx +48 -0
  116. package/src/components/Typography/Typography.css.ts +17 -0
  117. package/src/components/Typography/Typography.spec.tsx +35 -0
  118. package/src/components/Typography/index.tsx +57 -0
  119. package/src/components/index.ts +18 -0
  120. package/src/contexts/UIProvider.tsx +30 -0
  121. package/src/contexts/index.ts +1 -0
  122. package/src/hooks/index.ts +5 -0
  123. package/src/hooks/useDialog/index.tsx +51 -0
  124. package/src/hooks/useDialog/useDialog.spec.tsx +80 -0
  125. package/src/hooks/useMouseScroll/index.ts +63 -0
  126. package/src/hooks/usePointerSlider/index.ts +79 -0
  127. package/src/hooks/useRipple/index.tsx +152 -0
  128. package/src/hooks/useRipple/ripple.css.ts +40 -0
  129. package/src/hooks/useToast/ToastContainer.css.ts +12 -0
  130. package/src/hooks/useToast/ToastContainer.tsx +11 -0
  131. package/src/hooks/useToast/ToastProvider.tsx +131 -0
  132. package/src/hooks/useToast/index.ts +15 -0
  133. package/src/index.ts +8 -0
  134. package/src/styles/globalStyle.css.ts +36 -0
  135. package/src/styles/index.ts +4 -0
  136. package/src/styles/layers.css.ts +4 -0
  137. package/src/styles/overlay.css.ts +40 -0
  138. package/src/styles/sprinkles.css.ts +149 -0
  139. package/src/styles/sx.ts +13 -0
  140. package/src/tests/uiTest.tsx +54 -0
  141. package/src/themes/darkTheme.css.ts +30 -0
  142. package/src/themes/index.ts +3 -0
  143. package/src/themes/lightTheme.css.ts +30 -0
  144. package/src/themes/theme.css.ts +32 -0
  145. package/src/tokens/index.ts +5 -0
  146. package/src/tokens/scale/color.ts +604 -0
  147. package/src/tokens/semantic/breakpoint.ts +6 -0
  148. package/src/tokens/semantic/color.ts +10 -0
  149. package/src/tokens/semantic/spacing.ts +9 -0
  150. package/src/tokens/semantic/typography.ts +32 -0
  151. package/src/types/index.ts +1 -0
  152. package/src/types/ui.types.ts +26 -0
  153. package/src/utils/index.ts +1 -0
  154. package/src/utils/sprinklesUtils.ts +28 -0
  155. package/src/utils/styleUtils.css.ts +109 -0
  156. package/tsconfig.json +11 -0
  157. package/turbo/generators/config.ts +30 -0
  158. package/turbo/generators/templates/component.hbs +8 -0
@@ -0,0 +1,46 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+
3
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '.';
4
+ import { uiTest } from '../../tests/uiTest';
5
+
6
+ describe('Tabs 컴포넌트', () => {
7
+ uiTest(Tabs, 'Tabs');
8
+ uiTest(TabsList, 'TabsList');
9
+
10
+ it('TabsTrigger를 클릭하면 해당하는 value의 Content를 보여준다.', () => {
11
+ render(
12
+ <Tabs>
13
+ <TabsList>
14
+ <TabsTrigger value={1}>Trigger1</TabsTrigger>
15
+ <TabsTrigger value={2}>Trigger2</TabsTrigger>
16
+ </TabsList>
17
+ <TabsContent value={1}>Content1</TabsContent>
18
+ <TabsContent value={2}>Content2</TabsContent>
19
+ </Tabs>,
20
+ );
21
+
22
+ fireEvent.click(screen.getByText('Trigger1'));
23
+ expect(screen.queryByText('Content1')).toBeInTheDocument();
24
+ expect(screen.queryByText('Content2')).not.toBeInTheDocument();
25
+
26
+ fireEvent.click(screen.getByText('Trigger2'));
27
+ expect(screen.queryByText('Content1')).not.toBeInTheDocument();
28
+ expect(screen.queryByText('Content2')).toBeInTheDocument();
29
+ });
30
+
31
+ it('defaultValue에 해당하는 Content를 기본적으로 보여준다.', () => {
32
+ render(
33
+ <Tabs defaultValue={2}>
34
+ <TabsList>
35
+ <TabsTrigger value={1}>Trigger1</TabsTrigger>
36
+ <TabsTrigger value={2}>Trigger2</TabsTrigger>
37
+ </TabsList>
38
+ <TabsContent value={1}>Content1</TabsContent>
39
+ <TabsContent value={2}>Content2</TabsContent>
40
+ </Tabs>,
41
+ );
42
+
43
+ expect(screen.queryByText('Content1')).not.toBeInTheDocument();
44
+ expect(screen.queryByText('Content2')).toBeInTheDocument();
45
+ });
46
+ });
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, useReducer } from 'react';
4
+
5
+ import clsx from 'clsx';
6
+
7
+ import { sx } from '#styles';
8
+ import type { UIComponent } from '#types';
9
+
10
+ import { TabsContext, tabsReducer } from './TabsProvider';
11
+
12
+ interface TabsProps extends UIComponent<'div'> {
13
+ defaultValue?: number | string;
14
+ }
15
+
16
+ export const Tabs = forwardRef<HTMLDivElement, TabsProps>(
17
+ ({ children, defaultValue, className, sx: propSx, ...props }, ref) => {
18
+ const [value, setValue] = useReducer(tabsReducer, defaultValue);
19
+
20
+ return (
21
+ <TabsContext.Provider value={{ value, setValue }}>
22
+ <div
23
+ ref={ref}
24
+ className={clsx(className, sx(propSx))}
25
+ style={{ width: '100%' }}
26
+ {...props}
27
+ >
28
+ {children}
29
+ </div>
30
+ </TabsContext.Provider>
31
+ );
32
+ },
33
+ );
34
+ Tabs.displayName = 'Tabs';
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, useContext, type ReactNode } from 'react';
4
+
5
+ import clsx from 'clsx';
6
+
7
+ import { sx } from '#styles';
8
+ import type { UIComponent } from '#types';
9
+
10
+ import { TabsContext } from './TabsProvider';
11
+
12
+ interface TabsContentProps extends UIComponent<'div'> {
13
+ children: ReactNode;
14
+ value: string | number;
15
+ }
16
+
17
+ export const TabsContent = forwardRef<HTMLDivElement, TabsContentProps>(
18
+ ({ value, className, sx: propSx, ...props }, ref) => {
19
+ const tabsContext = useContext(TabsContext);
20
+
21
+ if (tabsContext === undefined) {
22
+ throw new Error('TabsContext must be used within a Tabs.');
23
+ }
24
+
25
+ if (tabsContext.value !== value) {
26
+ return null;
27
+ }
28
+
29
+ return <div ref={ref} className={clsx(className, sx(propSx))} {...props} />;
30
+ },
31
+ );
32
+ TabsContent.displayName = 'TabsContent';
@@ -0,0 +1,11 @@
1
+ import { styleWithLayer } from '#styleUtils';
2
+ import { theme } from '#themes';
3
+
4
+ export const container = styleWithLayer({
5
+ display: 'flex',
6
+
7
+ height: '2.5em',
8
+
9
+ boxShadow: `inset 0 -0.125em 0 rgb(${theme.color.muted})`,
10
+ backgroundColor: `rgb(${theme.color.background})`,
11
+ });
@@ -0,0 +1,25 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import clsx from 'clsx';
4
+
5
+ import { sx } from '#styles';
6
+ import type { UIComponent } from '#types';
7
+
8
+ import * as s from './TabsList.css';
9
+
10
+ type TabsListProps = UIComponent<'div'>;
11
+
12
+ export const TabsList = forwardRef<HTMLDivElement, TabsListProps>(
13
+ ({ children, className, sx: propSx, ...props }, ref) => {
14
+ return (
15
+ <div
16
+ ref={ref}
17
+ className={clsx(className, s.container, sx(propSx))}
18
+ {...props}
19
+ >
20
+ {children}
21
+ </div>
22
+ );
23
+ },
24
+ );
25
+ TabsList.displayName = 'TabsList';
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+
3
+ import { createContext, type Dispatch } from 'react';
4
+
5
+ interface TabsContext {
6
+ value: number | string | undefined;
7
+ setValue: Dispatch<number | string>;
8
+ }
9
+
10
+ export const TabsContext = createContext<TabsContext | undefined>(undefined);
11
+
12
+ export const tabsReducer = (
13
+ _: TabsContext['value'],
14
+ value: TabsContext['value'],
15
+ ) => {
16
+ return value;
17
+ };
@@ -0,0 +1,38 @@
1
+ import { recipeWithLayer } from '#styleUtils';
2
+ import { theme } from '#themes';
3
+
4
+ export const container = recipeWithLayer({
5
+ base: {
6
+ overflow: 'hidden',
7
+
8
+ height: '100%',
9
+ padding: '0 0.75em',
10
+ border: '0',
11
+ borderBottom: `0.125rem solid`,
12
+
13
+ backgroundColor: 'transparent',
14
+
15
+ fontSize: '1em',
16
+ textOverflow: 'ellipsis',
17
+ whiteSpace: 'nowrap',
18
+
19
+ transition: 'border-bottom-color 0.15s ease, color 0.15s ease',
20
+
21
+ cursor: 'pointer',
22
+ },
23
+
24
+ variants: {
25
+ isSelected: {
26
+ true: {
27
+ borderBottomColor: `rgb(${theme.color.foreground})`,
28
+
29
+ color: `rgb(${theme.color.foreground})`,
30
+ },
31
+ false: {
32
+ borderBottomColor: 'transparent',
33
+
34
+ color: `rgb(${theme.color['muted-foreground']})`,
35
+ },
36
+ },
37
+ },
38
+ });
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+
3
+ import { forwardRef, useContext } from 'react';
4
+
5
+ import clsx from 'clsx';
6
+
7
+ import { sx } from '#styles';
8
+ import type { UIComponent } from '#types';
9
+
10
+ import { TabsContext } from './TabsProvider';
11
+ import * as s from './TabsTrigger.css';
12
+
13
+ interface TabsTriggerProps extends UIComponent<'button'> {
14
+ value: number | string;
15
+ }
16
+
17
+ export const TabsTrigger = forwardRef<HTMLButtonElement, TabsTriggerProps>(
18
+ ({ children, value, className, sx: propSx, ...props }, ref) => {
19
+ const tabsContext = useContext(TabsContext);
20
+
21
+ if (tabsContext === undefined) {
22
+ throw new Error('TabsTrigger must be used within a Tabs.');
23
+ }
24
+
25
+ const isSelected = tabsContext.value === value;
26
+
27
+ const handleClick = () => {
28
+ tabsContext.setValue(value);
29
+ };
30
+
31
+ return (
32
+ <button
33
+ ref={ref}
34
+ className={clsx(className, s.container({ isSelected }), sx(propSx))}
35
+ onClick={handleClick}
36
+ {...props}
37
+ >
38
+ {children}
39
+ </button>
40
+ );
41
+ },
42
+ );
43
+ TabsTrigger.displayName = 'TabsTrigger';
@@ -0,0 +1,4 @@
1
+ export * from './Tabs';
2
+ export * from './TabsContent';
3
+ export * from './TabsList';
4
+ export * from './TabsTrigger';
@@ -0,0 +1,81 @@
1
+ import { createVar } from '@vanilla-extract/css';
2
+
3
+ import { recipeWithLayer, styleWithLayer } from '#styleUtils';
4
+ import { theme } from '#themes';
5
+ import { semanticColor } from '#tokens';
6
+
7
+ import { SCALE_COLOR, type ScaleColor } from '../../tokens/scale/color';
8
+
9
+ const backgroundVar = createVar();
10
+
11
+ const semanticColors = semanticColor.reduce(
12
+ (prev, color) => ({
13
+ ...prev,
14
+ [color]: styleWithLayer({
15
+ vars: {
16
+ [backgroundVar]: theme.color[color],
17
+ },
18
+ }),
19
+ }),
20
+ {} as Record<(typeof semanticColor)[number], string>,
21
+ );
22
+
23
+ const scaleColors = SCALE_COLOR.reduce(
24
+ (prev, value) => ({
25
+ ...prev,
26
+ [value]: styleWithLayer({
27
+ vars: {
28
+ [backgroundVar]: theme.color[value][500],
29
+ },
30
+ }),
31
+ }),
32
+ {} as Record<ScaleColor, string>,
33
+ );
34
+
35
+ export const textField = recipeWithLayer({
36
+ base: {
37
+ padding: '0.75em 0.5em',
38
+ border: `1px solid`,
39
+ borderColor: ` rgb(${theme.color.border})`,
40
+
41
+ backgroundColor: `rgb(${theme.color.background})`,
42
+
43
+ color: `rgb(${theme.color.foreground})`,
44
+
45
+ transition: 'border-color 0.15s ease, color 0.15s ease',
46
+
47
+ outline: 'none',
48
+
49
+ ':focus': {
50
+ borderColor: `rgb(${backgroundVar})`,
51
+ },
52
+
53
+ ':disabled': {
54
+ color: `rgb(${theme.color['muted-foreground']})`,
55
+ },
56
+ },
57
+ variants: {
58
+ color: {
59
+ ...semanticColors,
60
+ ...scaleColors,
61
+ },
62
+
63
+ size: {
64
+ sm: {
65
+ borderRadius: theme.borderRadius,
66
+
67
+ fontSize: '0.875em',
68
+ },
69
+ md: {
70
+ borderRadius: theme.borderRadius,
71
+
72
+ fontSize: '1em',
73
+ },
74
+ lg: {
75
+ borderRadius: theme.borderRadius,
76
+
77
+ fontSize: '1.125em',
78
+ },
79
+ },
80
+ },
81
+ });
@@ -0,0 +1,6 @@
1
+ import { TextField } from '.';
2
+ import { uiTest } from '../../tests/uiTest';
3
+
4
+ describe('TextField 컴포넌트', () => {
5
+ uiTest(TextField, 'TextField');
6
+ });
@@ -0,0 +1,38 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import { clsx } from 'clsx';
4
+
5
+ import { sx } from '#styles';
6
+ import type { UIComponent } from '#types';
7
+
8
+ import * as s from './TextField.css';
9
+
10
+ interface TextFieldProps extends UIComponent<'input', typeof s.textField> {
11
+ type?: React.HTMLInputTypeAttribute;
12
+ }
13
+
14
+ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
15
+ (
16
+ {
17
+ className,
18
+ type = 'text',
19
+ size = 'md',
20
+ color = 'primary',
21
+ sx: propSx,
22
+ ...props
23
+ },
24
+ ref,
25
+ ) => {
26
+ return (
27
+ <input
28
+ ref={ref}
29
+ type={type}
30
+ className={clsx(className, s.textField({ size, color }), sx(propSx))}
31
+ {...props}
32
+ />
33
+ );
34
+ },
35
+ );
36
+ TextField.displayName = 'TextField';
37
+
38
+ export { s as textFieldCss };
@@ -0,0 +1,79 @@
1
+ import { createVar, keyframes } from '@vanilla-extract/css';
2
+
3
+ import { recipeWithLayer, styleWithLayer } from '#styleUtils';
4
+ import { theme } from '#themes';
5
+ import { semanticColor } from '#tokens';
6
+
7
+ const colorVar = createVar();
8
+ const foregroundVar = createVar();
9
+
10
+ const semanticColors = semanticColor.reduce(
11
+ (prev, color) => ({
12
+ ...prev,
13
+ [color]: styleWithLayer({
14
+ vars: {
15
+ [colorVar]: theme.color[color],
16
+ [foregroundVar]: theme.color[`${color}-foreground`],
17
+ },
18
+ }),
19
+ }),
20
+ {} as Record<(typeof semanticColor)[number], string>,
21
+ );
22
+
23
+ const fill = keyframes({
24
+ '0%': {
25
+ transform: 'scaleX(0)',
26
+ },
27
+ '100%': {
28
+ transform: 'scaleX(1)',
29
+ },
30
+ });
31
+
32
+ export const progress = recipeWithLayer({
33
+ base: {
34
+ position: 'absolute',
35
+ inset: '0',
36
+
37
+ width: '100%',
38
+
39
+ backgroundColor: `rgba(${foregroundVar}, 0.25)`,
40
+
41
+ transformOrigin: '0 0',
42
+ },
43
+
44
+ variants: {
45
+ animation: {
46
+ true: {
47
+ animationName: fill,
48
+ animationTimingFunction: 'linear',
49
+ animationIterationCount: '1',
50
+ animationFillMode: 'forwards',
51
+ },
52
+ false: {
53
+ display: 'none',
54
+ },
55
+ },
56
+ },
57
+ });
58
+
59
+ export const toast = recipeWithLayer({
60
+ base: {
61
+ position: 'relative',
62
+ overflow: 'hidden',
63
+
64
+ padding: '0.875em',
65
+
66
+ backgroundColor: `rgb(${colorVar})`,
67
+
68
+ color: `rgb(${foregroundVar})`,
69
+ whiteSpace: 'nowrap',
70
+ wordBreak: 'break-all',
71
+
72
+ cursor: 'pointer',
73
+ },
74
+ variants: {
75
+ color: {
76
+ ...semanticColors,
77
+ },
78
+ },
79
+ });
@@ -0,0 +1,6 @@
1
+ import { Toast } from '.';
2
+ import { uiTest } from '../../tests/uiTest';
3
+
4
+ describe('Toast 컴포넌트', () => {
5
+ uiTest(Toast, 'Toast');
6
+ });
@@ -0,0 +1,48 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import clsx from 'clsx';
4
+
5
+ import { Box } from '#components';
6
+ import { sx } from '#styles';
7
+ import type { UIComponent } from '#types';
8
+
9
+ import * as s from './Toast.css';
10
+
11
+ interface ToastProps extends UIComponent<'div', typeof s.toast> {
12
+ duration?: number;
13
+ }
14
+
15
+ export const Toast = forwardRef<HTMLDivElement, ToastProps>(
16
+ (
17
+ {
18
+ children,
19
+ color = 'accent',
20
+ className,
21
+ duration = 0,
22
+ sx: propSx,
23
+ ...props
24
+ },
25
+ ref,
26
+ ) => {
27
+ return (
28
+ <Box
29
+ ref={ref}
30
+ boxShadow="accent-sm"
31
+ rounded
32
+ className={clsx(s.toast({ color }), className, sx(propSx))}
33
+ {...props}
34
+ >
35
+ <div
36
+ className={s.progress({ animation: duration !== 0 })}
37
+ style={{
38
+ animationDuration: duration > 0 ? `${duration}ms` : undefined,
39
+ }}
40
+ ></div>
41
+ <span style={{ position: 'relative' }}>{children}</span>
42
+ </Box>
43
+ );
44
+ },
45
+ );
46
+ Toast.displayName = 'Toast';
47
+
48
+ export { s as toastCss };
@@ -0,0 +1,17 @@
1
+ import { recipeWithLayer } from '#styleUtils';
2
+
3
+ export const typography = recipeWithLayer({
4
+ base: {
5
+ margin: '0',
6
+ },
7
+ variants: {
8
+ isEllipsis: {
9
+ true: {
10
+ overflow: 'hidden',
11
+
12
+ whiteSpace: 'nowrap',
13
+ textOverflow: 'ellipsis',
14
+ },
15
+ },
16
+ },
17
+ });
@@ -0,0 +1,35 @@
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { cleanup, render, screen } from '@testing-library/react';
4
+
5
+ import { Typography } from '#components';
6
+
7
+ import { uiTest } from '../../tests/uiTest';
8
+
9
+ describe('Typogrphy 컴포넌트', () => {
10
+ uiTest(Typography, 'Typography');
11
+
12
+ test('기본적으로 p태그로 출력한다.', () => {
13
+ render(<Typography>test</Typography>);
14
+ expect(screen.getByText('test').tagName.toLowerCase()).toBe('p');
15
+ });
16
+
17
+ test('as에 전달된 알맞은 태그로 출력한다.', () => {
18
+ const tags: ComponentProps<typeof Typography>['as'][] = [
19
+ 'p',
20
+ 'h1',
21
+ 'h2',
22
+ 'h3',
23
+ 'h4',
24
+ 'h5',
25
+ 'h6',
26
+ 'span',
27
+ ];
28
+
29
+ for (const tag of tags) {
30
+ cleanup();
31
+ render(<Typography as={tag}>test</Typography>);
32
+ expect(screen.getByText('test').tagName.toLowerCase()).toBe(tag);
33
+ }
34
+ });
35
+ });
@@ -0,0 +1,57 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import clsx from 'clsx';
4
+
5
+ import { sprinkles, type SprinklesProps } from '#styles';
6
+ import { type UIComponent } from '#types';
7
+
8
+ import * as s from './Typography.css';
9
+
10
+ type TypographyElement = 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'span';
11
+
12
+ type TypographyProps<T extends TypographyElement = TypographyElement> = Omit<
13
+ UIComponent<T, typeof s.typography> & {
14
+ as?: T;
15
+ color?: SprinklesProps['color'];
16
+ fontSize?: SprinklesProps['fontSize'];
17
+ fontWeight?: SprinklesProps['fontWeight'];
18
+ lineHeight?: SprinklesProps['lineHeight'];
19
+ },
20
+ 'ref'
21
+ >;
22
+
23
+ export const Typography = forwardRef<HTMLParagraphElement, TypographyProps>(
24
+ (
25
+ {
26
+ children,
27
+ as: Component = 'p',
28
+ className,
29
+ color = 'foreground',
30
+ fontSize = 'md',
31
+ fontWeight = 'normal',
32
+ lineHeight,
33
+ isEllipsis = false,
34
+ sx,
35
+ ...props
36
+ },
37
+ ref,
38
+ ) => {
39
+ return (
40
+ <Component
41
+ ref={ref}
42
+ className={clsx(
43
+ s.typography({ isEllipsis }),
44
+ sprinkles({ color, fontSize, fontWeight, lineHeight }),
45
+ sx && sprinkles(sx),
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ </Component>
52
+ );
53
+ },
54
+ );
55
+ Typography.displayName = 'Typography';
56
+
57
+ export { s as typographyCss };
@@ -0,0 +1,18 @@
1
+ export * from './Accordion';
2
+ export * from './Alert';
3
+ export * from './Box';
4
+ export * from './Button';
5
+ export * from './Card';
6
+ export * from './Chip';
7
+ export * from './Confirm';
8
+ export * from './Dialog';
9
+ export * from './Navigation';
10
+ export * from './Range';
11
+ export * from './ScrollArea';
12
+ export * from './Select';
13
+ export * from './Skeleton';
14
+ export * from './Table';
15
+ export * from './Tabs';
16
+ export * from './TextField';
17
+ export * from './Toast';
18
+ export * from './Typography';
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+
5
+ import { OverlayProvider, type OverlayOption } from '@kimdw-rtk/utils';
6
+
7
+ import { ToastProvider } from '#hooks';
8
+
9
+ import * as overlayStyle from '../styles/overlay.css';
10
+
11
+ interface UIProviderProps {
12
+ children: ReactNode;
13
+ overlayUnmountOn?: OverlayOption['unmountOn'];
14
+ }
15
+
16
+ export const UIProvider = ({
17
+ children,
18
+ overlayUnmountOn = 'transitionEnd',
19
+ }: UIProviderProps) => {
20
+ return (
21
+ <>
22
+ <OverlayProvider
23
+ className={{ ...overlayStyle }}
24
+ unmountOn={overlayUnmountOn}
25
+ >
26
+ <ToastProvider>{children}</ToastProvider>
27
+ </OverlayProvider>
28
+ </>
29
+ );
30
+ };
@@ -0,0 +1 @@
1
+ export * from './UIProvider';