elementdrawing 1.0.0

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/dist/elementdrawing.min.js +3 -0
  3. package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
  4. package/dist/elementdrawing.min.js.map +1 -0
  5. package/dist/index.html +1 -0
  6. package/package.json +127 -0
  7. package/src/core/bridge.h +855 -0
  8. package/src/core/diff.c +900 -0
  9. package/src/core/element.c +1078 -0
  10. package/src/core/event.c +813 -0
  11. package/src/core/fiber.c +1027 -0
  12. package/src/core/hooks.c +919 -0
  13. package/src/core/renderer.c +963 -0
  14. package/src/core/scheduler.c +702 -0
  15. package/src/core/state.c +803 -0
  16. package/src/css/animations.css +779 -0
  17. package/src/css/base.css +615 -0
  18. package/src/css/components.css +1311 -0
  19. package/src/css/tailwind.css +370 -0
  20. package/src/css/themes.css +517 -0
  21. package/src/css/utilities.css +475 -0
  22. package/src/index.js +746 -0
  23. package/src/js/animation.js +655 -0
  24. package/src/js/dom.js +665 -0
  25. package/src/js/events.js +585 -0
  26. package/src/js/http.js +446 -0
  27. package/src/js/index.js +26 -0
  28. package/src/js/router.js +483 -0
  29. package/src/js/store.js +539 -0
  30. package/src/js/utils.js +593 -0
  31. package/src/js/validator.js +529 -0
  32. package/src/jsx/components/Accordion.jsx +210 -0
  33. package/src/jsx/components/Alert.jsx +169 -0
  34. package/src/jsx/components/Avatar.jsx +214 -0
  35. package/src/jsx/components/Badge.jsx +136 -0
  36. package/src/jsx/components/Breadcrumb.jsx +200 -0
  37. package/src/jsx/components/Button.jsx +188 -0
  38. package/src/jsx/components/Card.jsx +192 -0
  39. package/src/jsx/components/Carousel.jsx +278 -0
  40. package/src/jsx/components/Checkbox.jsx +215 -0
  41. package/src/jsx/components/Dialog.jsx +242 -0
  42. package/src/jsx/components/Drawer.jsx +190 -0
  43. package/src/jsx/components/Dropdown.jsx +268 -0
  44. package/src/jsx/components/Form.jsx +274 -0
  45. package/src/jsx/components/Input.jsx +285 -0
  46. package/src/jsx/components/Menu.jsx +276 -0
  47. package/src/jsx/components/Modal.jsx +274 -0
  48. package/src/jsx/components/Navbar.jsx +292 -0
  49. package/src/jsx/components/Pagination.jsx +268 -0
  50. package/src/jsx/components/Progress.jsx +252 -0
  51. package/src/jsx/components/Radio.jsx +208 -0
  52. package/src/jsx/components/Select.jsx +397 -0
  53. package/src/jsx/components/Sidebar.jsx +250 -0
  54. package/src/jsx/components/Slider.jsx +310 -0
  55. package/src/jsx/components/Spinner.jsx +198 -0
  56. package/src/jsx/components/Switch.jsx +201 -0
  57. package/src/jsx/components/Table.jsx +332 -0
  58. package/src/jsx/components/Tabs.jsx +227 -0
  59. package/src/jsx/components/Textarea.jsx +212 -0
  60. package/src/jsx/components/Toast.jsx +270 -0
  61. package/src/jsx/components/Tooltip.jsx +178 -0
  62. package/src/jsx/components/Typography.jsx +299 -0
  63. package/src/jsx/components/index.jsx +70 -0
  64. package/src/jsx/core/element.js +3 -0
  65. package/src/jsx/hooks/index.js +356 -0
  66. package/src/jsx/hooks/useCallback.js +472 -0
  67. package/src/jsx/hooks/useContext.js +586 -0
  68. package/src/jsx/hooks/useEffect.js +704 -0
  69. package/src/jsx/hooks/useLayoutEffect.js +508 -0
  70. package/src/jsx/hooks/useMemo.js +689 -0
  71. package/src/jsx/hooks/useReducer.js +729 -0
  72. package/src/jsx/hooks/useRef.js +542 -0
  73. package/src/jsx/hooks/useState.js +854 -0
  74. package/src/jsx/runtime/commit.js +903 -0
  75. package/src/jsx/runtime/createElement.js +860 -0
  76. package/src/jsx/runtime/index.js +356 -0
  77. package/src/jsx/runtime/reconcile.js +687 -0
  78. package/src/jsx/runtime/render.js +914 -0
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Table Component for ElementDrawing Framework
3
+ * Supports sorting, pagination, selection, filtering, striped, bordered, compact, loading, empty, expandable, fixed header
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ function Table(props) {
8
+ const {
9
+ data = [],
10
+ columns = [],
11
+ loading = false,
12
+ striped = false,
13
+ bordered = false,
14
+ compact = false,
15
+ hoverable = true,
16
+ selectable = false,
17
+ selectedRowKeys = [],
18
+ onSelectionChange,
19
+ onRowClick,
20
+ onRowExpand,
21
+ expandedRowKeys = [],
22
+ expandable = false,
23
+ expandedRowRender,
24
+ sortField,
25
+ sortOrder,
26
+ onSort,
27
+ pagination = false,
28
+ pageSize = 10,
29
+ currentPage = 1,
30
+ total = 0,
31
+ onPageChange,
32
+ onPageSizeChange,
33
+ showPagination = true,
34
+ pageSizeOptions = [10, 20, 50, 100],
35
+ emptyText = 'No data',
36
+ emptyIcon,
37
+ rowKey = 'id',
38
+ className = '',
39
+ style = {},
40
+ fixedHeader = false,
41
+ maxHeight,
42
+ footer,
43
+ title,
44
+ size = 'default',
45
+ showHeader = true,
46
+ scrollX,
47
+ rowClassName,
48
+ onFilter,
49
+ filters,
50
+ } = props;
51
+
52
+ const sizeClasses = {
53
+ small: 'ed-text-xs',
54
+ default: 'ed-text-sm',
55
+ large: 'ed-text-base',
56
+ };
57
+
58
+ const cellPadding = {
59
+ small: 'ed-px-3 ed-py-1.5',
60
+ default: 'ed-px-4 ed-py-3',
61
+ large: 'ed-px-5 ed-py-4',
62
+ };
63
+
64
+ const isAllSelected = data.length > 0 && selectedRowKeys.length === data.length;
65
+ const isIndeterminate = selectedRowKeys.length > 0 && selectedRowKeys.length < data.length;
66
+
67
+ const handleSelectAll = () => {
68
+ if (isAllSelected) {
69
+ onSelectionChange?.([]);
70
+ } else {
71
+ onSelectionChange?.(data.map(row => row[rowKey]));
72
+ }
73
+ };
74
+
75
+ const handleSelectRow = (row) => {
76
+ const key = row[rowKey];
77
+ const newSelected = selectedRowKeys.includes(key)
78
+ ? selectedRowKeys.filter(k => k !== key)
79
+ : [...selectedRowKeys, key];
80
+ onSelectionChange?.(newSelected);
81
+ };
82
+
83
+ const handleSort = (column) => {
84
+ if (!column.sortable) return;
85
+ let newOrder = 'asc';
86
+ if (sortField === column.key && sortOrder === 'asc') newOrder = 'desc';
87
+ if (sortField === column.key && sortOrder === 'desc') newOrder = null;
88
+ onSort?.(column.key, newOrder);
89
+ };
90
+
91
+ const sortIcon = (column) => {
92
+ if (!column.sortable) return null;
93
+ const isActive = sortField === column.key;
94
+ return ED.createElement('span', { className: 'ed-inline-flex ed-flex-col ed-ml-1 ed-leading-none' },
95
+ ED.createElement('svg', {
96
+ className: `ed-w-3 ed-h-3 ${isActive && sortOrder === 'asc' ? 'ed-text-blue-600' : 'ed-text-gray-400'}`,
97
+ viewBox: '0 0 12 12', fill: 'currentColor',
98
+ children: ED.createElement('path', { d: 'M6 2l4 4H2z' }),
99
+ }),
100
+ ED.createElement('svg', {
101
+ className: `ed-w-3 ed-h-3 ${isActive && sortOrder === 'desc' ? 'ed-text-blue-600' : 'ed-text-gray-400'}`,
102
+ viewBox: '0 0 12 12', fill: 'currentColor',
103
+ children: ED.createElement('path', { d: 'M6 10L2 6h8z' }),
104
+ })
105
+ );
106
+ };
107
+
108
+ const checkboxCell = (checked, onChange) =>
109
+ ED.createElement('input', {
110
+ type: 'checkbox',
111
+ checked,
112
+ onChange,
113
+ className: 'ed-w-4 ed-h-4 ed-text-blue-600 ed-rounded ed-border-gray-300 focus:ed-ring-blue-500 ed-cursor-pointer',
114
+ });
115
+
116
+ const expandCell = (row) => {
117
+ const key = row[rowKey];
118
+ const isExpanded = expandedRowKeys.includes(key);
119
+ return ED.createElement('button', {
120
+ className: 'ed-w-6 ed-h-6 ed-flex ed-items-center ed-justify-center ed-rounded ed-text-gray-500 hover:ed-bg-gray-100 ed-transition-transform',
121
+ onClick: (e) => { e.stopPropagation(); onRowExpand?.(key, !isExpanded); },
122
+ style: { transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)' },
123
+ children: ED.createElement('svg', {
124
+ className: 'ed-w-4 ed-h-4', fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
125
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M9 5l7 7-7 7' }),
126
+ }),
127
+ });
128
+ };
129
+
130
+ const headerRow = ED.createElement('tr', {
131
+ className: 'ed-bg-gray-50 ed-border-b ed-border-gray-200',
132
+ children: [
133
+ expandable ? ED.createElement('th', { key: 'expand', className: `${cellPadding[size || 'default']} ed-w-10` }) : null,
134
+ selectable ? ED.createElement('th', { key: 'select', className: `${cellPadding[size || 'default']} ed-w-10` },
135
+ checkboxCell(isAllSelected, handleSelectAll)
136
+ ) : null,
137
+ ...columns.map((col, idx) =>
138
+ ED.createElement('th', {
139
+ key: col.key || idx,
140
+ className: [
141
+ cellPadding[size || 'default'],
142
+ 'ed-font-semibold ed-text-gray-700 ed-text-left ed-whitespace-nowrap',
143
+ col.sortable ? 'ed-cursor-pointer hover:ed-bg-gray-100 ed-select-none' : '',
144
+ col.align === 'center' ? 'ed-text-center' : col.align === 'right' ? 'ed-text-right' : '',
145
+ col.className || '',
146
+ ].filter(Boolean).join(' '),
147
+ style: col.width ? { width: col.width } : {},
148
+ onClick: col.sortable ? () => handleSort(col) : undefined,
149
+ children: [
150
+ ED.createElement('span', null, col.title || col.header),
151
+ col.sortable ? sortIcon(col) : null,
152
+ ],
153
+ })
154
+ ),
155
+ ].filter(Boolean),
156
+ });
157
+
158
+ const renderRow = (row, rowIndex) => {
159
+ const key = row[rowKey] || rowIndex;
160
+ const isSelected = selectedRowKeys.includes(key);
161
+ const isExpanded = expandedRowKeys.includes(key);
162
+
163
+ const rowClasses = [
164
+ striped && rowIndex % 2 === 1 ? 'ed-bg-gray-50' : 'ed-bg-white',
165
+ hoverable ? 'hover:ed-bg-blue-50' : '',
166
+ isSelected ? 'ed-bg-blue-50' : '',
167
+ onRowClick ? 'ed-cursor-pointer' : '',
168
+ bordered ? 'ed-border-b ed-border-gray-200' : 'ed-border-b ed-border-gray-100',
169
+ typeof rowClassName === 'function' ? rowClassName(row, rowIndex) : rowClassName || '',
170
+ ].filter(Boolean).join(' ');
171
+
172
+ const mainRow = ED.createElement('tr', {
173
+ key,
174
+ className: rowClasses,
175
+ onClick: (e) => onRowClick?.(row, rowIndex, e),
176
+ children: [
177
+ expandable ? ED.createElement('td', { key: 'expand', className: cellPadding[size || 'default'] }, expandCell(row)) : null,
178
+ selectable ? ED.createElement('td', { key: 'select', className: cellPadding[size || 'default'] },
179
+ checkboxCell(isSelected, () => handleSelectRow(row))
180
+ ) : null,
181
+ ...columns.map((col, colIdx) => {
182
+ const cellValue = col.render ? col.render(row[col.key], row, rowIndex) : row[col.key];
183
+ return ED.createElement('td', {
184
+ key: col.key || colIdx,
185
+ className: [
186
+ cellPadding[size || 'default'],
187
+ col.align === 'center' ? 'ed-text-center' : col.align === 'right' ? 'ed-text-right' : '',
188
+ col.ellipsis ? 'ed-truncate ed-max-w-xs' : '',
189
+ 'ed-text-gray-700',
190
+ ].filter(Boolean).join(' '),
191
+ style: col.width ? { width: col.width } : {},
192
+ children: cellValue,
193
+ });
194
+ }),
195
+ ].filter(Boolean),
196
+ });
197
+
198
+ const expandedRow = expandable && isExpanded && expandedRowRender
199
+ ? ED.createElement('tr', {
200
+ key: `${key}-expanded`,
201
+ className: 'ed-bg-gray-50',
202
+ children: ED.createElement('td', {
203
+ colSpan: (selectable ? 1 : 0) + (expandable ? 1 : 0) + columns.length,
204
+ className: 'ed-px-4 ed-py-4',
205
+ children: expandedRowRender(row, rowIndex),
206
+ }),
207
+ })
208
+ : null;
209
+
210
+ return [mainRow, expandedRow].filter(Boolean);
211
+ };
212
+
213
+ const emptyState = data.length === 0
214
+ ? ED.createElement('tr', null,
215
+ ED.createElement('td', {
216
+ colSpan: (selectable ? 1 : 0) + (expandable ? 1 : 0) + columns.length,
217
+ className: 'ed-px-4 ed-py-12 ed-text-center ed-text-gray-500',
218
+ children: ED.createElement('div', { className: 'ed-flex ed-flex-col ed-items-center ed-gap-2' },
219
+ emptyIcon || ED.createElement('svg', {
220
+ className: 'ed-w-12 ed-h-12 ed-text-gray-300',
221
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
222
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 1.5, d: 'M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4' }),
223
+ }),
224
+ ED.createElement('span', { className: 'ed-text-sm' }, emptyText),
225
+ ),
226
+ })
227
+ )
228
+ : null;
229
+
230
+ const loadingOverlay = loading
231
+ ? ED.createElement('div', {
232
+ className: 'ed-absolute ed-inset-0 ed-bg-white ed-bg-opacity-70 ed-flex ed-items-center ed-justify-center ed-z-10',
233
+ children: ED.createElement('div', {
234
+ className: 'ed-w-8 ed-h-8 ed-border-4 ed-border-blue-500 ed-border-t-transparent ed-rounded-full ed-animate-spin',
235
+ }),
236
+ })
237
+ : null;
238
+
239
+ const paginationElement = pagination && total > pageSize
240
+ ? ED.createElement('div', {
241
+ className: 'ed-flex ed-items-center ed-justify-between ed-px-4 ed-py-3 ed-border-t ed-border-gray-200',
242
+ children: [
243
+ ED.createElement('span', { className: 'ed-text-sm ed-text-gray-500', key: 'info' },
244
+ `Showing ${(currentPage - 1) * pageSize + 1}-${Math.min(currentPage * pageSize, total)} of ${total}`
245
+ ),
246
+ ED.createElement('div', { className: 'ed-flex ed-items-center ed-gap-2', key: 'controls' },
247
+ ED.createElement('button', {
248
+ className: 'ed-px-3 ed-py-1 ed-text-sm ed-border ed-rounded ed-disabled:ed-opacity-50 ed-disabled:ed-cursor-not-allowed hover:ed-bg-gray-50',
249
+ disabled: currentPage <= 1,
250
+ onClick: () => onPageChange?.(currentPage - 1),
251
+ }, 'Previous'),
252
+ ...Array.from({ length: Math.ceil(total / pageSize) }, (_, i) => i + 1)
253
+ .filter(p => Math.abs(p - currentPage) <= 2 || p === 1 || p === Math.ceil(total / pageSize))
254
+ .map((page, idx, arr) => {
255
+ const prev = arr[idx - 1];
256
+ const showEllipsis = prev && page - prev > 1;
257
+ return [
258
+ showEllipsis ? ED.createElement('span', { key: `ellipsis-${idx}`, className: 'ed-px-1' }, '...') : null,
259
+ ED.createElement('button', {
260
+ key: page,
261
+ className: [
262
+ 'ed-px-3 ed-py-1 ed-text-sm ed-border ed-rounded ed-transition-colors',
263
+ page === currentPage
264
+ ? 'ed-bg-blue-600 ed-text-white ed-border-blue-600'
265
+ : 'ed-border-gray-300 hover:ed-bg-gray-50',
266
+ ].join(' '),
267
+ onClick: () => onPageChange?.(page),
268
+ }, page),
269
+ ];
270
+ }).flat().filter(Boolean),
271
+ ED.createElement('button', {
272
+ className: 'ed-px-3 ed-py-1 ed-text-sm ed-border ed-rounded ed-disabled:ed-opacity-50 ed-disabled:ed-cursor-not-allowed hover:ed-bg-gray-50',
273
+ disabled: currentPage >= Math.ceil(total / pageSize),
274
+ onClick: () => onPageChange?.(currentPage + 1),
275
+ }, 'Next'),
276
+ ),
277
+ ],
278
+ })
279
+ : null;
280
+
281
+ const tableClasses = [
282
+ 'ed-w-full ed-border-collapse',
283
+ sizeClasses[size] || sizeClasses.default,
284
+ className,
285
+ ].filter(Boolean).join(' ');
286
+
287
+ const wrapperClasses = [
288
+ 'ed-relative ed-bg-white ed-rounded-lg ed-overflow-hidden ed-border ed-border-gray-200',
289
+ bordered ? 'ed-border' : 'ed-border ed-border-gray-200',
290
+ ].filter(Boolean).join(' ');
291
+
292
+ return ED.createElement('div', {
293
+ className: wrapperClasses,
294
+ style: { position: 'relative', ...style },
295
+ children: [
296
+ title
297
+ ? ED.createElement('div', { key: 'title', className: 'ed-px-4 ed-py-3 ed-border-b ed-border-gray-200 ed-font-semibold ed-text-gray-900' }, title)
298
+ : null,
299
+ ED.createElement('div', {
300
+ key: 'table-wrapper',
301
+ className: 'ed-overflow-x-auto',
302
+ style: fixedHeader ? { maxHeight, overflowY: 'auto' } : scrollX ? { overflowX: 'auto' } : {},
303
+ children: ED.createElement('table', {
304
+ className: tableClasses,
305
+ children: [
306
+ showHeader ? ED.createElement('thead', { key: 'head' }, headerRow) : null,
307
+ ED.createElement('tbody', { key: 'body' },
308
+ ...(data.length > 0 ? data.flatMap(renderRow) : [emptyState]).filter(Boolean)
309
+ ),
310
+ footer
311
+ ? ED.createElement('tfoot', { key: 'foot' },
312
+ ED.createElement('tr', null,
313
+ ED.createElement('td', {
314
+ colSpan: (selectable ? 1 : 0) + (expandable ? 1 : 0) + columns.length,
315
+ className: 'ed-px-4 ed-py-3 ed-border-t ed-border-gray-200 ed-bg-gray-50',
316
+ children: footer,
317
+ })
318
+ )
319
+ )
320
+ : null,
321
+ ].filter(Boolean),
322
+ }),
323
+ }),
324
+ loadingOverlay,
325
+ paginationElement,
326
+ ].filter(Boolean),
327
+ });
328
+ }
329
+
330
+ Table.displayName = 'Table';
331
+
332
+ module.exports = Table;
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Tabs Component for ElementDrawing Framework
3
+ * Supports line, card, pill variants, closable, scrollable, lazy, icons, vertical, animated indicator
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ function TabPane(props) {
8
+ const {
9
+ children,
10
+ tab,
11
+ key: tabKey,
12
+ disabled = false,
13
+ icon,
14
+ closable = true,
15
+ forceRender = false,
16
+ className = '',
17
+ style = {},
18
+ lazy = false,
19
+ } = props;
20
+
21
+ return ED.createElement('div', {
22
+ className: `ed-tab-pane ed-py-3 ${className}`,
23
+ style,
24
+ role: 'tabpanel',
25
+ children,
26
+ });
27
+ }
28
+
29
+ TabPane.displayName = 'TabPane';
30
+
31
+ function Tabs(props) {
32
+ const {
33
+ children,
34
+ activeKey,
35
+ defaultActiveKey = '0',
36
+ variant = 'line',
37
+ onChange,
38
+ type = variant,
39
+ closable = false,
40
+ onClose,
41
+ onEdit,
42
+ scrollable = false,
43
+ lazy = false,
44
+ vertical = false,
45
+ animated = true,
46
+ addable = false,
47
+ onAdd,
48
+ hideAdd = false,
49
+ size = 'md',
50
+ className = '',
51
+ style = {},
52
+ tabBarExtraContent,
53
+ tabBarGutter = 0,
54
+ tabBarStyle = {},
55
+ inkBar = true,
56
+ items,
57
+ tabPosition = 'top',
58
+ destroyInactiveTabPane = false,
59
+ renderTabBar,
60
+ } = props;
61
+
62
+ const currentVariant = type || variant;
63
+ const currentActiveKey = activeKey !== undefined ? activeKey : defaultActiveKey;
64
+
65
+ const tabItems = items || [];
66
+ const childPanes = children
67
+ ? (Array.isArray(children) ? children : [children])
68
+ : [];
69
+
70
+ const allTabs = tabItems.length > 0
71
+ ? tabItems.map((item, idx) => ({
72
+ key: item.key || String(idx),
73
+ label: item.label || item.tab,
74
+ icon: item.icon,
75
+ disabled: item.disabled,
76
+ closable: item.closable !== undefined ? item.closable : closable,
77
+ content: item.children || item.content,
78
+ }))
79
+ : childPanes.map((child, idx) => ({
80
+ key: child.props?.key || String(idx),
81
+ label: child.props?.tab,
82
+ icon: child.props?.icon,
83
+ disabled: child.props?.disabled,
84
+ closable: child.props?.closable !== undefined ? child.props?.closable : closable,
85
+ content: child,
86
+ }));
87
+
88
+ const sizeClasses = {
89
+ sm: 'ed-text-sm',
90
+ md: 'ed-text-sm',
91
+ lg: 'ed-text-base',
92
+ };
93
+
94
+ const variantClasses = {
95
+ line: {
96
+ nav: 'ed-border-b ed-border-gray-200',
97
+ tab: 'ed-px-4 ed-py-2 ed-border-b-2 ed-border-transparent ed-text-gray-500 hover:ed-text-gray-700 hover:ed-border-gray-300',
98
+ active: 'ed-border-blue-500 ed-text-blue-600 ed-font-medium',
99
+ },
100
+ card: {
101
+ nav: 'ed-border-b ed-border-gray-200 ed--mb-px',
102
+ tab: 'ed-px-4 ed-py-2 ed-border ed-border-b-0 ed-border-transparent ed-rounded-t-lg ed-text-gray-500 hover:ed-text-gray-700 ed-bg-gray-50',
103
+ active: 'ed-border-gray-200 ed-border-b-white ed-bg-white ed-text-gray-900 ed-font-medium',
104
+ },
105
+ pill: {
106
+ nav: 'ed-gap-1 ed-bg-gray-100 ed-p-1 ed-rounded-lg',
107
+ tab: 'ed-px-4 ed-py-1.5 ed-rounded-md ed-text-gray-500 hover:ed-text-gray-700',
108
+ active: 'ed-bg-white ed-text-gray-900 ed-font-medium ed-shadow-sm',
109
+ },
110
+ };
111
+
112
+ const currentStyle = variantClasses[currentVariant] || variantClasses.line;
113
+
114
+ const navClasses = [
115
+ 'ed-flex ed-items-center',
116
+ vertical ? 'ed-flex-col ed-border-r ed-border-gray-200' : currentStyle.nav,
117
+ scrollable ? 'ed-overflow-x-auto' : '',
118
+ sizeClasses[size] || sizeClasses.md,
119
+ className,
120
+ ].filter(Boolean).join(' ');
121
+
122
+ const renderTab = (tab, idx) => {
123
+ const isActive = tab.key === currentActiveKey;
124
+ const isDisabled = tab.disabled;
125
+
126
+ const tabClasses = [
127
+ 'ed-flex ed-items-center ed-gap-1.5 ed-whitespace-nowrap ed-transition-all ed-duration-200 ed-cursor-pointer ed-select-none',
128
+ currentStyle.tab,
129
+ isActive ? currentStyle.active : '',
130
+ isDisabled ? 'ed-opacity-50 ed-cursor-not-allowed ed-pointer-events-none' : '',
131
+ closable && tab.closable ? 'ed-pr-2' : '',
132
+ ].filter(Boolean).join(' ');
133
+
134
+ const closeBtn = closable && tab.closable
135
+ ? ED.createElement('button', {
136
+ key: 'close',
137
+ className: 'ed-ml-1 ed-p-0.5 ed-rounded ed-text-gray-400 hover:ed-text-red-500 hover:ed-bg-red-50 ed-transition-colors',
138
+ onClick: (e) => { e.stopPropagation(); onClose?.(tab.key); },
139
+ 'aria-label': 'Close tab',
140
+ children: ED.createElement('svg', {
141
+ className: 'ed-w-3.5 ed-h-3.5',
142
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
143
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M6 18L18 6M6 6l12 12' }),
144
+ }),
145
+ })
146
+ : null;
147
+
148
+ return ED.createElement('div', {
149
+ key: tab.key,
150
+ className: tabClasses,
151
+ style: tabBarGutter ? { marginRight: tabBarGutter } : {},
152
+ role: 'tab',
153
+ 'aria-selected': isActive,
154
+ tabIndex: isDisabled ? -1 : (isActive ? 0 : -1),
155
+ onClick: () => { if (!isDisabled) onChange?.(tab.key); },
156
+ children: [
157
+ tab.icon
158
+ ? ED.createElement('span', { key: 'icon', className: 'ed-flex ed-items-center ed-justify-center ed-w-4 ed-h-4' },
159
+ typeof tab.icon === 'string' ? ED.createElement('i', { className: tab.icon }) : tab.icon
160
+ )
161
+ : null,
162
+ ED.createElement('span', { key: 'label' }, tab.label),
163
+ closeBtn,
164
+ ].filter(Boolean),
165
+ });
166
+ };
167
+
168
+ const addButton = addable && !hideAdd
169
+ ? ED.createElement('button', {
170
+ key: 'add',
171
+ className: 'ed-flex ed-items-center ed-justify-center ed-w-8 ed-h-8 ed-ml-2 ed-rounded-md ed-text-gray-400 hover:ed-text-gray-600 hover:ed-bg-gray-100 ed-transition-colors',
172
+ onClick: onAdd,
173
+ 'aria-label': 'Add tab',
174
+ children: ED.createElement('svg', {
175
+ className: 'ed-w-4 ed-h-4',
176
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
177
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M12 4v16m8-8H4' }),
178
+ }),
179
+ })
180
+ : null;
181
+
182
+ const inkBarElement = inkBar && currentVariant === 'line' && animated
183
+ ? ED.createElement('div', {
184
+ className: 'ed-absolute ed-bottom-0 ed-h-0.5 ed-bg-blue-500 ed-transition-all ed-duration-300',
185
+ })
186
+ : null;
187
+
188
+ const navElement = ED.createElement('div', {
189
+ className: navClasses,
190
+ style: { position: 'relative', ...tabBarStyle },
191
+ role: 'tablist',
192
+ children: [
193
+ ...allTabs.map(renderTab),
194
+ addButton,
195
+ inkBarElement,
196
+ tabBarExtraContent
197
+ ? ED.createElement('div', { key: 'extra', className: 'ed-ml-auto ed-flex ed-items-center' }, tabBarExtraContent)
198
+ : null,
199
+ ],
200
+ });
201
+
202
+ const activePane = allTabs.find(t => t.key === currentActiveKey);
203
+
204
+ const contentElement = ED.createElement('div', {
205
+ className: 'ed-tab-content ed-flex-1',
206
+ role: 'tabpanel',
207
+ children: activePane ? activePane.content : null,
208
+ });
209
+
210
+ if (vertical) {
211
+ return ED.createElement('div', {
212
+ className: 'ed-flex ed-gap-0',
213
+ style,
214
+ children: [
215
+ ED.createElement('div', { key: 'nav', className: 'ed-w-48 ed-flex-shrink-0' }, navElement),
216
+ ED.createElement('div', { key: 'content', className: 'ed-flex-1 ed-pl-4' }, contentElement),
217
+ ],
218
+ });
219
+ }
220
+
221
+ return ED.createElement('div', { className: 'ed-flex ed-flex-col', style, children: [navElement, contentElement] });
222
+ }
223
+
224
+ Tabs.displayName = 'Tabs';
225
+ Tabs.TabPane = TabPane;
226
+
227
+ module.exports = Tabs;