@uniai-fe/uds-primitives 0.0.1

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 (217) hide show
  1. package/README.md +63 -0
  2. package/package.json +85 -0
  3. package/src/components/alternate/hooks/index.ts +4 -0
  4. package/src/components/alternate/img/.gitkeep +0 -0
  5. package/src/components/alternate/index.scss +1 -0
  6. package/src/components/alternate/index.tsx +4 -0
  7. package/src/components/alternate/markup/index.tsx +4 -0
  8. package/src/components/alternate/styles/index.scss +3 -0
  9. package/src/components/alternate/types/index.ts +4 -0
  10. package/src/components/alternate/utils/index.ts +4 -0
  11. package/src/components/badge/hooks/index.ts +4 -0
  12. package/src/components/badge/img/.gitkeep +0 -0
  13. package/src/components/badge/index.scss +1 -0
  14. package/src/components/badge/index.tsx +6 -0
  15. package/src/components/badge/markup/Badge.tsx +51 -0
  16. package/src/components/badge/markup/index.tsx +1 -0
  17. package/src/components/badge/styles/index.scss +189 -0
  18. package/src/components/badge/types/index.ts +55 -0
  19. package/src/components/badge/utils/index.ts +21 -0
  20. package/src/components/button/hooks/index.ts +4 -0
  21. package/src/components/button/img/.gitkeep +0 -0
  22. package/src/components/button/index.scss +1 -0
  23. package/src/components/button/index.tsx +6 -0
  24. package/src/components/button/markup/Button.tsx +175 -0
  25. package/src/components/button/markup/index.tsx +1 -0
  26. package/src/components/button/styles/index.scss +847 -0
  27. package/src/components/button/types/index.ts +79 -0
  28. package/src/components/button/utils/index.ts +58 -0
  29. package/src/components/calendar/hooks/index.ts +4 -0
  30. package/src/components/calendar/img/.gitkeep +0 -0
  31. package/src/components/calendar/index.scss +1 -0
  32. package/src/components/calendar/index.tsx +4 -0
  33. package/src/components/calendar/markup/index.tsx +4 -0
  34. package/src/components/calendar/styles/index.scss +3 -0
  35. package/src/components/calendar/types/index.ts +4 -0
  36. package/src/components/calendar/utils/index.ts +4 -0
  37. package/src/components/checkbox/hooks/index.ts +4 -0
  38. package/src/components/checkbox/img/.gitkeep +0 -0
  39. package/src/components/checkbox/img/check-large.svg +3 -0
  40. package/src/components/checkbox/img/check-medium.svg +3 -0
  41. package/src/components/checkbox/img/check.svg +3 -0
  42. package/src/components/checkbox/index.scss +1 -0
  43. package/src/components/checkbox/index.tsx +4 -0
  44. package/src/components/checkbox/markup/Checkbox.tsx +127 -0
  45. package/src/components/checkbox/markup/index.ts +1 -0
  46. package/src/components/checkbox/styles/index.scss +164 -0
  47. package/src/components/checkbox/types/checkbox.ts +21 -0
  48. package/src/components/checkbox/types/index.ts +1 -0
  49. package/src/components/chip/hooks/index.ts +4 -0
  50. package/src/components/chip/img/.gitkeep +0 -0
  51. package/src/components/chip/img/remove.svg +3 -0
  52. package/src/components/chip/index.scss +1 -0
  53. package/src/components/chip/index.tsx +6 -0
  54. package/src/components/chip/markup/Chip.tsx +103 -0
  55. package/src/components/chip/markup/index.tsx +1 -0
  56. package/src/components/chip/styles/index.scss +140 -0
  57. package/src/components/chip/types/index.ts +52 -0
  58. package/src/components/chip/utils/index.ts +36 -0
  59. package/src/components/dialog/hooks/index.ts +4 -0
  60. package/src/components/dialog/img/.gitkeep +0 -0
  61. package/src/components/dialog/index.scss +1 -0
  62. package/src/components/dialog/index.tsx +3 -0
  63. package/src/components/dialog/markup/confirm-dialog.tsx +316 -0
  64. package/src/components/dialog/markup/index.tsx +4 -0
  65. package/src/components/dialog/markup/notice-dialog.tsx +191 -0
  66. package/src/components/dialog/styles/base.scss +153 -0
  67. package/src/components/dialog/styles/confirm.scss +58 -0
  68. package/src/components/dialog/styles/index.scss +3 -0
  69. package/src/components/dialog/styles/notice.scss +65 -0
  70. package/src/components/dialog/types/index.ts +70 -0
  71. package/src/components/dialog/utils/index.ts +4 -0
  72. package/src/components/drawer/hooks/index.ts +113 -0
  73. package/src/components/drawer/img/.gitkeep +0 -0
  74. package/src/components/drawer/img/close.svg +3 -0
  75. package/src/components/drawer/index.scss +1 -0
  76. package/src/components/drawer/index.tsx +3 -0
  77. package/src/components/drawer/markup/drawer.tsx +421 -0
  78. package/src/components/drawer/markup/index.tsx +3 -0
  79. package/src/components/drawer/styles/index.scss +232 -0
  80. package/src/components/drawer/types/index.ts +51 -0
  81. package/src/components/drawer/utils/context.ts +15 -0
  82. package/src/components/drawer/utils/index.tsx +77 -0
  83. package/src/components/dropdown/hooks/index.ts +4 -0
  84. package/src/components/dropdown/img/.gitkeep +0 -0
  85. package/src/components/dropdown/index.scss +1 -0
  86. package/src/components/dropdown/index.tsx +4 -0
  87. package/src/components/dropdown/markup/index.tsx +4 -0
  88. package/src/components/dropdown/styles/index.scss +3 -0
  89. package/src/components/dropdown/types/index.ts +4 -0
  90. package/src/components/dropdown/utils/index.ts +4 -0
  91. package/src/components/input/hooks/index.ts +4 -0
  92. package/src/components/input/img/.gitkeep +0 -0
  93. package/src/components/input/img/check-correct.svg +3 -0
  94. package/src/components/input/img/check-default.svg +3 -0
  95. package/src/components/input/img/check-incorrect.svg +3 -0
  96. package/src/components/input/img/error.svg +5 -0
  97. package/src/components/input/img/hide-off.svg +4 -0
  98. package/src/components/input/img/hide-on.svg +6 -0
  99. package/src/components/input/img/reset.svg +3 -0
  100. package/src/components/input/img/search.svg +4 -0
  101. package/src/components/input/img/success.svg +3 -0
  102. package/src/components/input/index.scss +1 -0
  103. package/src/components/input/index.tsx +6 -0
  104. package/src/components/input/markup/index.tsx +1 -0
  105. package/src/components/input/markup/text/Base.tsx +311 -0
  106. package/src/components/input/markup/text/Identification.tsx +145 -0
  107. package/src/components/input/markup/text/Password.tsx +71 -0
  108. package/src/components/input/markup/text/Phone.tsx +115 -0
  109. package/src/components/input/markup/text/Search.tsx +35 -0
  110. package/src/components/input/markup/text/index.ts +10 -0
  111. package/src/components/input/styles/index.scss +375 -0
  112. package/src/components/input/types/index.ts +56 -0
  113. package/src/components/input/utils/index.ts +54 -0
  114. package/src/components/label/hooks/index.ts +4 -0
  115. package/src/components/label/img/.gitkeep +0 -0
  116. package/src/components/label/index.scss +1 -0
  117. package/src/components/label/index.tsx +4 -0
  118. package/src/components/label/markup/index.tsx +4 -0
  119. package/src/components/label/styles/index.scss +3 -0
  120. package/src/components/label/types/index.ts +4 -0
  121. package/src/components/label/utils/index.ts +4 -0
  122. package/src/components/navigation/hooks/index.ts +4 -0
  123. package/src/components/navigation/img/.gitkeep +0 -0
  124. package/src/components/navigation/index.scss +1 -0
  125. package/src/components/navigation/index.tsx +8 -0
  126. package/src/components/navigation/markup/index.tsx +2 -0
  127. package/src/components/navigation/markup/mobile/BottomNavigation.tsx +127 -0
  128. package/src/components/navigation/markup/mobile/index.ts +1 -0
  129. package/src/components/navigation/markup/web/index.ts +4 -0
  130. package/src/components/navigation/styles/index.scss +133 -0
  131. package/src/components/navigation/types/index.ts +38 -0
  132. package/src/components/navigation/utils/index.ts +23 -0
  133. package/src/components/pagination/hooks/index.ts +4 -0
  134. package/src/components/pagination/img/.gitkeep +0 -0
  135. package/src/components/pagination/index.scss +1 -0
  136. package/src/components/pagination/index.tsx +6 -0
  137. package/src/components/pagination/markup/Carousel.tsx +76 -0
  138. package/src/components/pagination/markup/Count.tsx +54 -0
  139. package/src/components/pagination/markup/Pagination.tsx +83 -0
  140. package/src/components/pagination/markup/index.tsx +3 -0
  141. package/src/components/pagination/styles/index.scss +155 -0
  142. package/src/components/pagination/types/index.ts +68 -0
  143. package/src/components/pagination/utils/index.ts +58 -0
  144. package/src/components/radio/hooks/index.ts +4 -0
  145. package/src/components/radio/img/.gitkeep +0 -0
  146. package/src/components/radio/index.scss +1 -0
  147. package/src/components/radio/index.tsx +7 -0
  148. package/src/components/radio/markup/Radio.tsx +121 -0
  149. package/src/components/radio/markup/RadioCard.tsx +68 -0
  150. package/src/components/radio/markup/RadioCardGroup.tsx +75 -0
  151. package/src/components/radio/markup/index.tsx +3 -0
  152. package/src/components/radio/styles/index.scss +252 -0
  153. package/src/components/radio/types/index.ts +1 -0
  154. package/src/components/radio/types/radio.ts +63 -0
  155. package/src/components/radio/utils/index.ts +4 -0
  156. package/src/components/scrollbar/hooks/index.ts +4 -0
  157. package/src/components/scrollbar/img/.gitkeep +0 -0
  158. package/src/components/scrollbar/index.scss +1 -0
  159. package/src/components/scrollbar/index.tsx +4 -0
  160. package/src/components/scrollbar/markup/index.tsx +4 -0
  161. package/src/components/scrollbar/styles/index.scss +3 -0
  162. package/src/components/scrollbar/types/index.ts +4 -0
  163. package/src/components/scrollbar/utils/index.ts +4 -0
  164. package/src/components/segmented-control/index.scss +1 -0
  165. package/src/components/segmented-control/index.tsx +7 -0
  166. package/src/components/segmented-control/markup/SegmentedControl.tsx +117 -0
  167. package/src/components/segmented-control/markup/index.ts +1 -0
  168. package/src/components/segmented-control/styles/index.scss +113 -0
  169. package/src/components/segmented-control/types/index.ts +22 -0
  170. package/src/components/select/hooks/index.ts +4 -0
  171. package/src/components/select/img/.gitkeep +0 -0
  172. package/src/components/select/index.scss +1 -0
  173. package/src/components/select/index.tsx +4 -0
  174. package/src/components/select/markup/index.tsx +4 -0
  175. package/src/components/select/styles/index.scss +3 -0
  176. package/src/components/select/types/index.ts +4 -0
  177. package/src/components/select/utils/index.ts +4 -0
  178. package/src/components/spinner/hooks/index.ts +4 -0
  179. package/src/components/spinner/img/.gitkeep +0 -0
  180. package/src/components/spinner/index.scss +1 -0
  181. package/src/components/spinner/index.tsx +4 -0
  182. package/src/components/spinner/markup/index.tsx +4 -0
  183. package/src/components/spinner/styles/index.scss +3 -0
  184. package/src/components/spinner/types/index.ts +4 -0
  185. package/src/components/spinner/utils/index.ts +4 -0
  186. package/src/components/tab/hooks/index.ts +4 -0
  187. package/src/components/tab/img/.gitkeep +0 -0
  188. package/src/components/tab/index.scss +1 -0
  189. package/src/components/tab/index.tsx +6 -0
  190. package/src/components/tab/markup/TabContent.tsx +29 -0
  191. package/src/components/tab/markup/TabList.tsx +60 -0
  192. package/src/components/tab/markup/TabRoot.tsx +74 -0
  193. package/src/components/tab/markup/TabTrigger.tsx +47 -0
  194. package/src/components/tab/markup/index.tsx +4 -0
  195. package/src/components/tab/styles/index.scss +182 -0
  196. package/src/components/tab/types/index.ts +46 -0
  197. package/src/components/tab/utils/index.ts +5 -0
  198. package/src/components/tab/utils/tab-context.ts +20 -0
  199. package/src/components/table/hooks/index.ts +4 -0
  200. package/src/components/table/img/.gitkeep +0 -0
  201. package/src/components/table/index.scss +1 -0
  202. package/src/components/table/index.tsx +4 -0
  203. package/src/components/table/markup/index.tsx +4 -0
  204. package/src/components/table/styles/index.scss +3 -0
  205. package/src/components/table/types/index.ts +4 -0
  206. package/src/components/table/utils/index.ts +4 -0
  207. package/src/hooks/index.ts +4 -0
  208. package/src/img/.gitkeep +0 -0
  209. package/src/index.scss +3 -0
  210. package/src/index.tsx +26 -0
  211. package/src/init/dayjs.ts +14 -0
  212. package/src/theme/ThemeProvider.tsx +25 -0
  213. package/src/theme/config.ts +29 -0
  214. package/src/theme/index.ts +3 -0
  215. package/src/theme/overrides.scss +215 -0
  216. package/src/types/index.ts +4 -0
  217. package/src/utils/index.ts +4 -0
@@ -0,0 +1,252 @@
1
+ @use "@uniai-fe/uds-foundation/css";
2
+
3
+ :where(.radix-themes, .theme-root) {
4
+ --theme-radio-frame-size-medium: 20px;
5
+ --theme-radio-frame-size-large: 24px;
6
+ --theme-radio-indicator-size-medium: 16px;
7
+ --theme-radio-indicator-size-large: 20px;
8
+ --theme-radio-indicator-border-width: 1.4px;
9
+ --theme-radio-indicator-border-width-selected-medium: 4px;
10
+ --theme-radio-indicator-border-width-selected-large: 6px;
11
+ --theme-radio-border-color: var(--color-border-standard-assistive);
12
+ --theme-radio-border-selected: var(--color-primary-default);
13
+ --theme-radio-surface-selected: var(--color-primary-default);
14
+ --theme-radio-surface-disabled: var(--color-border-standard-assistive);
15
+ --theme-radio-border-disabled: var(--color-border-standard-assistive);
16
+ --theme-radio-disabled-selected-fill: var(--color-primary-default);
17
+ --theme-radio-label-color: var(--color-label-strong);
18
+ --theme-radio-label-disabled: var(--color-label-disabled);
19
+ --theme-radio-helper-color: var(--color-label-neutral);
20
+ --theme-radio-helper-disabled: var(--color-label-disabled);
21
+ --theme-radio-card-background: var(--color-common-100);
22
+ --theme-radio-card-title-color: var(--color-label-strong);
23
+ --theme-radio-card-description-color: var(--color-label-neutral);
24
+ --theme-radio-card-badge-background: var(--color-bg-surface-static-blue);
25
+ --theme-radio-card-badge-color: var(--color-primary-default);
26
+ --theme-radio-focus-ring: rgba(2, 84, 255, 0.32);
27
+ --theme-radio-disabled-selected-opacity: 0.28;
28
+ }
29
+
30
+ .radio {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ inline-size: var(--theme-radio-frame-size-medium);
35
+ block-size: var(--theme-radio-frame-size-medium);
36
+ border: none;
37
+ background-color: transparent;
38
+ border-radius: 0;
39
+ padding: 0;
40
+ cursor: pointer;
41
+ transition:
42
+ box-shadow 0.15s ease,
43
+ transform 0.15s ease;
44
+
45
+ &[data-size="large"] {
46
+ inline-size: var(--theme-radio-frame-size-large);
47
+ block-size: var(--theme-radio-frame-size-large);
48
+ }
49
+
50
+ &[data-disabled="true"] {
51
+ cursor: not-allowed;
52
+ opacity: 0.6;
53
+ }
54
+ }
55
+
56
+ .radio:focus-visible {
57
+ box-shadow: 0 0 0 2px var(--theme-radio-focus-ring);
58
+ }
59
+
60
+ .radio-indicator {
61
+ inline-size: var(--theme-radio-indicator-size-medium);
62
+ block-size: var(--theme-radio-indicator-size-medium);
63
+ border-radius: 9999px;
64
+ border: var(--theme-radio-indicator-border-width) solid
65
+ var(--theme-radio-border-color);
66
+ transition: border-color 0.15s ease;
67
+ }
68
+
69
+ .radio,
70
+ .radio[data-size="medium"] {
71
+ &[data-state="checked"] .radio-indicator {
72
+ border-width: var(--theme-radio-indicator-border-width-selected-medium);
73
+ }
74
+ }
75
+
76
+ .radio[data-size="large"] {
77
+ .radio-indicator {
78
+ inline-size: var(--theme-radio-indicator-size-large);
79
+ block-size: var(--theme-radio-indicator-size-large);
80
+ }
81
+ &[data-state="checked"] .radio-indicator {
82
+ border-width: var(--theme-radio-indicator-border-width-selected-large);
83
+ }
84
+ }
85
+
86
+ .radio[data-state="checked"] .radio-indicator {
87
+ border-color: var(--theme-radio-border-selected);
88
+ }
89
+
90
+ .radio[data-disabled="true"] .radio-indicator {
91
+ border-color: var(--theme-radio-border-disabled);
92
+ background-color: var(--theme-radio-surface-disabled);
93
+ opacity: 1;
94
+ }
95
+
96
+ .radio[data-disabled="true"][data-state="checked"] .radio-indicator {
97
+ border-color: var(--theme-radio-disabled-selected-fill);
98
+ opacity: var(--theme-radio-disabled-selected-opacity);
99
+ }
100
+
101
+ .radio-field {
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: var(--spacing-gap-1);
105
+ color: var(--theme-radio-label-color);
106
+ }
107
+
108
+ .radio-field[data-disabled="true"] {
109
+ color: var(--theme-radio-label-disabled);
110
+ }
111
+
112
+ .radio-label-wrapper {
113
+ display: inline-flex;
114
+ align-items: center;
115
+ gap: var(--spacing-gap-2);
116
+ cursor: pointer;
117
+ }
118
+
119
+ .radio-label-wrapper[data-disabled="true"] {
120
+ cursor: not-allowed;
121
+ }
122
+
123
+ .radio-label-text {
124
+ font-weight: var(--font-body-medium-weight);
125
+ user-select: none;
126
+ }
127
+
128
+ .radio-field[data-size="medium"] .radio-label-text {
129
+ font-size: var(--font-body-xsmall-size);
130
+ line-height: var(--font-body-xsmall-line-height);
131
+ }
132
+
133
+ .radio-field[data-size="large"] .radio-label-text {
134
+ font-size: var(--font-body-medium-size);
135
+ line-height: var(--font-body-medium-line-height);
136
+ }
137
+
138
+ .radio-helper {
139
+ margin: 0;
140
+ font-size: var(--font-caption-large-size);
141
+ line-height: var(--font-caption-large-line-height);
142
+ color: var(--theme-radio-helper-color);
143
+ user-select: none;
144
+ }
145
+
146
+ .radio-field[data-disabled="true"] .radio-helper {
147
+ color: var(--theme-radio-helper-disabled);
148
+ }
149
+
150
+ .radio-card-group {
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: var(--spacing-gap-3);
154
+ }
155
+
156
+ .radio-card {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ inline-size: 100%;
161
+ gap: var(--spacing-gap-3);
162
+ padding: 16px 24px;
163
+ background-color: var(--theme-radio-card-background);
164
+ transition: background-color 0.15s ease;
165
+ cursor: pointer;
166
+
167
+ &[data-disabled="true"] {
168
+ cursor: not-allowed;
169
+ opacity: 0.7;
170
+ }
171
+ }
172
+
173
+ .radio-card-content {
174
+ display: flex;
175
+ flex-direction: column;
176
+ gap: var(--spacing-gap-1);
177
+ min-inline-size: 0;
178
+ }
179
+
180
+ .radio-card-title-row {
181
+ display: inline-flex;
182
+ align-items: center;
183
+ gap: var(--spacing-gap-1);
184
+ min-inline-size: 0;
185
+ }
186
+
187
+ .radio-card-title {
188
+ font-size: var(--font-heading-small-size);
189
+ line-height: var(--font-heading-small-line-height);
190
+ font-weight: var(--font-heading-small-weight);
191
+ letter-spacing: 0.2px;
192
+ color: var(--theme-radio-card-title-color);
193
+ white-space: nowrap;
194
+ }
195
+
196
+ .radio-card-badge {
197
+ display: none;
198
+ align-items: center;
199
+ justify-content: center;
200
+ padding: 4px 6px;
201
+ font-size: var(--font-caption-medium-size);
202
+ line-height: var(--font-caption-medium-line-height);
203
+ border-radius: 6px;
204
+ background-color: var(--theme-radio-card-badge-background);
205
+ color: var(--theme-radio-card-badge-color);
206
+ }
207
+
208
+ .radio-card[data-state="checked"] {
209
+ .radio-card-title {
210
+ color: var(--theme-radio-card-badge-color);
211
+ }
212
+ .radio-card-badge {
213
+ display: inline-flex;
214
+ }
215
+ }
216
+
217
+ .radio-card-description {
218
+ margin: 0;
219
+ font-size: var(--font-body-small-size);
220
+ line-height: var(--font-body-small-line-height);
221
+ color: var(--theme-radio-card-description-color);
222
+ }
223
+
224
+ .radio-card-indicator-wrapper {
225
+ display: inline-flex;
226
+ align-items: center;
227
+ justify-content: center;
228
+ flex-shrink: 0;
229
+ }
230
+
231
+ .radio-card-indicator {
232
+ inline-size: var(--theme-radio-indicator-size-medium);
233
+ block-size: var(--theme-radio-indicator-size-medium);
234
+ border-radius: 9999px;
235
+ border: var(--theme-radio-indicator-border-width) solid
236
+ var(--theme-radio-border-color);
237
+ transition: border-color 0.15s ease;
238
+ }
239
+
240
+ .radio-card[data-size="large"] .radio-card-indicator {
241
+ inline-size: var(--theme-radio-indicator-size-large);
242
+ block-size: var(--theme-radio-indicator-size-large);
243
+ }
244
+
245
+ .radio-card[data-state="checked"] .radio-card-indicator {
246
+ border-color: var(--theme-radio-border-selected);
247
+ border-width: var(--theme-radio-indicator-border-width-selected-medium);
248
+ }
249
+
250
+ .radio-card[data-size="large"][data-state="checked"] .radio-card-indicator {
251
+ border-width: var(--theme-radio-indicator-border-width-selected-large);
252
+ }
@@ -0,0 +1 @@
1
+ export type * from "./radio";
@@ -0,0 +1,63 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from "react";
2
+ import type * as RadixRadioGroup from "@radix-ui/react-radio-group";
3
+
4
+ export type RadioSize = "medium" | "large";
5
+
6
+ export type RadioPrimitiveProps = ComponentPropsWithoutRef<
7
+ typeof RadixRadioGroup.Item
8
+ >;
9
+
10
+ export interface RadioProps extends RadioPrimitiveProps {
11
+ size?: RadioSize;
12
+ className?: string;
13
+ disabled?: boolean;
14
+ id?: string;
15
+ }
16
+
17
+ export interface RadioFieldProps extends RadioProps {
18
+ label?: ReactNode;
19
+ helperText?: ReactNode;
20
+ helperTextProps?: ComponentPropsWithoutRef<"p">;
21
+ labelProps?: ComponentPropsWithoutRef<"label">;
22
+ fieldClassName?: string;
23
+ labelWrapperClassName?: string;
24
+ id?: string;
25
+ disabled?: boolean;
26
+ }
27
+
28
+ interface RadioCardDisplayProps {
29
+ title: ReactNode;
30
+ description?: ReactNode;
31
+ badge?: ReactNode;
32
+ }
33
+
34
+ export interface RadioCardProps
35
+ extends
36
+ Omit<RadioProps, keyof RadioCardDisplayProps>,
37
+ RadioCardDisplayProps {}
38
+
39
+ export interface RadioCardGroupOption extends RadioCardDisplayProps {
40
+ id: string;
41
+ size?: RadioSize;
42
+ disabled?: boolean;
43
+ className?: string;
44
+ }
45
+
46
+ export interface RadioCardBadgeRenderContext {
47
+ option: RadioCardGroupOption;
48
+ isSelected: boolean;
49
+ isHighlighted: boolean;
50
+ }
51
+
52
+ export type RadioCardBadgeRender = (
53
+ context: RadioCardBadgeRenderContext,
54
+ ) => ReactNode;
55
+
56
+ export interface RadioCardGroupProps extends ComponentPropsWithoutRef<
57
+ typeof RadixRadioGroup.Root
58
+ > {
59
+ options: RadioCardGroupOption[];
60
+ size?: RadioSize;
61
+ highlightedValue?: string;
62
+ renderBadge?: RadioCardBadgeRender;
63
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(radio): 토큰 매핑과 클래스명 유틸을 구현한다.
3
+ */
4
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(scrollbar): 접근성/상태 계산 hook을 정의한다.
3
+ */
4
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * scrollbar 카테고리 배럴 placeholder: 실제 구현은 markup/ 하위에 추가한다.
3
+ */
4
+ export * from "./markup";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(scrollbar): SOT 및 사용자 제약에 따라 컴포넌트를 구현한다.
3
+ */
4
+ export {};
@@ -0,0 +1,3 @@
1
+ @use "@uniai-fe/uds-foundation/css";
2
+
3
+ /* TODO(scrollbar): 스타일을 SOT 토큰 값으로 정의한다. */
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(scrollbar): variant/slot 타입 정의를 작성한다.
3
+ */
4
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(scrollbar): 토큰 매핑과 클래스명 유틸을 구현한다.
3
+ */
4
+ export {};
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * segmented-control 카테고리 배럴
3
+ */
4
+ import "./index.scss";
5
+
6
+ export * from "./markup";
7
+ export * from "./types";
@@ -0,0 +1,117 @@
1
+ import { SegmentedControl as RadixSegmentedControl } from "@radix-ui/themes";
2
+ import clsx from "clsx";
3
+ import {
4
+ forwardRef,
5
+ useMemo,
6
+ useState,
7
+ type KeyboardEvent,
8
+ type MouseEvent,
9
+ type PointerEvent,
10
+ } from "react";
11
+ import type { SegmentedControlProps, SegmentedControlValue } from "../types";
12
+
13
+ const toNullableValue = (value?: SegmentedControlValue) =>
14
+ value === undefined || value === "" ? undefined : value;
15
+
16
+ const SegmentedControl = forwardRef<HTMLDivElement, SegmentedControlProps>(
17
+ (
18
+ {
19
+ options,
20
+ ariaLabel,
21
+ keepSelected = true,
22
+ className,
23
+ onValueChange,
24
+ value: valueProp,
25
+ defaultValue,
26
+ ...restProps
27
+ },
28
+ forwardedRef,
29
+ ) => {
30
+ const isControlled = valueProp !== undefined;
31
+ const [uncontrolledValue, setUncontrolledValue] = useState<
32
+ SegmentedControlValue | undefined
33
+ >(toNullableValue(defaultValue));
34
+ const selectedValue = toNullableValue(
35
+ isControlled ? valueProp : uncontrolledValue,
36
+ );
37
+
38
+ const emitChange = (nextValue: SegmentedControlValue | undefined) => {
39
+ if (!isControlled) {
40
+ setUncontrolledValue(nextValue);
41
+ }
42
+ onValueChange?.(nextValue ?? "");
43
+ };
44
+
45
+ const handleRootValueChange = (nextValue: string) => {
46
+ emitChange(nextValue);
47
+ };
48
+
49
+ const resolvedValue = useMemo(
50
+ () => selectedValue ?? undefined,
51
+ [selectedValue],
52
+ );
53
+
54
+ return (
55
+ <RadixSegmentedControl.Root
56
+ {...restProps}
57
+ ref={forwardedRef}
58
+ aria-label={ariaLabel}
59
+ className={clsx("segmented-control", className)}
60
+ value={resolvedValue}
61
+ onValueChange={handleRootValueChange}
62
+ data-keep-selected={keepSelected ? "true" : undefined}
63
+ >
64
+ {options.map(option => {
65
+ const isDisabled = Boolean(option.disabled);
66
+ const isSelected = selectedValue === option.value;
67
+
68
+ const preventDisabledInteraction = (
69
+ event:
70
+ | MouseEvent<HTMLButtonElement>
71
+ | PointerEvent<HTMLButtonElement>
72
+ | KeyboardEvent<HTMLButtonElement>,
73
+ ) => {
74
+ if (!isDisabled) {
75
+ return;
76
+ }
77
+ event.preventDefault();
78
+ event.stopPropagation();
79
+ };
80
+
81
+ const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
82
+ preventDisabledInteraction(event);
83
+ if (isDisabled) {
84
+ return;
85
+ }
86
+ if (!keepSelected && isSelected) {
87
+ event.preventDefault();
88
+ emitChange(undefined);
89
+ }
90
+ };
91
+
92
+ return (
93
+ <RadixSegmentedControl.Item
94
+ key={option.value}
95
+ value={option.value}
96
+ className="segmented-control-item"
97
+ aria-disabled={isDisabled || undefined}
98
+ data-disabled={isDisabled ? "true" : undefined}
99
+ tabIndex={isDisabled ? -1 : undefined}
100
+ onPointerDown={preventDisabledInteraction}
101
+ onKeyDown={preventDisabledInteraction}
102
+ onClick={handleClick}
103
+ >
104
+ <span className="segmented-control-item-label">
105
+ {option.label}
106
+ </span>
107
+ </RadixSegmentedControl.Item>
108
+ );
109
+ })}
110
+ </RadixSegmentedControl.Root>
111
+ );
112
+ },
113
+ );
114
+
115
+ SegmentedControl.displayName = "SegmentedControl";
116
+
117
+ export { SegmentedControl };
@@ -0,0 +1 @@
1
+ export * from "./SegmentedControl";
@@ -0,0 +1,113 @@
1
+ @use "@uniai-fe/uds-foundation/css";
2
+
3
+ .segmented-control {
4
+ --segmented-height: 34px;
5
+ --segmented-padding: 2px;
6
+ --segmented-radius: 16px;
7
+ --segmented-bg: var(--color-bg-alternative-cool-gray, #f2f2f3);
8
+ --segmented-indicator-bg: var(--color-common-100, #ffffff);
9
+ --segmented-indicator-shadow: 2px 2px 4px rgba(0, 0, 0, 0.02);
10
+ --segmented-label-color: var(--color-label-neutral, #797e86);
11
+ --segmented-label-active-color: var(--color-label-strong, #181a1b);
12
+ --segmented-disabled-opacity: 0.4;
13
+ --segmented-item-padding-x: 22px;
14
+ --segmented-item-padding-y: 4px;
15
+ --segmented-item-font-size: var(--font-heading-xxsmall-size, 15px);
16
+ --segmented-item-font-weight: var(--font-heading-xxsmall-weight, 500);
17
+ --segmented-item-line-height: var(--font-heading-xxsmall-line-height, 1.5);
18
+ display: grid;
19
+ grid-auto-flow: column;
20
+ grid-auto-columns: 1fr;
21
+ align-items: stretch;
22
+ padding: var(--segmented-padding);
23
+ border-radius: var(--segmented-radius);
24
+ background: var(--segmented-bg);
25
+ width: fit-content;
26
+ height: var(--segmented-height);
27
+ font-size: 0;
28
+ isolation: isolate;
29
+ }
30
+
31
+ .segmented-control:where(.rt-SegmentedControlRoot) {
32
+ /* Radix Theme 기본 inline-grid를 덮어 동일한 sizing 시스템에서만 layout이 될 수 있도록 한다. */
33
+ display: grid;
34
+ }
35
+
36
+ .segmented-control:where([data-keep-selected="true"]) {
37
+ --segmented-disabled-opacity: 0.3;
38
+ }
39
+
40
+ .segmented-control :where(.rt-SegmentedControlIndicator) {
41
+ border-radius: calc(var(--segmented-radius) - var(--segmented-padding));
42
+ background: transparent;
43
+ box-shadow: none;
44
+ overflow: hidden;
45
+ }
46
+
47
+ .segmented-control :where(.rt-SegmentedControlIndicator)::before {
48
+ content: "";
49
+ position: absolute;
50
+ inset: var(--segmented-padding);
51
+ border-radius: calc(var(--segmented-radius) - var(--segmented-padding));
52
+ background: var(--segmented-indicator-bg);
53
+ box-shadow: var(--segmented-indicator-shadow);
54
+ }
55
+
56
+ .segmented-control :where(.rt-SegmentedControlItemSeparator) {
57
+ display: none;
58
+ }
59
+
60
+ .segmented-control :where(.rt-SegmentedControlItem) {
61
+ background: transparent;
62
+ padding: 0;
63
+ border: none;
64
+ min-width: 0;
65
+ display: flex;
66
+ }
67
+
68
+ .segmented-control :where(.rt-SegmentedControlItemLabel) {
69
+ gap: 0;
70
+ height: fit-content;
71
+ padding: 0;
72
+ font-size: 0;
73
+ }
74
+
75
+ .segmented-control-item {
76
+ width: 100%;
77
+ height: 100%;
78
+ border: none;
79
+ background: transparent;
80
+ cursor: pointer;
81
+ .rt-SegmentedControlItemLabel {
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ padding: var(--segmented-item-padding-y) var(--segmented-item-padding-x);
86
+ border-radius: calc(var(--segmented-radius) - var(--segmented-padding));
87
+ }
88
+ }
89
+ .segmented-control-item:where([data-disabled="true"]) {
90
+ cursor: not-allowed;
91
+ opacity: var(--segmented-disabled-opacity);
92
+ }
93
+
94
+ .segmented-control-item:where(:focus-visible) {
95
+ outline: 2px solid var(--color-focus-ring, var(--color-primary-default));
96
+ outline-offset: 2px;
97
+ }
98
+
99
+ .segmented-control-item-label {
100
+ // display: flex;
101
+ // align-items: center;
102
+ // justify-content: center;
103
+ font-size: var(--segmented-item-font-size);
104
+ font-weight: var(--segmented-item-font-weight);
105
+ line-height: var(--segmented-item-line-height);
106
+ letter-spacing: 0.2px;
107
+ color: var(--segmented-label-color);
108
+ transition: color 0.2s ease;
109
+ }
110
+
111
+ .segmented-control-item:where([data-state="on"]) .segmented-control-item-label {
112
+ color: var(--segmented-label-active-color);
113
+ }
@@ -0,0 +1,22 @@
1
+ import type { ComponentPropsWithoutRef } from "react";
2
+ import type { SegmentedControl as RadixSegmentedControlNamespace } from "@radix-ui/themes";
3
+
4
+ export type SegmentedControlValue = string;
5
+
6
+ export interface SegmentedControlOption {
7
+ value: SegmentedControlValue;
8
+ label: string;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ type RadixSegmentedControlRootProps = ComponentPropsWithoutRef<
13
+ typeof RadixSegmentedControlNamespace.Root
14
+ >;
15
+ export interface SegmentedControlProps extends Omit<
16
+ RadixSegmentedControlRootProps,
17
+ "children"
18
+ > {
19
+ options: SegmentedControlOption[];
20
+ ariaLabel: string;
21
+ keepSelected?: boolean;
22
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(select): 접근성/상태 계산 hook을 정의한다.
3
+ */
4
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * select 카테고리 배럴 placeholder: 실제 구현은 markup/ 하위에 추가한다.
3
+ */
4
+ export * from "./markup";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(select): SOT 및 사용자 제약에 따라 컴포넌트를 구현한다.
3
+ */
4
+ export {};
@@ -0,0 +1,3 @@
1
+ @use "@uniai-fe/uds-foundation/css";
2
+
3
+ /* TODO(select): 스타일을 SOT 토큰 값으로 정의한다. */
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(select): variant/slot 타입 정의를 작성한다.
3
+ */
4
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(select): 토큰 매핑과 클래스명 유틸을 구현한다.
3
+ */
4
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(spinner): 접근성/상태 계산 hook을 정의한다.
3
+ */
4
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";