@galyan/ui 0.0.1 → 0.0.2

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 (90) hide show
  1. package/package.json +53 -22
  2. package/.turbo/turbo-build.log +0 -20
  3. package/eslint.config.mjs +0 -4
  4. package/src/accordion/Accordion.tsx +0 -88
  5. package/src/accordion/accordion.css +0 -78
  6. package/src/accordion/index.ts +0 -2
  7. package/src/animatednumber/AnimatedNumber.tsx +0 -71
  8. package/src/animatednumber/index.ts +0 -2
  9. package/src/banner/Banner.tsx +0 -84
  10. package/src/banner/banner.css +0 -56
  11. package/src/banner/index.ts +0 -2
  12. package/src/breadcrumb/Breadcrumb.tsx +0 -94
  13. package/src/breadcrumb/breadcrumb.css +0 -58
  14. package/src/breadcrumb/index.ts +0 -2
  15. package/src/button/Button.tsx +0 -98
  16. package/src/button/button.css +0 -190
  17. package/src/button/index.ts +0 -2
  18. package/src/button.tsx +0 -20
  19. package/src/calendar/Calendar.tsx +0 -190
  20. package/src/calendar/calendar.css +0 -142
  21. package/src/calendar/index.ts +0 -2
  22. package/src/card/Card.tsx +0 -88
  23. package/src/card/card.css +0 -115
  24. package/src/card/index.ts +0 -2
  25. package/src/card.tsx +0 -27
  26. package/src/chart/Chart.tsx +0 -186
  27. package/src/chart/index.ts +0 -2
  28. package/src/checkbox/Checkbox.tsx +0 -101
  29. package/src/checkbox/checkbox.css +0 -76
  30. package/src/checkbox/index.ts +0 -2
  31. package/src/chips/Chips.tsx +0 -146
  32. package/src/chips/chips.css +0 -90
  33. package/src/chips/index.ts +0 -2
  34. package/src/code.tsx +0 -11
  35. package/src/datepicker/DatePicker.tsx +0 -104
  36. package/src/datepicker/datepicker.css +0 -22
  37. package/src/datepicker/index.ts +0 -2
  38. package/src/dragdrop/DragDrop.tsx +0 -168
  39. package/src/dragdrop/dragdrop.css +0 -122
  40. package/src/dragdrop/index.ts +0 -2
  41. package/src/dropdown/Dropdown.tsx +0 -176
  42. package/src/dropdown/dropdown.css +0 -118
  43. package/src/dropdown/index.ts +0 -2
  44. package/src/fileupload/FileUpload.tsx +0 -144
  45. package/src/fileupload/fileupload.css +0 -118
  46. package/src/fileupload/index.ts +0 -2
  47. package/src/index.ts +0 -28
  48. package/src/input/Input.tsx +0 -121
  49. package/src/input/index.ts +0 -2
  50. package/src/input/input.css +0 -120
  51. package/src/menu/Menu.tsx +0 -118
  52. package/src/menu/index.ts +0 -2
  53. package/src/menu/menu.css +0 -117
  54. package/src/modal/Modal.tsx +0 -103
  55. package/src/modal/index.ts +0 -2
  56. package/src/modal/modal.css +0 -79
  57. package/src/progressbar/ProgressBar.tsx +0 -68
  58. package/src/progressbar/index.ts +0 -2
  59. package/src/progressbar/progressbar.css +0 -73
  60. package/src/radiogroup/RadioGroup.tsx +0 -73
  61. package/src/radiogroup/index.ts +0 -2
  62. package/src/radiogroup/radiogroup.css +0 -80
  63. package/src/skeleton/Skeleton.tsx +0 -61
  64. package/src/skeleton/index.ts +0 -2
  65. package/src/skeleton/skeleton.css +0 -46
  66. package/src/spinner/Spinner.tsx +0 -29
  67. package/src/spinner/index.ts +0 -2
  68. package/src/spinner/spinner.css +0 -39
  69. package/src/stepper/Stepper.tsx +0 -127
  70. package/src/stepper/index.ts +0 -2
  71. package/src/stepper/stepper.css +0 -141
  72. package/src/tab/Tabs.tsx +0 -71
  73. package/src/tab/index.ts +0 -2
  74. package/src/tab/tab.css +0 -92
  75. package/src/table/Table.tsx +0 -205
  76. package/src/table/index.ts +0 -2
  77. package/src/table/table.css +0 -119
  78. package/src/textarea/Textarea.tsx +0 -102
  79. package/src/textarea/index.ts +0 -2
  80. package/src/textarea/textarea.css +0 -85
  81. package/src/toaster/Toaster.tsx +0 -108
  82. package/src/toaster/index.ts +0 -2
  83. package/src/toaster/toaster.css +0 -113
  84. package/src/toggle/Toggle.tsx +0 -88
  85. package/src/toggle/index.ts +0 -2
  86. package/src/toggle/toggle.css +0 -63
  87. package/src/typography/Typography.tsx +0 -58
  88. package/src/typography/index.ts +0 -2
  89. package/src/typography/typography.css +0 -102
  90. package/tsconfig.json +0 -13
@@ -1,102 +0,0 @@
1
- 'use client';
2
-
3
- import React, { forwardRef, useCallback, useEffect, useId, useRef } from 'react';
4
- import './textarea.css';
5
-
6
- export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
7
- label?: string;
8
- error?: string;
9
- helperText?: string;
10
- maxCharCount?: number;
11
- autoResize?: boolean;
12
- required?: boolean;
13
- }
14
-
15
- export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(
16
- {
17
- label,
18
- error,
19
- helperText,
20
- maxCharCount,
21
- autoResize = false,
22
- required,
23
- disabled,
24
- value,
25
- onChange,
26
- className = '',
27
- id,
28
- ...rest
29
- },
30
- ref
31
- ) {
32
- const uid = useId();
33
- const inputId = id ?? uid;
34
- const innerRef = useRef<HTMLTextAreaElement>(null);
35
- const textareaRef = (ref as React.RefObject<HTMLTextAreaElement>) ?? innerRef;
36
-
37
- const charCount = typeof value === 'string' ? value.length : 0;
38
- const isOver = maxCharCount ? charCount > maxCharCount : false;
39
-
40
- const adjustHeight = useCallback(() => {
41
- const el = textareaRef.current;
42
- if (!el || !autoResize) return;
43
- el.style.height = 'auto';
44
- el.style.height = `${el.scrollHeight}px`;
45
- }, [autoResize, textareaRef]);
46
-
47
- useEffect(() => {
48
- adjustHeight();
49
- }, [value, adjustHeight]);
50
-
51
- const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
52
- adjustHeight();
53
- onChange?.(e);
54
- };
55
-
56
- return (
57
- <div className={`gy-textarea-root ${className}`}>
58
- {label && (
59
- <label
60
- className={`gy-textarea-label ${required ? 'gy-textarea-label--required' : ''}`}
61
- htmlFor={inputId}
62
- >
63
- {label}
64
- </label>
65
- )}
66
-
67
- <textarea
68
- ref={textareaRef}
69
- id={inputId}
70
- className={[
71
- 'gy-textarea',
72
- error ? 'gy-textarea--error' : '',
73
- autoResize ? 'gy-textarea--auto-resize' : '',
74
- ]
75
- .filter(Boolean)
76
- .join(' ')}
77
- disabled={disabled}
78
- required={required}
79
- aria-invalid={!!error}
80
- value={value}
81
- onChange={handleChange}
82
- {...rest}
83
- />
84
-
85
- <div className="gy-textarea-footer">
86
- {error ? (
87
- <span className="gy-textarea-helper gy-textarea-helper--error" role="alert">{error}</span>
88
- ) : helperText ? (
89
- <span className="gy-textarea-helper">{helperText}</span>
90
- ) : (
91
- <span />
92
- )}
93
-
94
- {maxCharCount !== undefined && (
95
- <span className={`gy-textarea-count ${isOver ? 'gy-textarea-count--over' : ''}`}>
96
- {charCount}/{maxCharCount}
97
- </span>
98
- )}
99
- </div>
100
- </div>
101
- );
102
- });
@@ -1,2 +0,0 @@
1
- export { Textarea } from './Textarea';
2
- export type { TextareaProps } from './Textarea';
@@ -1,85 +0,0 @@
1
- /* ── Textarea ─────────────────────────────────────────────────────────────── */
2
- .gy-textarea-root {
3
- display: flex;
4
- flex-direction: column;
5
- gap: var(--gy-spacing-1-5);
6
- width: 100%;
7
- }
8
-
9
- .gy-textarea-label {
10
- font-size: var(--gy-font-size-sm);
11
- font-weight: var(--gy-font-weight-medium);
12
- color: var(--gy-text);
13
- }
14
-
15
- .gy-textarea-label--required::after {
16
- content: '*';
17
- color: var(--gy-danger);
18
- margin-left: 2px;
19
- }
20
-
21
- .gy-textarea {
22
- width: 100%;
23
- min-height: 80px;
24
- padding: var(--gy-spacing-2-5) var(--gy-spacing-3);
25
- font-family: var(--gy-font-sans);
26
- font-size: var(--gy-font-size-sm);
27
- line-height: var(--gy-line-height-relaxed);
28
- color: var(--gy-text);
29
- background: var(--gy-surface);
30
- border: 1.5px solid var(--gy-border);
31
- border-radius: var(--gy-radius-lg);
32
- resize: vertical;
33
- transition:
34
- border-color var(--gy-duration-fast) var(--gy-ease-in-out),
35
- box-shadow var(--gy-duration-fast) var(--gy-ease-in-out);
36
- outline: none;
37
- }
38
-
39
- .gy-textarea::placeholder { color: var(--gy-text-subtle); }
40
-
41
- .gy-textarea:focus {
42
- border-color: var(--gy-border-focus);
43
- box-shadow: 0 0 0 3px var(--gy-focus-ring);
44
- }
45
-
46
- .gy-textarea--error {
47
- border-color: var(--gy-danger);
48
- }
49
-
50
- .gy-textarea--error:focus {
51
- box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2);
52
- }
53
-
54
- .gy-textarea:disabled {
55
- background: var(--gy-disabled-bg);
56
- color: var(--gy-text-disabled);
57
- cursor: not-allowed;
58
- resize: none;
59
- }
60
-
61
- .gy-textarea--auto-resize {
62
- resize: none;
63
- overflow: hidden;
64
- }
65
-
66
- .gy-textarea-footer {
67
- display: flex;
68
- justify-content: space-between;
69
- align-items: center;
70
- }
71
-
72
- .gy-textarea-helper {
73
- font-size: var(--gy-font-size-xs);
74
- color: var(--gy-text-subtle);
75
- }
76
-
77
- .gy-textarea-helper--error { color: var(--gy-danger); }
78
-
79
- .gy-textarea-count {
80
- font-size: var(--gy-font-size-xs);
81
- color: var(--gy-text-subtle);
82
- margin-left: auto;
83
- }
84
-
85
- .gy-textarea-count--over { color: var(--gy-danger); }
@@ -1,108 +0,0 @@
1
- 'use client';
2
-
3
- import React, { createContext, useCallback, useContext, useId, useState } from 'react';
4
- import { createPortal } from 'react-dom';
5
- import { Spinner } from '../spinner/Spinner';
6
- import './toaster.css';
7
-
8
- export type ToastVariant = 'success' | 'error' | 'warning' | 'info' | 'loading';
9
- export type ToastPosition =
10
- | 'top-right' | 'top-left' | 'top-center'
11
- | 'bottom-right' | 'bottom-left' | 'bottom-center';
12
-
13
- export interface Toast {
14
- id: string;
15
- title: string;
16
- description?: string;
17
- variant?: ToastVariant;
18
- duration?: number;
19
- dismissible?: boolean;
20
- }
21
-
22
- interface ToastContextValue {
23
- toasts: Toast[];
24
- toast: (toast: Omit<Toast, 'id'>) => string;
25
- dismiss: (id: string) => void;
26
- dismissAll: () => void;
27
- }
28
-
29
- const ToastContext = createContext<ToastContextValue | null>(null);
30
-
31
- const icons: Record<ToastVariant, React.ReactNode> = {
32
- success: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="#10b981" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="8" cy="8" r="7"/><polyline points="5,8 7,10 11,6"/></svg>,
33
- error: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="#ef4444" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="8" cy="8" r="7"/><line x1="8" y1="5" x2="8" y2="9"/><circle cx="8" cy="11.5" r="0.5" fill="#ef4444"/></svg>,
34
- warning: <svg width="16" height="16" viewBox="0 0 16 16" fill="#f59e0b"><path d="M8 2L1 14h14L8 2zm0 4v4m0 2v.01" stroke="white" strokeWidth="1.5" strokeLinecap="round"/></svg>,
35
- info: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="#0ea5e9" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="8" cy="8" r="7"/><line x1="8" y1="8" x2="8" y2="12"/><circle cx="8" cy="5" r="0.5" fill="#0ea5e9"/></svg>,
36
- loading: <Spinner size="xs" />,
37
- };
38
-
39
- export function ToasterProvider({ children, position = 'top-right' }: { children: React.ReactNode; position?: ToastPosition }) {
40
- const [toasts, setToasts] = useState<Toast[]>([]);
41
-
42
- const dismiss = useCallback((id: string) => {
43
- setToasts((prev) => prev.filter((t) => t.id !== id));
44
- }, []);
45
-
46
- const dismissAll = useCallback(() => setToasts([]), []);
47
-
48
- const toast = useCallback((data: Omit<Toast, 'id'>) => {
49
- const id = Math.random().toString(36).slice(2);
50
- const duration = data.duration ?? (data.variant === 'loading' ? 0 : 4000);
51
- const newToast: Toast = { ...data, id, duration };
52
-
53
- setToasts((prev) => [...prev, newToast]);
54
-
55
- if (duration > 0) {
56
- setTimeout(() => dismiss(id), duration);
57
- }
58
-
59
- return id;
60
- }, [dismiss]);
61
-
62
- const isLeft = position.includes('left');
63
-
64
- const toasterNode = (
65
- <div className={`gy-toaster gy-toaster--${position}`}>
66
- {toasts.map((t) => (
67
- <div
68
- key={t.id}
69
- className={`gy-toast gy-toast--${t.variant ?? 'info'} ${isLeft ? 'gy-toast--left' : ''}`}
70
- role="alert"
71
- aria-live="polite"
72
- >
73
- <div className="gy-toast-accent" />
74
- <span className="gy-toast-icon">{icons[t.variant ?? 'info']}</span>
75
- <div className="gy-toast-content">
76
- <div className="gy-toast-title">{t.title}</div>
77
- {t.description && <div className="gy-toast-description">{t.description}</div>}
78
- </div>
79
- {(t.dismissible ?? true) && (
80
- <button className="gy-toast-close" onClick={() => dismiss(t.id)} aria-label="Dismiss">
81
- <svg width="12" height="12" viewBox="0 0 12 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
82
- <line x1="1" y1="1" x2="11" y2="11" /><line x1="11" y1="1" x2="1" y2="11" />
83
- </svg>
84
- </button>
85
- )}
86
- {t.duration && t.duration > 0 && (
87
- <div className="gy-toast-progress">
88
- <div className="gy-toast-progress-bar" style={{ animationDuration: `${t.duration}ms` }} />
89
- </div>
90
- )}
91
- </div>
92
- ))}
93
- </div>
94
- );
95
-
96
- return (
97
- <ToastContext.Provider value={{ toasts, toast, dismiss, dismissAll }}>
98
- {children}
99
- {typeof document !== 'undefined' && createPortal(toasterNode, document.body)}
100
- </ToastContext.Provider>
101
- );
102
- }
103
-
104
- export function useToast() {
105
- const ctx = useContext(ToastContext);
106
- if (!ctx) throw new Error('useToast must be used inside ToasterProvider');
107
- return ctx;
108
- }
@@ -1,2 +0,0 @@
1
- export { ToasterProvider, useToast } from './Toaster';
2
- export type { Toast, ToastVariant, ToastPosition } from './Toaster';
@@ -1,113 +0,0 @@
1
- /* ── Toaster ──────────────────────────────────────────────────────────────── */
2
- .gy-toaster {
3
- position: fixed;
4
- z-index: var(--gy-z-toast);
5
- display: flex;
6
- flex-direction: column;
7
- gap: var(--gy-spacing-2);
8
- pointer-events: none;
9
- padding: var(--gy-spacing-4);
10
- }
11
-
12
- .gy-toaster--top-right { top: 0; right: 0; align-items: flex-end; }
13
- .gy-toaster--top-left { top: 0; left: 0; align-items: flex-start; }
14
- .gy-toaster--top-center { top: 0; left: 50%; transform: translateX(-50%); align-items: center; }
15
- .gy-toaster--bottom-right { bottom: 0; right: 0; align-items: flex-end; flex-direction: column-reverse; }
16
- .gy-toaster--bottom-left { bottom: 0; left: 0; align-items: flex-start; flex-direction: column-reverse; }
17
- .gy-toaster--bottom-center { bottom: 0; left: 50%; transform: translateX(-50%); align-items: center; flex-direction: column-reverse; }
18
-
19
- .gy-toast {
20
- display: flex;
21
- align-items: flex-start;
22
- gap: var(--gy-spacing-3);
23
- padding: var(--gy-spacing-3) var(--gy-spacing-4);
24
- background: var(--gy-surface);
25
- border-radius: var(--gy-radius-xl);
26
- box-shadow: var(--gy-shadow-lg);
27
- border: 1px solid var(--gy-border);
28
- min-width: 280px;
29
- max-width: 380px;
30
- pointer-events: all;
31
- animation: gy-slide-toast-in-right var(--gy-duration-normal) var(--gy-ease-spring);
32
- position: relative;
33
- overflow: hidden;
34
- }
35
-
36
- .gy-toast--left { animation: gy-slide-in-left var(--gy-duration-normal) var(--gy-ease-spring); }
37
-
38
- .gy-toast-accent {
39
- position: absolute;
40
- left: 0;
41
- top: 0;
42
- bottom: 0;
43
- width: 4px;
44
- border-radius: var(--gy-radius-lg) 0 0 var(--gy-radius-lg);
45
- }
46
-
47
- .gy-toast--success .gy-toast-accent { background: var(--gy-success); }
48
- .gy-toast--error .gy-toast-accent { background: var(--gy-danger); }
49
- .gy-toast--warning .gy-toast-accent { background: var(--gy-warning); }
50
- .gy-toast--info .gy-toast-accent { background: var(--gy-info); }
51
- .gy-toast--loading .gy-toast-accent { background: var(--gy-primary); }
52
-
53
- .gy-toast-icon {
54
- flex-shrink: 0;
55
- margin-left: var(--gy-spacing-1);
56
- }
57
-
58
- .gy-toast-content { flex: 1; }
59
-
60
- .gy-toast-title {
61
- font-size: var(--gy-font-size-sm);
62
- font-weight: var(--gy-font-weight-semibold);
63
- color: var(--gy-text);
64
- line-height: 1.4;
65
- }
66
-
67
- .gy-toast-description {
68
- font-size: var(--gy-font-size-xs);
69
- color: var(--gy-text-muted);
70
- margin-top: 2px;
71
- line-height: 1.5;
72
- }
73
-
74
- .gy-toast-close {
75
- display: flex;
76
- align-items: center;
77
- justify-content: center;
78
- width: 1.5rem;
79
- height: 1.5rem;
80
- border-radius: var(--gy-radius-md);
81
- flex-shrink: 0;
82
- color: var(--gy-text-subtle);
83
- cursor: pointer;
84
- background: none;
85
- border: none;
86
- transition: color var(--gy-duration-fast), background var(--gy-duration-fast);
87
- margin-top: -2px;
88
- }
89
-
90
- .gy-toast-close:hover { color: var(--gy-text); background: var(--gy-background-muted); }
91
-
92
- .gy-toast-progress {
93
- position: absolute;
94
- bottom: 0;
95
- left: 0;
96
- right: 0;
97
- height: 3px;
98
- background: var(--gy-primary-muted);
99
- border-radius: 0 0 var(--gy-radius-xl) var(--gy-radius-xl);
100
- }
101
-
102
- .gy-toast-progress-bar {
103
- height: 100%;
104
- background: var(--gy-primary);
105
- border-radius: inherit;
106
- transform-origin: left;
107
- animation: gy-toast-progress linear forwards;
108
- }
109
-
110
- @keyframes gy-toast-progress {
111
- from { width: 100%; }
112
- to { width: 0%; }
113
- }
@@ -1,88 +0,0 @@
1
- 'use client';
2
-
3
- import React, { forwardRef, useId } from 'react';
4
- import './toggle.css';
5
-
6
- export type ToggleSize = 'sm' | 'md' | 'lg';
7
-
8
- export interface ToggleProps {
9
- checked?: boolean;
10
- defaultChecked?: boolean;
11
- onChange?: (checked: boolean) => void;
12
- disabled?: boolean;
13
- label?: React.ReactNode;
14
- labelPosition?: 'left' | 'right';
15
- size?: ToggleSize;
16
- id?: string;
17
- name?: string;
18
- className?: string;
19
- }
20
-
21
- export const Toggle = forwardRef<HTMLInputElement, ToggleProps>(function Toggle(
22
- {
23
- checked,
24
- defaultChecked,
25
- onChange,
26
- disabled = false,
27
- label,
28
- labelPosition = 'right',
29
- size = 'md',
30
- id,
31
- name,
32
- className = '',
33
- },
34
- ref
35
- ) {
36
- const uid = useId();
37
- const inputId = id ?? uid;
38
- const isChecked = checked ?? defaultChecked ?? false;
39
-
40
- const track = (
41
- <span
42
- className={[
43
- 'gy-toggle-track',
44
- `gy-toggle-track--${size}`,
45
- isChecked ? 'gy-toggle-track--checked' : '',
46
- ]
47
- .filter(Boolean)
48
- .join(' ')}
49
- aria-hidden="true"
50
- >
51
- <span className="gy-toggle-thumb" />
52
- </span>
53
- );
54
-
55
- const labelEl = label && (
56
- <span className="gy-toggle-label">{label}</span>
57
- );
58
-
59
- return (
60
- <label
61
- htmlFor={inputId}
62
- className={[
63
- 'gy-toggle-root',
64
- disabled ? 'gy-toggle-root--disabled' : '',
65
- className,
66
- ]
67
- .filter(Boolean)
68
- .join(' ')}
69
- >
70
- <input
71
- ref={ref}
72
- id={inputId}
73
- type="checkbox"
74
- role="switch"
75
- name={name}
76
- checked={checked}
77
- defaultChecked={defaultChecked}
78
- disabled={disabled}
79
- className="gy-toggle-input"
80
- aria-checked={isChecked}
81
- onChange={(e) => onChange?.(e.target.checked)}
82
- />
83
- {labelPosition === 'left' && labelEl}
84
- {track}
85
- {labelPosition === 'right' && labelEl}
86
- </label>
87
- );
88
- });
@@ -1,2 +0,0 @@
1
- export { Toggle } from './Toggle';
2
- export type { ToggleProps, ToggleSize } from './Toggle';
@@ -1,63 +0,0 @@
1
- /* ── Toggle / Switch ──────────────────────────────────────────────────────── */
2
- .gy-toggle-root {
3
- display: inline-flex;
4
- align-items: center;
5
- gap: var(--gy-spacing-2-5);
6
- cursor: pointer;
7
- }
8
-
9
- .gy-toggle-root--disabled {
10
- cursor: not-allowed;
11
- opacity: 0.5;
12
- }
13
-
14
- .gy-toggle-input {
15
- position: absolute;
16
- opacity: 0;
17
- width: 0;
18
- height: 0;
19
- }
20
-
21
- .gy-toggle-track {
22
- position: relative;
23
- background: var(--gy-border-strong);
24
- border-radius: var(--gy-radius-full);
25
- transition:
26
- background-color var(--gy-duration-fast) var(--gy-ease-in-out),
27
- box-shadow var(--gy-duration-fast) var(--gy-ease-in-out);
28
- }
29
-
30
- .gy-toggle-track--sm { width: 2rem; height: 1.125rem; }
31
- .gy-toggle-track--md { width: 2.5rem; height: 1.375rem; }
32
- .gy-toggle-track--lg { width: 3rem; height: 1.625rem; }
33
-
34
- .gy-toggle-track--checked { background: var(--gy-primary); }
35
-
36
- .gy-toggle-input:focus-visible ~ .gy-toggle-label,
37
- .gy-toggle-root:has(.gy-toggle-input:focus-visible) .gy-toggle-track {
38
- box-shadow: 0 0 0 3px var(--gy-focus-ring);
39
- }
40
-
41
- .gy-toggle-thumb {
42
- position: absolute;
43
- top: 50%;
44
- transform: translateY(-50%) translateX(2px);
45
- background: white;
46
- border-radius: 50%;
47
- box-shadow: var(--gy-shadow-sm);
48
- transition: transform var(--gy-duration-normal) var(--gy-ease-spring);
49
- }
50
-
51
- .gy-toggle-track--sm .gy-toggle-thumb { width: 0.875rem; height: 0.875rem; }
52
- .gy-toggle-track--md .gy-toggle-thumb { width: 1.0625rem; height: 1.0625rem; }
53
- .gy-toggle-track--lg .gy-toggle-thumb { width: 1.25rem; height: 1.25rem; }
54
-
55
- .gy-toggle-track--sm.gy-toggle-track--checked .gy-toggle-thumb { transform: translateY(-50%) translateX(calc(2rem - 0.875rem - 2px)); }
56
- .gy-toggle-track--md.gy-toggle-track--checked .gy-toggle-thumb { transform: translateY(-50%) translateX(calc(2.5rem - 1.0625rem - 2px)); }
57
- .gy-toggle-track--lg.gy-toggle-track--checked .gy-toggle-thumb { transform: translateY(-50%) translateX(calc(3rem - 1.25rem - 2px)); }
58
-
59
- .gy-toggle-label {
60
- font-size: var(--gy-font-size-sm);
61
- font-weight: var(--gy-font-weight-medium);
62
- color: var(--gy-text);
63
- }
@@ -1,58 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
- import './typography.css';
5
-
6
- export type TypographyVariant =
7
- | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
8
- | 'body-xl' | 'body-lg' | 'body-md' | 'body-sm' | 'body-xs'
9
- | 'caption-lg' | 'caption-md'
10
- | 'label' | 'code';
11
-
12
- export type TypographyColor =
13
- | 'default' | 'muted' | 'subtle' | 'disabled' | 'inverse'
14
- | 'primary' | 'success' | 'warning' | 'danger' | 'info';
15
-
16
- const variantTagMap: Record<TypographyVariant, React.ElementType> = {
17
- h1: 'h1', h2: 'h2', h3: 'h3', h4: 'h4', h5: 'h5', h6: 'h6',
18
- 'body-xl': 'p', 'body-lg': 'p', 'body-md': 'p', 'body-sm': 'p', 'body-xs': 'p',
19
- 'caption-lg': 'span', 'caption-md': 'span',
20
- label: 'label',
21
- code: 'code',
22
- };
23
-
24
- export interface TypographyProps {
25
- variant?: TypographyVariant;
26
- color?: TypographyColor;
27
- as?: React.ElementType;
28
- truncate?: boolean;
29
- className?: string;
30
- children?: React.ReactNode;
31
- id?: string;
32
- htmlFor?: string;
33
- style?: React.CSSProperties;
34
- }
35
-
36
- export function Typography({
37
- variant = 'body-md',
38
- color = 'default',
39
- as,
40
- truncate = false,
41
- className = '',
42
- children,
43
- ...rest
44
- }: TypographyProps) {
45
- const Tag = as ?? variantTagMap[variant];
46
-
47
- const classes = [
48
- 'gy-text',
49
- `gy-text--${variant}`,
50
- `gy-text--color-${color}`,
51
- truncate ? 'gy-text--truncate' : '',
52
- className,
53
- ]
54
- .filter(Boolean)
55
- .join(' ');
56
-
57
- return <Tag className={classes} {...rest}>{children}</Tag>;
58
- }
@@ -1,2 +0,0 @@
1
- export { Typography } from './Typography';
2
- export type { TypographyProps, TypographyVariant, TypographyColor } from './Typography';