torch-glare 2.0.0 → 2.1.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.
- package/apps/lib/components/Badge.tsx +194 -67
- package/apps/lib/components/BadgeField.tsx +4 -4
- package/apps/lib/components/Charts-dev.tsx +365 -0
- package/apps/lib/components/Command-dev.tsx +151 -0
- package/apps/lib/components/Dialog.tsx +2 -2
- package/apps/lib/components/DropdownMenu.tsx +2 -19
- package/apps/lib/components/IosDatePicker-dev.tsx +341 -0
- package/dist/bin/index.js +0 -0
- package/docs/components/badge-field.md +21 -21
- package/docs/components/badge.md +156 -483
- package/docs/reference/components.md +8 -7
- package/docs/reference/types.md +34 -26
- package/docs/tutorials/theming-basics.md +30 -27
- package/package.json +6 -6
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { ComponentProps, forwardRef, HTMLAttributes, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { getDaysInMonth } from 'date-fns';
|
|
3
|
+
import { Popover, PopoverContent, PopoverTrigger } from './Popover';
|
|
4
|
+
import { InputField } from './InputField';
|
|
5
|
+
|
|
6
|
+
interface PickerValue {
|
|
7
|
+
[key: string]: string | number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getDayArray(year: number, month: number): string[] {
|
|
11
|
+
const dayCount = getDaysInMonth(new Date(year, month - 1));
|
|
12
|
+
return Array.from({ length: dayCount }, (_, i) => String(i + 1).padStart(2, '0'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface IosDatePickerProps extends Omit<ComponentProps<typeof InputField>, 'onChange'> {
|
|
16
|
+
onChange?: (e: Date) => void;
|
|
17
|
+
theme?: "dark" | "light" | "default";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const IosDatePicker = forwardRef<HTMLInputElement, IosDatePickerProps>(
|
|
21
|
+
({ theme = "dark", onChange, ...props }, forwardedRef) => {
|
|
22
|
+
const today = new Date();
|
|
23
|
+
|
|
24
|
+
const [year, setYear] = useState(String(today.getFullYear()));
|
|
25
|
+
const [month, setMonth] = useState(String(today.getMonth() + 1).padStart(2, '0'));
|
|
26
|
+
const [day, setDay] = useState(String(today.getDate()).padStart(2, '0'));
|
|
27
|
+
|
|
28
|
+
const currentYear = new Date().getFullYear();
|
|
29
|
+
const years = Array.from({ length: 150 }, (_, i) => `${currentYear - 100 + i}`);
|
|
30
|
+
const months = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, '0'));
|
|
31
|
+
const days = getDayArray(Number(year), Number(month));
|
|
32
|
+
const monthsNames = [
|
|
33
|
+
"January", "February", "March", "April", "May", "June",
|
|
34
|
+
"July", "August", "September", "October", "November", "December",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// Update handlers for each state separately
|
|
38
|
+
const handleYearChange = (value: string) => {
|
|
39
|
+
setYear(value);
|
|
40
|
+
const newDays = getDayArray(Number(value), Number(month));
|
|
41
|
+
if (!newDays.includes(day)) {
|
|
42
|
+
setDay(newDays[newDays.length - 1]);
|
|
43
|
+
}
|
|
44
|
+
triggerOnChange(value, month, day);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleMonthChange = (value: string) => {
|
|
48
|
+
setMonth(value);
|
|
49
|
+
const newDays = getDayArray(Number(year), Number(value));
|
|
50
|
+
if (!newDays.includes(day)) {
|
|
51
|
+
setDay(newDays[newDays.length - 1]);
|
|
52
|
+
}
|
|
53
|
+
triggerOnChange(year, value, day);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleDayChange = (value: string) => {
|
|
57
|
+
setDay(value);
|
|
58
|
+
triggerOnChange(year, month, value);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Function to trigger onChange callback
|
|
62
|
+
const triggerOnChange = (y: string, m: string, d: string) => {
|
|
63
|
+
if (onChange) {
|
|
64
|
+
onChange(new Date(`${y}-${m}-${d}`));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Popover>
|
|
70
|
+
<PopoverTrigger data-theme={theme} className='w-full flex-1'>
|
|
71
|
+
<InputField
|
|
72
|
+
theme={theme}
|
|
73
|
+
{...props}
|
|
74
|
+
ref={forwardedRef}
|
|
75
|
+
value={`${year}/${month}/${day}`}
|
|
76
|
+
readOnly
|
|
77
|
+
/>
|
|
78
|
+
</PopoverTrigger>
|
|
79
|
+
<PopoverContent data-theme={theme} dir="ltr" variant={props.variant} className="overflow-hidden w-[285px] flex justify-center items-center p-[6px] pt-[30px]">
|
|
80
|
+
<div className="flex justify-evenly items-center w-full absolute top-0 py-[6px]">
|
|
81
|
+
<p className="text-content-system-global-secondary typography-headers-medium-regular">Year</p>
|
|
82
|
+
<div className="flex justify-center items-center self-center">
|
|
83
|
+
<span className="h-[13px] w-[1px] bg-border-system-global-secondary rounded-[3px]"></span>
|
|
84
|
+
<p className="text-content-system-global-secondary typography-headers-medium-regular px-[18px]">Month</p>
|
|
85
|
+
<span className="h-[13px] w-[1px] bg-border-system-global-secondary rounded-[3px]"></span>
|
|
86
|
+
</div>
|
|
87
|
+
<p className="text-content-system-global-secondary typography-headers-medium-regular">Day</p>
|
|
88
|
+
</div>
|
|
89
|
+
<div className='absolute inset-0 w-full h-full flex justify-center items-center z-0 p-[6px]'>
|
|
90
|
+
<div className='w-full h-[42px] rounded-[8px] bg-background-system-body-tertiary mt-[23px]'></div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div
|
|
94
|
+
className="relative flex w-full h-[300px] max-w-full mx-auto text-white"
|
|
95
|
+
style={{
|
|
96
|
+
maskImage: 'linear-gradient(to top, transparent, transparent 10%, white 50%, white 19%, transparent 75%, transparent)',
|
|
97
|
+
WebkitMaskImage: 'linear-gradient(to top, transparent, transparent 10%, white 50%, white 19%, transparent 75%, transparent)',
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
<IosPickerItem
|
|
101
|
+
onValueSelect={handleYearChange}
|
|
102
|
+
slideData={years}
|
|
103
|
+
perspective="left"
|
|
104
|
+
selectedValue={year}
|
|
105
|
+
>
|
|
106
|
+
{years.map((value) => (
|
|
107
|
+
<SliderItem key={value}>{value}</SliderItem>
|
|
108
|
+
))}
|
|
109
|
+
</IosPickerItem>
|
|
110
|
+
<IosPickerItem
|
|
111
|
+
onValueSelect={handleMonthChange}
|
|
112
|
+
slideData={months}
|
|
113
|
+
perspective="left"
|
|
114
|
+
selectedValue={month}
|
|
115
|
+
>
|
|
116
|
+
{months.map((value) => (
|
|
117
|
+
<SliderItem key={value}>
|
|
118
|
+
{`${monthsNames[Number(value) - 1].substring(0, 3)}-${value}`}
|
|
119
|
+
</SliderItem>
|
|
120
|
+
))}
|
|
121
|
+
</IosPickerItem>
|
|
122
|
+
<IosPickerItem
|
|
123
|
+
onValueSelect={handleDayChange}
|
|
124
|
+
slideData={getDayArray(Number(year), Number(month))}
|
|
125
|
+
perspective="right"
|
|
126
|
+
selectedValue={day}
|
|
127
|
+
>
|
|
128
|
+
{getDayArray(Number(year), Number(month)).map((value) => (
|
|
129
|
+
<SliderItem key={`${value}-days`}>{value}</SliderItem>
|
|
130
|
+
))}
|
|
131
|
+
</IosPickerItem>
|
|
132
|
+
</div>
|
|
133
|
+
</PopoverContent>
|
|
134
|
+
</Popover>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// using with react hook form lib
|
|
140
|
+
/*
|
|
141
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
142
|
+
<Controller
|
|
143
|
+
name="date"
|
|
144
|
+
control={control}
|
|
145
|
+
render={({ field }) => (
|
|
146
|
+
<SlideDatePicker
|
|
147
|
+
{...field}
|
|
148
|
+
onChange={(value: Date) => field.onChange(value)}
|
|
149
|
+
/>
|
|
150
|
+
)}
|
|
151
|
+
/>
|
|
152
|
+
<button>submit</button>
|
|
153
|
+
</form>
|
|
154
|
+
*/
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
import { EmblaCarouselType } from 'embla-carousel'
|
|
159
|
+
import useEmblaCarousel from 'embla-carousel-react'
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
const CIRCLE_DEGREES = 360;
|
|
163
|
+
const WHEEL_ITEM_SIZE = 32;
|
|
164
|
+
const WHEEL_ITEM_COUNT = 18;
|
|
165
|
+
const WHEEL_ITEMS_IN_VIEW = 4;
|
|
166
|
+
const WHEEL_ITEM_RADIUS = CIRCLE_DEGREES / WHEEL_ITEM_COUNT;
|
|
167
|
+
const IN_VIEW_DEGREES = WHEEL_ITEM_RADIUS * WHEEL_ITEMS_IN_VIEW;
|
|
168
|
+
const WHEEL_RADIUS = Math.round(
|
|
169
|
+
WHEEL_ITEM_SIZE / 2 / Math.tan(Math.PI / WHEEL_ITEM_COUNT)
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const isInView = (wheelLocation: number, slidePosition: number): boolean =>
|
|
173
|
+
Math.abs(wheelLocation - slidePosition) < IN_VIEW_DEGREES;
|
|
174
|
+
|
|
175
|
+
interface PropType extends HTMLAttributes<HTMLDivElement> {
|
|
176
|
+
loop?: boolean;
|
|
177
|
+
slideData: string[];
|
|
178
|
+
selectedValue: any
|
|
179
|
+
perspective: 'left' | 'right';
|
|
180
|
+
onValueSelect?: (value: string) => void; // Callback to pass the selected value to the parent
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const IosPickerItem: React.FC<PropType> = ({
|
|
184
|
+
onValueSelect,
|
|
185
|
+
slideData,
|
|
186
|
+
perspective,
|
|
187
|
+
selectedValue,
|
|
188
|
+
loop = false,
|
|
189
|
+
...props
|
|
190
|
+
}) => {
|
|
191
|
+
const [emblaRef, emblaApi] = useEmblaCarousel({
|
|
192
|
+
loop,
|
|
193
|
+
axis: 'y',
|
|
194
|
+
dragFree: true,
|
|
195
|
+
containScroll: false,
|
|
196
|
+
watchSlides: false,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const rootNodeRef = useRef<HTMLDivElement>(null);
|
|
200
|
+
const [totalRadius, setTotalRadius] = useState(slideData.length * WHEEL_ITEM_RADIUS);
|
|
201
|
+
const [rotationOffset, setRotationOffset] = useState(loop ? 0 : WHEEL_ITEM_RADIUS);
|
|
202
|
+
|
|
203
|
+
// Update totalRadius and rotationOffset when data or loop changes
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
setTotalRadius(slideData.length * WHEEL_ITEM_RADIUS);
|
|
206
|
+
setRotationOffset(loop ? 0 : WHEEL_ITEM_RADIUS);
|
|
207
|
+
}, [slideData, loop]);
|
|
208
|
+
|
|
209
|
+
const inactivateEmblaTransform = useCallback(
|
|
210
|
+
(emblaApi: EmblaCarouselType) => {
|
|
211
|
+
if (!emblaApi) return;
|
|
212
|
+
const { translate, slideLooper } = emblaApi.internalEngine();
|
|
213
|
+
translate.clear();
|
|
214
|
+
translate.toggleActive(false);
|
|
215
|
+
slideLooper.loopPoints.forEach(({ translate }) => {
|
|
216
|
+
translate.clear();
|
|
217
|
+
translate.toggleActive(false);
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
[]
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
console.log(slideData.length)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
const rotateWheel = useCallback(
|
|
227
|
+
(emblaApi: EmblaCarouselType) => {
|
|
228
|
+
const rotation = slideData.length * WHEEL_ITEM_RADIUS - rotationOffset;
|
|
229
|
+
const wheelRotation = rotation * emblaApi.scrollProgress();
|
|
230
|
+
emblaApi.containerNode().style.transform = `translateZ(${0}px) rotateX(${wheelRotation}deg)`;
|
|
231
|
+
emblaApi.slideNodes().forEach((_, index) => {
|
|
232
|
+
setSlideStyles(emblaApi, index, loop, slideData.length, totalRadius);
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
[slideData, rotationOffset, totalRadius, loop]
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const setSlideStyles = useCallback(
|
|
239
|
+
(
|
|
240
|
+
emblaApi: EmblaCarouselType,
|
|
241
|
+
index: number,
|
|
242
|
+
loop: boolean,
|
|
243
|
+
dataLength: number,
|
|
244
|
+
totalRadius: number
|
|
245
|
+
) => {
|
|
246
|
+
const slideNode = emblaApi.slideNodes()[index];
|
|
247
|
+
const wheelLocation = emblaApi.scrollProgress() * totalRadius;
|
|
248
|
+
const positionDefault = emblaApi.scrollSnapList()[index] * totalRadius;
|
|
249
|
+
const positionLoopStart = positionDefault + totalRadius;
|
|
250
|
+
const positionLoopEnd = positionDefault - totalRadius;
|
|
251
|
+
|
|
252
|
+
let inView = false;
|
|
253
|
+
let angle = index * -WHEEL_ITEM_RADIUS;
|
|
254
|
+
|
|
255
|
+
if (isInView(wheelLocation, positionDefault)) {
|
|
256
|
+
inView = true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (loop && isInView(wheelLocation, positionLoopEnd)) {
|
|
260
|
+
inView = true;
|
|
261
|
+
angle = -CIRCLE_DEGREES + (dataLength - index) * WHEEL_ITEM_RADIUS;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (loop && isInView(wheelLocation, positionLoopStart)) {
|
|
265
|
+
inView = true;
|
|
266
|
+
angle = -(totalRadius % CIRCLE_DEGREES) - index * WHEEL_ITEM_RADIUS;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (inView) {
|
|
270
|
+
slideNode.style.opacity = '1';
|
|
271
|
+
slideNode.style.transform = `translateY(-${index * 100}%) rotateX(${angle}deg) translateZ(${WHEEL_RADIUS}px)`;
|
|
272
|
+
} else {
|
|
273
|
+
slideNode.style.opacity = '0';
|
|
274
|
+
slideNode.style.transform = 'none';
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
[loop, totalRadius]
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
const handleSelection = useCallback(
|
|
281
|
+
(emblaApi: EmblaCarouselType) => {
|
|
282
|
+
const selectedIndex = emblaApi.selectedScrollSnap();
|
|
283
|
+
const selectedValue = slideData[selectedIndex];
|
|
284
|
+
if (onValueSelect) {
|
|
285
|
+
onValueSelect(selectedValue); // Pass the selected value to the parent
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
[slideData]
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
if (!emblaApi) return;
|
|
293
|
+
|
|
294
|
+
emblaApi.on('pointerUp', (emblaApi) => {
|
|
295
|
+
const { scrollTo, target, location } = emblaApi.internalEngine();
|
|
296
|
+
const diffToTarget = target.get() - location.get();
|
|
297
|
+
const factor = Math.abs(diffToTarget) < WHEEL_ITEM_SIZE / 2.5 ? 10 : 0.1;
|
|
298
|
+
const distance = diffToTarget * factor;
|
|
299
|
+
scrollTo.distance(distance, true);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
emblaApi.on('scroll', rotateWheel);
|
|
303
|
+
emblaApi.on('select', handleSelection); // Listen for slide selection changes
|
|
304
|
+
|
|
305
|
+
emblaApi.on('reInit', (emblaApi) => {
|
|
306
|
+
inactivateEmblaTransform(emblaApi);
|
|
307
|
+
rotateWheel(emblaApi);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
inactivateEmblaTransform(emblaApi);
|
|
311
|
+
rotateWheel(emblaApi);
|
|
312
|
+
}, [emblaApi]);
|
|
313
|
+
|
|
314
|
+
return (
|
|
315
|
+
<div {...props} className="flex items-center justify-center w-[90px] h-full text-[1.8rem]">
|
|
316
|
+
<div className="min-w-full h-full flex items-center justify-center overflow-hidden touch-pan-x" ref={rootNodeRef}>
|
|
317
|
+
<div
|
|
318
|
+
className={`w-[75px] h-[32px] perspective-[3200px] select-none ${perspective === 'left'
|
|
319
|
+
? '[perspective-origin:calc(50%+130px)]'
|
|
320
|
+
: '[perspective-origin:calc(50%-130px)]'
|
|
321
|
+
}`}
|
|
322
|
+
ref={emblaRef}
|
|
323
|
+
>
|
|
324
|
+
<div className="flex flex-col w-full h-full [transform-style:preserve-3d] will-change-transform scroll-smooth">
|
|
325
|
+
{props.children}
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
interface SliderItemType extends HTMLAttributes<HTMLDivElement> { }
|
|
334
|
+
const SliderItem = (props: SliderItemType) => {
|
|
335
|
+
return (
|
|
336
|
+
<div className="w-full h-full text-[19px] text-center flex items-center justify-center [backface-visibility:hidden] opacity-0" {...props}>
|
|
337
|
+
{props.children}
|
|
338
|
+
</div>
|
|
339
|
+
);
|
|
340
|
+
};
|
|
341
|
+
|
package/dist/bin/index.js
CHANGED
|
File without changes
|
|
@@ -34,8 +34,8 @@ import { useState } from 'react'
|
|
|
34
34
|
export function BasicBadgeField() {
|
|
35
35
|
const [tags] = useState([
|
|
36
36
|
{ id: '1', name: 'React', variant: 'blue' },
|
|
37
|
-
{ id: '2', name: 'TypeScript', variant: '
|
|
38
|
-
{ id: '3', name: 'Next.js', variant: '
|
|
37
|
+
{ id: '2', name: 'TypeScript', variant: 'purple' },
|
|
38
|
+
{ id: '3', name: 'Next.js', variant: 'slate' },
|
|
39
39
|
])
|
|
40
40
|
|
|
41
41
|
return (
|
|
@@ -60,12 +60,12 @@ export function TechnologySelector() {
|
|
|
60
60
|
|
|
61
61
|
const allTags: Tag[] = [
|
|
62
62
|
{ id: '1', name: 'React', variant: 'blue' },
|
|
63
|
-
{ id: '2', name: 'TypeScript', variant: '
|
|
64
|
-
{ id: '3', name: 'Next.js', variant: '
|
|
65
|
-
{ id: '4', name: 'Tailwind CSS', variant: '
|
|
63
|
+
{ id: '2', name: 'TypeScript', variant: 'purple' },
|
|
64
|
+
{ id: '3', name: 'Next.js', variant: 'slate' },
|
|
65
|
+
{ id: '4', name: 'Tailwind CSS', variant: 'green' },
|
|
66
66
|
{ id: '5', name: 'Node.js', variant: 'green' },
|
|
67
67
|
{ id: '6', name: 'PostgreSQL', variant: 'blue' },
|
|
68
|
-
{ id: '7', name: 'MongoDB', variant: '
|
|
68
|
+
{ id: '7', name: 'MongoDB', variant: 'green' },
|
|
69
69
|
{ id: '8', name: 'GraphQL', variant: 'purple' },
|
|
70
70
|
{ id: '9', name: 'REST API', variant: 'gray' },
|
|
71
71
|
{ id: '10', name: 'Docker', variant: 'blue' },
|
|
@@ -105,9 +105,9 @@ export function CategoryFilter() {
|
|
|
105
105
|
{ id: 'development', name: 'Development', variant: 'blue' },
|
|
106
106
|
{ id: 'marketing', name: 'Marketing', variant: 'yellow' },
|
|
107
107
|
{ id: 'sales', name: 'Sales', variant: 'green' },
|
|
108
|
-
{ id: 'support', name: 'Support', variant: '
|
|
108
|
+
{ id: 'support', name: 'Support', variant: 'orange' },
|
|
109
109
|
{ id: 'hr', name: 'Human Resources', variant: 'rose' },
|
|
110
|
-
{ id: 'finance', name: 'Finance', variant: '
|
|
110
|
+
{ id: 'finance', name: 'Finance', variant: 'slate' },
|
|
111
111
|
]
|
|
112
112
|
|
|
113
113
|
const [selectedCategories, setSelectedCategories] = useState<Tag[]>([])
|
|
@@ -152,13 +152,13 @@ export function SkillsSelector() {
|
|
|
152
152
|
{ id: 'ts', name: 'TypeScript', variant: 'blue' },
|
|
153
153
|
{ id: 'react', name: 'React', variant: 'blue' },
|
|
154
154
|
{ id: 'vue', name: 'Vue.js', variant: 'green' },
|
|
155
|
-
{ id: 'angular', name: 'Angular', variant: '
|
|
156
|
-
{ id: 'node', name: 'Node.js', variant: '
|
|
155
|
+
{ id: 'angular', name: 'Angular', variant: 'red' },
|
|
156
|
+
{ id: 'node', name: 'Node.js', variant: 'green' },
|
|
157
157
|
{ id: 'python', name: 'Python', variant: 'blue' },
|
|
158
|
-
{ id: 'java', name: 'Java', variant: '
|
|
159
|
-
{ id: 'go', name: 'Go', variant: '
|
|
160
|
-
{ id: 'rust', name: 'Rust', variant: '
|
|
161
|
-
{ id: 'sql', name: 'SQL', variant: '
|
|
158
|
+
{ id: 'java', name: 'Java', variant: 'orange' },
|
|
159
|
+
{ id: 'go', name: 'Go', variant: 'green' },
|
|
160
|
+
{ id: 'rust', name: 'Rust', variant: 'orange' },
|
|
161
|
+
{ id: 'sql', name: 'SQL', variant: 'slate' },
|
|
162
162
|
{ id: 'nosql', name: 'NoSQL', variant: 'green' },
|
|
163
163
|
{ id: 'aws', name: 'AWS', variant: 'yellow' },
|
|
164
164
|
{ id: 'azure', name: 'Azure', variant: 'blue' },
|
|
@@ -213,16 +213,16 @@ Tag management for projects with custom variants.
|
|
|
213
213
|
```tsx
|
|
214
214
|
export function ProjectTags() {
|
|
215
215
|
const projectTags: Tag[] = [
|
|
216
|
-
{ id: '1', name: 'High Priority', variant: '
|
|
216
|
+
{ id: '1', name: 'High Priority', variant: 'red' },
|
|
217
217
|
{ id: '2', name: 'In Progress', variant: 'blue' },
|
|
218
218
|
{ id: '3', name: 'Completed', variant: 'green' },
|
|
219
219
|
{ id: '4', name: 'On Hold', variant: 'yellow' },
|
|
220
220
|
{ id: '5', name: 'Needs Review', variant: 'purple' },
|
|
221
|
-
{ id: '6', name: 'Client Approval', variant: '
|
|
221
|
+
{ id: '6', name: 'Client Approval', variant: 'purple' },
|
|
222
222
|
{ id: '7', name: 'Internal', variant: 'gray' },
|
|
223
|
-
{ id: '8', name: 'External', variant: '
|
|
224
|
-
{ id: '9', name: 'Urgent', variant: '
|
|
225
|
-
{ id: '10', name: 'Can Wait', variant: '
|
|
223
|
+
{ id: '8', name: 'External', variant: 'slate' },
|
|
224
|
+
{ id: '9', name: 'Urgent', variant: 'orange' },
|
|
225
|
+
{ id: '10', name: 'Can Wait', variant: 'green' },
|
|
226
226
|
]
|
|
227
227
|
|
|
228
228
|
const [projectData, setProjectData] = useState({
|
|
@@ -310,7 +310,7 @@ export function BadgeFieldWithIcon() {
|
|
|
310
310
|
const tags: Tag[] = [
|
|
311
311
|
{ id: '1', name: 'JavaScript', variant: 'yellow' },
|
|
312
312
|
{ id: '2', name: 'Python', variant: 'blue' },
|
|
313
|
-
{ id: '3', name: 'Ruby', variant: '
|
|
313
|
+
{ id: '3', name: 'Ruby', variant: 'red' },
|
|
314
314
|
]
|
|
315
315
|
|
|
316
316
|
return (
|
|
@@ -385,7 +385,7 @@ export function EmailRecipients() {
|
|
|
385
385
|
{ id: '1', name: 'john@example.com', variant: 'blue' },
|
|
386
386
|
{ id: '2', name: 'jane@example.com', variant: 'green' },
|
|
387
387
|
{ id: '3', name: 'team@example.com', variant: 'purple' },
|
|
388
|
-
{ id: '4', name: 'support@example.com', variant: '
|
|
388
|
+
{ id: '4', name: 'support@example.com', variant: 'slate' },
|
|
389
389
|
]
|
|
390
390
|
|
|
391
391
|
const [recipients, setRecipients] = useState<Tag[]>([])
|