@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,146 +0,0 @@
1
- 'use client';
2
-
3
- import React, { useRef, useState } from 'react';
4
- import './chips.css';
5
-
6
- export type ChipVariant = 'solid' | 'soft' | 'outline' | 'success' | 'warning' | 'danger' | 'neutral';
7
- export type ChipSize = 'sm' | 'md' | 'lg';
8
-
9
- export interface ChipProps {
10
- children: React.ReactNode;
11
- variant?: ChipVariant;
12
- size?: ChipSize;
13
- removable?: boolean;
14
- onRemove?: () => void;
15
- clickable?: boolean;
16
- selected?: boolean;
17
- onClick?: () => void;
18
- icon?: React.ReactNode;
19
- className?: string;
20
- }
21
-
22
- export function Chip({
23
- children,
24
- variant = 'soft',
25
- size = 'md',
26
- removable = false,
27
- onRemove,
28
- clickable = false,
29
- selected = false,
30
- onClick,
31
- icon,
32
- className = '',
33
- }: ChipProps) {
34
- return (
35
- <span
36
- className={[
37
- 'gy-chip',
38
- `gy-chip--${variant}`,
39
- `gy-chip--${size}`,
40
- clickable ? 'gy-chip--clickable' : '',
41
- selected ? 'gy-chip--selected' : '',
42
- className,
43
- ]
44
- .filter(Boolean)
45
- .join(' ')}
46
- role={clickable ? 'button' : undefined}
47
- tabIndex={clickable ? 0 : undefined}
48
- onClick={clickable ? onClick : undefined}
49
- onKeyDown={clickable ? (e) => { if (e.key === 'Enter' || e.key === ' ') onClick?.(); } : undefined}
50
- >
51
- {icon && <span aria-hidden="true">{icon}</span>}
52
- {children}
53
- {removable && (
54
- <button
55
- type="button"
56
- className="gy-chip__remove"
57
- onClick={(e) => { e.stopPropagation(); onRemove?.(); }}
58
- aria-label="Remove"
59
- >
60
- <svg width="8" height="8" viewBox="0 0 8 8" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round">
61
- <line x1="1" y1="1" x2="7" y2="7" />
62
- <line x1="7" y1="1" x2="1" y2="7" />
63
- </svg>
64
- </button>
65
- )}
66
- </span>
67
- );
68
- }
69
-
70
- // ── ChipsInput ────────────────────────────────────────────────────────────────
71
- export interface ChipsInputProps {
72
- values: string[];
73
- onChange: (values: string[]) => void;
74
- placeholder?: string;
75
- disabled?: boolean;
76
- maxItems?: number;
77
- chipVariant?: ChipVariant;
78
- className?: string;
79
- }
80
-
81
- export function ChipsInput({
82
- values,
83
- onChange,
84
- placeholder = 'Add a tag...',
85
- disabled = false,
86
- maxItems,
87
- chipVariant = 'soft',
88
- className = '',
89
- }: ChipsInputProps) {
90
- const [input, setInput] = useState('');
91
- const inputRef = useRef<HTMLInputElement>(null);
92
-
93
- const addChip = (val: string) => {
94
- const trimmed = val.trim();
95
- if (!trimmed || values.includes(trimmed)) return;
96
- if (maxItems && values.length >= maxItems) return;
97
- onChange([...values, trimmed]);
98
- setInput('');
99
- };
100
-
101
- const removeChip = (idx: number) => {
102
- onChange(values.filter((_, i) => i !== idx));
103
- };
104
-
105
- const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
106
- if (e.key === 'Enter' || e.key === ',') {
107
- e.preventDefault();
108
- addChip(input);
109
- }
110
- if (e.key === 'Backspace' && !input && values.length > 0) {
111
- removeChip(values.length - 1);
112
- }
113
- };
114
-
115
- return (
116
- <div
117
- className={`gy-chips-input ${className}`}
118
- onClick={() => inputRef.current?.focus()}
119
- >
120
- {values.map((val, i) => (
121
- <Chip
122
- key={i}
123
- variant={chipVariant}
124
- size="sm"
125
- removable={!disabled}
126
- onRemove={() => removeChip(i)}
127
- >
128
- {val}
129
- </Chip>
130
- ))}
131
- {(!maxItems || values.length < maxItems) && (
132
- <input
133
- ref={inputRef}
134
- className="gy-chips-input__field"
135
- value={input}
136
- onChange={(e) => setInput(e.target.value)}
137
- onKeyDown={handleKeyDown}
138
- onBlur={() => addChip(input)}
139
- placeholder={values.length === 0 ? placeholder : ''}
140
- disabled={disabled}
141
- aria-label={placeholder}
142
- />
143
- )}
144
- </div>
145
- );
146
- }
@@ -1,90 +0,0 @@
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); }
@@ -1,2 +0,0 @@
1
- export { Chip, ChipsInput } from './Chips';
2
- export type { ChipProps, ChipsInputProps, ChipVariant, ChipSize } from './Chips';
package/src/code.tsx DELETED
@@ -1,11 +0,0 @@
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
- }
@@ -1,104 +0,0 @@
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
- }
@@ -1,22 +0,0 @@
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
- }
@@ -1,2 +0,0 @@
1
- export { DatePicker } from './DatePicker';
2
- export type { DatePickerProps } from './DatePicker';
@@ -1,168 +0,0 @@
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
- }
@@ -1,122 +0,0 @@
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; }
@@ -1,2 +0,0 @@
1
- export { ReorderList, KanbanBoard } from './DragDrop';
2
- export type { ReorderListProps, KanbanBoardProps, KanbanColumnDef } from './DragDrop';