@galyan/ui 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 (95) hide show
  1. package/.turbo/turbo-build.log +20 -0
  2. package/dist/index.cjs +2291 -0
  3. package/dist/index.css +2558 -0
  4. package/dist/index.d.cts +498 -0
  5. package/dist/index.d.ts +498 -0
  6. package/dist/index.js +2235 -0
  7. package/eslint.config.mjs +4 -0
  8. package/package.json +43 -0
  9. package/src/accordion/Accordion.tsx +88 -0
  10. package/src/accordion/accordion.css +78 -0
  11. package/src/accordion/index.ts +2 -0
  12. package/src/animatednumber/AnimatedNumber.tsx +71 -0
  13. package/src/animatednumber/index.ts +2 -0
  14. package/src/banner/Banner.tsx +84 -0
  15. package/src/banner/banner.css +56 -0
  16. package/src/banner/index.ts +2 -0
  17. package/src/breadcrumb/Breadcrumb.tsx +94 -0
  18. package/src/breadcrumb/breadcrumb.css +58 -0
  19. package/src/breadcrumb/index.ts +2 -0
  20. package/src/button/Button.tsx +98 -0
  21. package/src/button/button.css +190 -0
  22. package/src/button/index.ts +2 -0
  23. package/src/button.tsx +20 -0
  24. package/src/calendar/Calendar.tsx +190 -0
  25. package/src/calendar/calendar.css +142 -0
  26. package/src/calendar/index.ts +2 -0
  27. package/src/card/Card.tsx +88 -0
  28. package/src/card/card.css +115 -0
  29. package/src/card/index.ts +2 -0
  30. package/src/card.tsx +27 -0
  31. package/src/chart/Chart.tsx +186 -0
  32. package/src/chart/index.ts +2 -0
  33. package/src/checkbox/Checkbox.tsx +101 -0
  34. package/src/checkbox/checkbox.css +76 -0
  35. package/src/checkbox/index.ts +2 -0
  36. package/src/chips/Chips.tsx +146 -0
  37. package/src/chips/chips.css +90 -0
  38. package/src/chips/index.ts +2 -0
  39. package/src/code.tsx +11 -0
  40. package/src/datepicker/DatePicker.tsx +104 -0
  41. package/src/datepicker/datepicker.css +22 -0
  42. package/src/datepicker/index.ts +2 -0
  43. package/src/dragdrop/DragDrop.tsx +168 -0
  44. package/src/dragdrop/dragdrop.css +122 -0
  45. package/src/dragdrop/index.ts +2 -0
  46. package/src/dropdown/Dropdown.tsx +176 -0
  47. package/src/dropdown/dropdown.css +118 -0
  48. package/src/dropdown/index.ts +2 -0
  49. package/src/fileupload/FileUpload.tsx +144 -0
  50. package/src/fileupload/fileupload.css +118 -0
  51. package/src/fileupload/index.ts +2 -0
  52. package/src/index.ts +28 -0
  53. package/src/input/Input.tsx +121 -0
  54. package/src/input/index.ts +2 -0
  55. package/src/input/input.css +120 -0
  56. package/src/menu/Menu.tsx +118 -0
  57. package/src/menu/index.ts +2 -0
  58. package/src/menu/menu.css +117 -0
  59. package/src/modal/Modal.tsx +103 -0
  60. package/src/modal/index.ts +2 -0
  61. package/src/modal/modal.css +79 -0
  62. package/src/progressbar/ProgressBar.tsx +68 -0
  63. package/src/progressbar/index.ts +2 -0
  64. package/src/progressbar/progressbar.css +73 -0
  65. package/src/radiogroup/RadioGroup.tsx +73 -0
  66. package/src/radiogroup/index.ts +2 -0
  67. package/src/radiogroup/radiogroup.css +80 -0
  68. package/src/skeleton/Skeleton.tsx +61 -0
  69. package/src/skeleton/index.ts +2 -0
  70. package/src/skeleton/skeleton.css +46 -0
  71. package/src/spinner/Spinner.tsx +29 -0
  72. package/src/spinner/index.ts +2 -0
  73. package/src/spinner/spinner.css +39 -0
  74. package/src/stepper/Stepper.tsx +127 -0
  75. package/src/stepper/index.ts +2 -0
  76. package/src/stepper/stepper.css +141 -0
  77. package/src/tab/Tabs.tsx +71 -0
  78. package/src/tab/index.ts +2 -0
  79. package/src/tab/tab.css +92 -0
  80. package/src/table/Table.tsx +205 -0
  81. package/src/table/index.ts +2 -0
  82. package/src/table/table.css +119 -0
  83. package/src/textarea/Textarea.tsx +102 -0
  84. package/src/textarea/index.ts +2 -0
  85. package/src/textarea/textarea.css +85 -0
  86. package/src/toaster/Toaster.tsx +108 -0
  87. package/src/toaster/index.ts +2 -0
  88. package/src/toaster/toaster.css +113 -0
  89. package/src/toggle/Toggle.tsx +88 -0
  90. package/src/toggle/index.ts +2 -0
  91. package/src/toggle/toggle.css +63 -0
  92. package/src/typography/Typography.tsx +58 -0
  93. package/src/typography/index.ts +2 -0
  94. package/src/typography/typography.css +102 -0
  95. package/tsconfig.json +13 -0
@@ -0,0 +1,90 @@
1
+ /* ── Chips ────────────────────────────────────────────────────────────────── */
2
+ .gy-chip {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ gap: var(--gy-spacing-1-5);
6
+ padding: var(--gy-spacing-1) var(--gy-spacing-2-5);
7
+ font-size: var(--gy-font-size-xs);
8
+ font-weight: var(--gy-font-weight-medium);
9
+ border-radius: var(--gy-radius-full);
10
+ border: 1.5px solid transparent;
11
+ cursor: default;
12
+ transition:
13
+ background-color var(--gy-duration-fast),
14
+ border-color var(--gy-duration-fast);
15
+ white-space: nowrap;
16
+ user-select: none;
17
+ }
18
+
19
+ .gy-chip--sm { font-size: 0.65rem; padding: 2px var(--gy-spacing-2); }
20
+ .gy-chip--md { font-size: var(--gy-font-size-xs); padding: var(--gy-spacing-1) var(--gy-spacing-2-5); }
21
+ .gy-chip--lg { font-size: var(--gy-font-size-sm); padding: var(--gy-spacing-1-5) var(--gy-spacing-3); }
22
+
23
+ /* Variants */
24
+ .gy-chip--solid { background: var(--gy-primary); color: var(--gy-primary-fg); }
25
+ .gy-chip--soft { background: var(--gy-primary-subtle); color: var(--gy-primary-hover); border-color: var(--gy-primary-muted); }
26
+ .gy-chip--outline { background: transparent; color: var(--gy-primary); border-color: var(--gy-primary-muted); }
27
+
28
+ /* Status */
29
+ .gy-chip--success { background: var(--gy-success-subtle); color: var(--gy-success-emphasis); border-color: var(--gy-success-muted); }
30
+ .gy-chip--warning { background: var(--gy-warning-subtle); color: var(--gy-warning-emphasis); border-color: var(--gy-warning-muted); }
31
+ .gy-chip--danger { background: var(--gy-danger-subtle); color: var(--gy-danger); border-color: var(--gy-danger-muted); }
32
+ .gy-chip--neutral { background: var(--gy-background-muted); color: var(--gy-text-muted); border-color: var(--gy-border); }
33
+
34
+ .gy-chip--clickable { cursor: pointer; }
35
+ .gy-chip--clickable:hover { filter: brightness(0.95); }
36
+ .gy-chip--clickable:focus-visible { outline: 2px solid var(--gy-focus-outline); outline-offset: 2px; }
37
+
38
+ .gy-chip--selected.gy-chip--soft { background: var(--gy-primary-muted); }
39
+
40
+ .gy-chip__remove {
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ width: 14px;
45
+ height: 14px;
46
+ border-radius: 50%;
47
+ cursor: pointer;
48
+ opacity: 0.7;
49
+ transition: opacity var(--gy-duration-fast), background var(--gy-duration-fast);
50
+ flex-shrink: 0;
51
+ color: inherit;
52
+ background: none;
53
+ border: none;
54
+ padding: 0;
55
+ }
56
+
57
+ .gy-chip__remove:hover { opacity: 1; background: rgba(0,0,0,0.1); }
58
+
59
+ /* ── Chips Input (multi-select) ─────────────────────────────────────────── */
60
+ .gy-chips-input {
61
+ display: flex;
62
+ flex-wrap: wrap;
63
+ gap: var(--gy-spacing-1-5);
64
+ padding: var(--gy-spacing-2) var(--gy-spacing-3);
65
+ background: var(--gy-surface);
66
+ border: 1.5px solid var(--gy-border);
67
+ border-radius: var(--gy-radius-lg);
68
+ min-height: 2.5rem;
69
+ cursor: text;
70
+ transition: border-color var(--gy-duration-fast), box-shadow var(--gy-duration-fast);
71
+ }
72
+
73
+ .gy-chips-input:focus-within {
74
+ border-color: var(--gy-border-focus);
75
+ box-shadow: 0 0 0 3px var(--gy-focus-ring);
76
+ }
77
+
78
+ .gy-chips-input__field {
79
+ flex: 1;
80
+ min-width: 80px;
81
+ border: none;
82
+ outline: none;
83
+ background: transparent;
84
+ font-size: var(--gy-font-size-sm);
85
+ color: var(--gy-text);
86
+ font-family: var(--gy-font-sans);
87
+ padding: 2px 0;
88
+ }
89
+
90
+ .gy-chips-input__field::placeholder { color: var(--gy-text-subtle); }
@@ -0,0 +1,2 @@
1
+ export { Chip, ChipsInput } from './Chips';
2
+ export type { ChipProps, ChipsInputProps, ChipVariant, ChipSize } from './Chips';
package/src/code.tsx ADDED
@@ -0,0 +1,11 @@
1
+ import { type JSX } from "react";
2
+
3
+ export function Code({
4
+ children,
5
+ className,
6
+ }: {
7
+ children: React.ReactNode;
8
+ className?: string;
9
+ }): JSX.Element {
10
+ return <code className={className}>{children}</code>;
11
+ }
@@ -0,0 +1,104 @@
1
+ 'use client';
2
+
3
+ import React, { useEffect, useRef, useState, useId } from 'react';
4
+ import { Calendar, CalendarProps } from '../calendar/Calendar';
5
+ import { Input } from '../input/Input';
6
+ import './datepicker.css';
7
+
8
+ export interface DatePickerProps extends Omit<CalendarProps, 'className'> {
9
+ label?: string;
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ error?: string;
13
+ helperText?: string;
14
+ className?: string;
15
+ }
16
+
17
+ const formatDate = (date: Date) => {
18
+ return date.toLocaleDateString(undefined, {
19
+ year: 'numeric',
20
+ month: 'short',
21
+ day: 'numeric',
22
+ });
23
+ };
24
+
25
+ export function DatePicker({
26
+ mode = 'single',
27
+ value,
28
+ onChange,
29
+ minDate,
30
+ maxDate,
31
+ label,
32
+ placeholder = 'Select date',
33
+ disabled,
34
+ error,
35
+ helperText,
36
+ className = '',
37
+ }: DatePickerProps) {
38
+ const uid = useId();
39
+ const [open, setOpen] = useState(false);
40
+ const rootRef = useRef<HTMLDivElement>(null);
41
+
42
+ useEffect(() => {
43
+ const handler = (e: MouseEvent) => {
44
+ if (!rootRef.current?.contains(e.target as Node)) setOpen(false);
45
+ };
46
+ document.addEventListener('mousedown', handler);
47
+ return () => document.removeEventListener('mousedown', handler);
48
+ }, []);
49
+
50
+ let displayValue = '';
51
+ if (Array.isArray(value)) {
52
+ if (value[0] && value[1]) displayValue = `${formatDate(value[0])} - ${formatDate(value[1])}`;
53
+ else if (value[0]) displayValue = formatDate(value[0]);
54
+ } else if (value) {
55
+ displayValue = formatDate(value as Date);
56
+ }
57
+
58
+ const handleChange = (val: Date | [Date, Date]) => {
59
+ onChange?.(val);
60
+ if (mode === 'single' || (Array.isArray(val) && val[0] && val[1])) {
61
+ setOpen(false);
62
+ }
63
+ };
64
+
65
+ return (
66
+ <div ref={rootRef} className={`gy-datepicker ${className}`}>
67
+ <div onClick={() => !disabled && setOpen((o) => !o)} style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}>
68
+ <Input
69
+ id={`gy-datepicker-${uid}`}
70
+ label={label}
71
+ placeholder={placeholder}
72
+ value={displayValue}
73
+ readOnly
74
+ disabled={disabled}
75
+ error={error}
76
+ helperText={helperText}
77
+ style={{ cursor: disabled ? 'not-allowed' : 'pointer', pointerEvents: 'none' }}
78
+ rightIcon={
79
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
80
+ <rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
81
+ <line x1="16" y1="2" x2="16" y2="6" />
82
+ <line x1="8" y1="2" x2="8" y2="6" />
83
+ <line x1="3" y1="10" x2="21" y2="10" />
84
+ </svg>
85
+ }
86
+ />
87
+ </div>
88
+
89
+ {open && !disabled && (
90
+ <div className="gy-datepicker-popover">
91
+ <Calendar
92
+ mode={mode}
93
+ value={value}
94
+ onChange={handleChange}
95
+ minDate={minDate}
96
+ maxDate={maxDate}
97
+ className="gy-calendar--popover"
98
+ style={{ border: 'none', borderRadius: 0 }}
99
+ />
100
+ </div>
101
+ )}
102
+ </div>
103
+ );
104
+ }
@@ -0,0 +1,22 @@
1
+ /* ── DatePicker ──────────────────────────────────────────────────────────── */
2
+ .gy-datepicker {
3
+ position: relative;
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: var(--gy-spacing-1-5);
7
+ width: 100%;
8
+ }
9
+
10
+ .gy-datepicker-popover {
11
+ position: absolute;
12
+ top: calc(100% + 4px);
13
+ left: 0;
14
+ z-index: var(--gy-z-dropdown);
15
+ background: var(--gy-surface);
16
+ border: 1px solid var(--gy-border);
17
+ border-radius: var(--gy-radius-xl);
18
+ box-shadow: var(--gy-shadow-dropdown);
19
+ padding: 0;
20
+ overflow: hidden;
21
+ animation: gy-slide-in-up var(--gy-duration-fast) var(--gy-ease-out);
22
+ }
@@ -0,0 +1,2 @@
1
+ export { DatePicker } from './DatePicker';
2
+ export type { DatePickerProps } from './DatePicker';
@@ -0,0 +1,168 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useRef } from 'react';
4
+ import './dragdrop.css';
5
+
6
+ // ── Reorderable List ────────────────────────────────────────────────────────
7
+ export interface ReorderListProps<T> {
8
+ items: T[];
9
+ onReorder: (newItems: T[]) => void;
10
+ keyExtractor: (item: T) => string;
11
+ renderItem: (item: T, isDragging: boolean) => React.ReactNode;
12
+ className?: string;
13
+ }
14
+
15
+ export function ReorderList<T>({ items, onReorder, keyExtractor, renderItem, className = '' }: ReorderListProps<T>) {
16
+ const [draggedIdx, setDraggedIdx] = useState<number | null>(null);
17
+ const [dragOverIdx, setDragOverIdx] = useState<number | null>(null);
18
+
19
+ const handleDragStart = (idx: number, e: React.DragEvent) => {
20
+ setDraggedIdx(idx);
21
+ e.dataTransfer.effectAllowed = 'move';
22
+ e.dataTransfer.setData('text/plain', idx.toString());
23
+ };
24
+
25
+ const handleDragOver = (idx: number, e: React.DragEvent) => {
26
+ e.preventDefault();
27
+ e.dataTransfer.dropEffect = 'move';
28
+ if (idx !== dragOverIdx) setDragOverIdx(idx);
29
+ };
30
+
31
+ const handleDrop = (idx: number, e: React.DragEvent) => {
32
+ e.preventDefault();
33
+ const fromIdx = Number(e.dataTransfer.getData('text/plain'));
34
+ if (fromIdx !== idx) {
35
+ const next = [...items];
36
+ const [moved] = next.splice(fromIdx, 1);
37
+ if (moved !== undefined) {
38
+ next.splice(idx, 0, moved);
39
+ onReorder(next);
40
+ }
41
+ }
42
+ setDraggedIdx(null);
43
+ setDragOverIdx(null);
44
+ };
45
+
46
+ return (
47
+ <div className={`gy-dnd-list ${className}`}>
48
+ {items.map((item, idx) => {
49
+ const isDragging = draggedIdx === idx;
50
+ const isOver = dragOverIdx === idx;
51
+ return (
52
+ <div
53
+ key={keyExtractor(item)}
54
+ className={[
55
+ 'gy-dnd-item',
56
+ isDragging ? 'gy-dnd-item--dragging' : '',
57
+ isOver && !isDragging ? 'gy-dnd-item--drag-over' : '',
58
+ ]
59
+ .filter(Boolean)
60
+ .join(' ')}
61
+ draggable
62
+ onDragStart={(e) => handleDragStart(idx, e)}
63
+ onDragOver={(e) => handleDragOver(idx, e)}
64
+ onDragLeave={() => setDragOverIdx(null)}
65
+ onDrop={(e) => handleDrop(idx, e)}
66
+ onDragEnd={() => { setDraggedIdx(null); setDragOverIdx(null); }}
67
+ >
68
+ <div className="gy-dnd-handle" aria-hidden="true">
69
+ <svg width="12" height="16" viewBox="0 0 12 16" fill="currentColor">
70
+ <circle cx="4" cy="4" r="1.5" /><circle cx="8" cy="4" r="1.5" />
71
+ <circle cx="4" cy="8" r="1.5" /><circle cx="8" cy="8" r="1.5" />
72
+ <circle cx="4" cy="12" r="1.5" /><circle cx="8" cy="12" r="1.5" />
73
+ </svg>
74
+ </div>
75
+ <div className="gy-dnd-item-content">
76
+ {renderItem(item, isDragging)}
77
+ </div>
78
+ </div>
79
+ );
80
+ })}
81
+ </div>
82
+ );
83
+ }
84
+
85
+ // ── Kanban Board ─────────────────────────────────────────────────────────────
86
+ export interface KanbanColumnDef<T> {
87
+ id: string;
88
+ title: string;
89
+ items: T[];
90
+ }
91
+
92
+ export interface KanbanBoardProps<T> {
93
+ columns: KanbanColumnDef<T>[];
94
+ onMove: (itemKey: string, fromCol: string, toCol: string, toIndex: number) => void;
95
+ keyExtractor: (item: T) => string;
96
+ renderCard: (item: T, isDragging: boolean) => React.ReactNode;
97
+ className?: string;
98
+ }
99
+
100
+ export function KanbanBoard<T>({ columns, onMove, keyExtractor, renderCard, className = '' }: KanbanBoardProps<T>) {
101
+ const [dragged, setDragged] = useState<{ key: string; colId: string; idx: number } | null>(null);
102
+ const [overCol, setOverCol] = useState<string | null>(null);
103
+
104
+ const handleDragStart = (e: React.DragEvent, key: string, colId: string, idx: number) => {
105
+ setDragged({ key, colId, idx });
106
+ e.dataTransfer.effectAllowed = 'move';
107
+ e.dataTransfer.setData('application/json', JSON.stringify({ key, colId }));
108
+ };
109
+
110
+ const handleDragOver = (e: React.DragEvent, colId: string) => {
111
+ e.preventDefault();
112
+ e.dataTransfer.dropEffect = 'move';
113
+ if (overCol !== colId) setOverCol(colId);
114
+ };
115
+
116
+ const handleDrop = (e: React.DragEvent, toCol: string, toIndex: number) => {
117
+ e.preventDefault();
118
+ setOverCol(null);
119
+ try {
120
+ const data = JSON.parse(e.dataTransfer.getData('application/json'));
121
+ if (data.colId === toCol && data.key === dragged?.key && toIndex === dragged?.idx) {
122
+ // dropped in same place
123
+ return;
124
+ }
125
+ onMove(data.key, data.colId, toCol, toIndex);
126
+ } catch (err) {}
127
+ setDragged(null);
128
+ };
129
+
130
+ return (
131
+ <div className={`gy-kanban ${className}`}>
132
+ {columns.map((col) => (
133
+ <div key={col.id} className="gy-kanban-column">
134
+ <div className="gy-kanban-column-header">
135
+ <span className="gy-kanban-column-title">{col.title}</span>
136
+ <span className="gy-kanban-column-count">{col.items.length}</span>
137
+ </div>
138
+ <div
139
+ className={`gy-kanban-drop-zone ${overCol === col.id ? 'gy-kanban-drop-zone--over' : ''}`}
140
+ onDragOver={(e) => handleDragOver(e, col.id)}
141
+ onDragLeave={() => setOverCol(null)}
142
+ onDrop={(e) => handleDrop(e, col.id, col.items.length)}
143
+ >
144
+ {col.items.map((item, idx) => {
145
+ const key = keyExtractor(item);
146
+ const isDragging = dragged?.key === key;
147
+ return (
148
+ <div
149
+ key={key}
150
+ className={`gy-kanban-card ${isDragging ? 'gy-kanban-card--dragging' : ''}`}
151
+ draggable
152
+ onDragStart={(e) => handleDragStart(e, key, col.id, idx)}
153
+ onDragEnd={() => { setDragged(null); setOverCol(null); }}
154
+ onDrop={(e) => {
155
+ e.stopPropagation();
156
+ handleDrop(e, col.id, idx);
157
+ }}
158
+ >
159
+ {renderCard(item, isDragging)}
160
+ </div>
161
+ );
162
+ })}
163
+ </div>
164
+ </div>
165
+ ))}
166
+ </div>
167
+ );
168
+ }
@@ -0,0 +1,122 @@
1
+ /* ── Drag & Drop ─────────────────────────────────────────────────────────── */
2
+
3
+ /* ── List Reorder ── */
4
+ .gy-dnd-list {
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: var(--gy-spacing-2);
8
+ width: 100%;
9
+ }
10
+
11
+ .gy-dnd-item {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--gy-spacing-3);
15
+ padding: var(--gy-spacing-3) var(--gy-spacing-4);
16
+ background: var(--gy-surface);
17
+ border: 1.5px solid var(--gy-border);
18
+ border-radius: var(--gy-radius-lg);
19
+ transition: box-shadow var(--gy-duration-fast), border-color var(--gy-duration-fast), opacity var(--gy-duration-fast);
20
+ cursor: default;
21
+ user-select: none;
22
+ }
23
+
24
+ .gy-dnd-item--dragging {
25
+ opacity: 0.4;
26
+ border-color: var(--gy-primary);
27
+ border-style: dashed;
28
+ }
29
+
30
+ .gy-dnd-item--drag-over {
31
+ border-color: var(--gy-primary);
32
+ background: var(--gy-primary-subtle);
33
+ box-shadow: 0 0 0 3px var(--gy-focus-ring);
34
+ }
35
+
36
+ .gy-dnd-handle {
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ color: var(--gy-text-subtle);
41
+ cursor: grab;
42
+ flex-shrink: 0;
43
+ padding: 2px;
44
+ border-radius: var(--gy-radius-sm);
45
+ transition: color var(--gy-duration-fast);
46
+ }
47
+
48
+ .gy-dnd-handle:hover { color: var(--gy-text-muted); }
49
+ .gy-dnd-handle:active { cursor: grabbing; }
50
+
51
+ .gy-dnd-item-content { flex: 1; min-width: 0; }
52
+
53
+ /* ── Kanban Board ── */
54
+ .gy-kanban {
55
+ display: flex;
56
+ gap: var(--gy-spacing-4);
57
+ overflow-x: auto;
58
+ padding-bottom: var(--gy-spacing-4);
59
+ align-items: flex-start;
60
+ min-height: 200px;
61
+ }
62
+
63
+ .gy-kanban-column {
64
+ flex-shrink: 0;
65
+ width: 280px;
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: var(--gy-spacing-3);
69
+ }
70
+
71
+ .gy-kanban-column-header {
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: space-between;
75
+ padding: var(--gy-spacing-2) var(--gy-spacing-3);
76
+ background: var(--gy-background-muted);
77
+ border-radius: var(--gy-radius-lg);
78
+ }
79
+
80
+ .gy-kanban-column-title {
81
+ font-size: var(--gy-font-size-sm);
82
+ font-weight: var(--gy-font-weight-semibold);
83
+ color: var(--gy-text);
84
+ }
85
+
86
+ .gy-kanban-column-count {
87
+ font-size: var(--gy-font-size-xs);
88
+ font-weight: var(--gy-font-weight-semibold);
89
+ color: var(--gy-text-subtle);
90
+ background: var(--gy-surface);
91
+ padding: 1px var(--gy-spacing-2);
92
+ border-radius: var(--gy-radius-full);
93
+ }
94
+
95
+ .gy-kanban-drop-zone {
96
+ flex: 1;
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: var(--gy-spacing-2);
100
+ min-height: 80px;
101
+ border-radius: var(--gy-radius-lg);
102
+ padding: var(--gy-spacing-1);
103
+ transition: background-color var(--gy-duration-fast), border-color var(--gy-duration-fast);
104
+ }
105
+
106
+ .gy-kanban-drop-zone--over {
107
+ background: var(--gy-primary-subtle);
108
+ border: 2px dashed var(--gy-primary);
109
+ }
110
+
111
+ .gy-kanban-card {
112
+ background: var(--gy-surface);
113
+ border: 1px solid var(--gy-border);
114
+ border-radius: var(--gy-radius-lg);
115
+ padding: var(--gy-spacing-3);
116
+ cursor: grab;
117
+ transition: box-shadow var(--gy-duration-fast), opacity var(--gy-duration-fast);
118
+ user-select: none;
119
+ }
120
+
121
+ .gy-kanban-card:hover { box-shadow: var(--gy-shadow-md); }
122
+ .gy-kanban-card--dragging { opacity: 0.4; cursor: grabbing; }
@@ -0,0 +1,2 @@
1
+ export { ReorderList, KanbanBoard } from './DragDrop';
2
+ export type { ReorderListProps, KanbanBoardProps, KanbanColumnDef } from './DragDrop';