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,212 @@
1
+ /**
2
+ * Textarea Component for ElementDrawing Framework
3
+ * Supports auto-resize, max length, character count, clearable, disabled, readonly, validation, resize control
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ const TEXTAREA_SIZES = {
8
+ sm: 'ed-text-sm ed-px-3 ed-py-1.5',
9
+ md: 'ed-text-sm ed-px-3 ed-py-2',
10
+ lg: 'ed-text-base ed-px-4 ed-py-2.5',
11
+ };
12
+
13
+ const VALIDATION_STATES = {
14
+ default: 'ed-border-gray-300 focus:ed-border-blue-500 focus:ed-ring-blue-500',
15
+ success: 'ed-border-green-500 focus:ed-border-green-600 focus:ed-ring-green-500',
16
+ warning: 'ed-border-yellow-500 focus:ed-border-yellow-600 focus:ed-ring-yellow-500',
17
+ error: 'ed-border-red-500 focus:ed-border-red-600 focus:ed-ring-red-500',
18
+ };
19
+
20
+ function Textarea(props) {
21
+ const {
22
+ value,
23
+ defaultValue,
24
+ onChange,
25
+ placeholder,
26
+ rows = 3,
27
+ minRows,
28
+ maxRows,
29
+ autoResize = false,
30
+ maxLength,
31
+ showCount = false,
32
+ clearable = false,
33
+ disabled = false,
34
+ readonly = false,
35
+ size = 'md',
36
+ validationState = 'default',
37
+ state,
38
+ status,
39
+ error,
40
+ errorMessage,
41
+ helperText,
42
+ label,
43
+ id,
44
+ name,
45
+ className = '',
46
+ style = {},
47
+ textareaClassName = '',
48
+ textareaStyle = {},
49
+ resize = 'vertical',
50
+ bordered = true,
51
+ onFocus,
52
+ onBlur,
53
+ onInput,
54
+ onKeyDown,
55
+ onKeyUp,
56
+ onKeyPress,
57
+ onEnter,
58
+ autoFocus = false,
59
+ tabIndex,
60
+ required = false,
61
+ spellCheck = true,
62
+ wrap = 'soft',
63
+ cols,
64
+ onClear,
65
+ floatingLabel,
66
+ width,
67
+ } = props;
68
+
69
+ const currentState = status || state || validationState || (error ? 'error' : 'default');
70
+ const sizeClass = TEXTAREA_SIZES[size] || TEXTAREA_SIZES.md;
71
+ const stateClass = VALIDATION_STATES[currentState] || VALIDATION_STATES.default;
72
+
73
+ const resizeClass = {
74
+ none: 'ed-resize-none',
75
+ both: 'ed-resize',
76
+ horizontal: 'ed-resize-x',
77
+ vertical: 'ed-resize-y',
78
+ }[resize] || (autoResize ? 'ed-resize-none ed-overflow-hidden' : 'ed-resize-y');
79
+
80
+ const textareaClasses = [
81
+ 'ed-w-full ed-outline-none ed-transition-all ed-duration-200 ed-rounded-md',
82
+ sizeClass,
83
+ bordered ? 'ed-border' : 'ed-border-none',
84
+ stateClass,
85
+ resizeClass,
86
+ disabled ? 'ed-bg-gray-100 ed-cursor-not-allowed ed-text-gray-400' : 'ed-bg-white',
87
+ readonly ? 'ed-bg-gray-50 ed-cursor-default' : '',
88
+ 'focus:ed-ring-2 focus:ed-ring-offset-0 ed-placeholder-ed-text-gray-400',
89
+ textareaClassName,
90
+ ].filter(Boolean).join(' ');
91
+
92
+ const handleAutoResize = (el) => {
93
+ if (!autoResize || !el) return;
94
+ el.style.height = 'auto';
95
+ const minH = minRows ? minRows * 24 : 0;
96
+ const maxH = maxRows ? maxRows * 24 : Infinity;
97
+ el.style.height = `${Math.min(Math.max(el.scrollHeight, minH), maxH)}px`;
98
+ };
99
+
100
+ const handleChange = (e) => {
101
+ handleAutoResize(e.target);
102
+ onChange?.(e);
103
+ };
104
+
105
+ const handleKeyDown = (e) => {
106
+ if (onEnter && e.key === 'Enter' && !e.shiftKey) {
107
+ e.preventDefault();
108
+ onEnter(e);
109
+ }
110
+ onKeyDown?.(e);
111
+ };
112
+
113
+ const clearButton = clearable && value && !disabled && !readonly
114
+ ? ED.createElement('button', {
115
+ className: 'ed-absolute ed-top-2 ed-right-2 ed-p-1 ed-rounded ed-text-gray-400 hover:ed-text-gray-600 hover:ed-bg-gray-100 ed-transition-colors ed-z-10',
116
+ onClick: (e) => {
117
+ e.stopPropagation();
118
+ onClear?.();
119
+ onChange?.({ target: { value: '' } });
120
+ },
121
+ tabIndex: -1,
122
+ type: 'button',
123
+ 'aria-label': 'Clear',
124
+ children: ED.createElement('svg', {
125
+ className: 'ed-w-4 ed-h-4',
126
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
127
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M6 18L18 6M6 6l12 12' }),
128
+ }),
129
+ })
130
+ : null;
131
+
132
+ const charCount = showCount
133
+ ? ED.createElement('div', {
134
+ className: 'ed-flex ed-justify-end ed-mt-1',
135
+ }, [
136
+ maxLength
137
+ ? ED.createElement('span', {
138
+ key: 'count',
139
+ className: `ed-text-xs ${(value?.length || 0) > maxLength ? 'ed-text-red-500' : 'ed-text-gray-400'}`,
140
+ }, `${value?.length || 0} / ${maxLength}`)
141
+ : ED.createElement('span', {
142
+ key: 'count',
143
+ className: 'ed-text-xs ed-text-gray-400',
144
+ }, `${value?.length || 0}`),
145
+ ])
146
+ : null;
147
+
148
+ const messageElement = errorMessage && currentState === 'error'
149
+ ? ED.createElement('p', { className: 'ed-text-xs ed-text-red-500 ed-mt-1' }, errorMessage)
150
+ : helperText
151
+ ? ED.createElement('p', { className: 'ed-text-xs ed-text-gray-500 ed-mt-1' }, helperText)
152
+ : null;
153
+
154
+ const textareaElement = ED.createElement('textarea', {
155
+ id,
156
+ name,
157
+ value,
158
+ defaultValue,
159
+ placeholder,
160
+ rows: autoResize ? 1 : rows,
161
+ cols,
162
+ maxLength,
163
+ disabled,
164
+ readOnly: readonly,
165
+ spellCheck,
166
+ wrap,
167
+ className: textareaClasses,
168
+ style: textareaStyle,
169
+ onChange: handleChange,
170
+ onFocus,
171
+ onBlur,
172
+ onInput,
173
+ onKeyDown: handleKeyDown,
174
+ onKeyUp,
175
+ onKeyPress,
176
+ autoFocus,
177
+ tabIndex,
178
+ required,
179
+ 'aria-invalid': currentState === 'error',
180
+ 'aria-describedby': errorMessage ? `${id}-error` : undefined,
181
+ ref: autoResize ? handleAutoResize : undefined,
182
+ });
183
+
184
+ return ED.createElement('div', {
185
+ className: `ed-inline-flex ed-flex-col ${width ? '' : 'ed-w-full'}`,
186
+ style: { width, ...style },
187
+ }, [
188
+ label && !floatingLabel
189
+ ? ED.createElement('label', {
190
+ key: 'label',
191
+ htmlFor: id,
192
+ className: `ed-block ed-text-sm ed-font-medium ed-mb-1 ${required ? "ed-after:content-['*'] ed-after:ed-text-red-500 ed-after:ed-ml-0.5" : ''} ${disabled ? 'ed-text-gray-400' : 'ed-text-gray-700'}`,
193
+ }, label)
194
+ : null,
195
+ ED.createElement('div', {
196
+ key: 'textarea-wrapper',
197
+ className: 'ed-relative',
198
+ children: [textareaElement, clearButton],
199
+ }),
200
+ ED.createElement('div', {
201
+ key: 'messages',
202
+ className: 'ed-flex ed-justify-between',
203
+ children: [messageElement, charCount].filter(Boolean),
204
+ }),
205
+ ].filter(Boolean));
206
+ }
207
+
208
+ Textarea.displayName = 'Textarea';
209
+ Textarea.SIZES = TEXTAREA_SIZES;
210
+ Textarea.STATES = VALIDATION_STATES;
211
+
212
+ module.exports = Textarea;
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Toast Component for ElementDrawing Framework
3
+ * Supports positions, types, auto-close, progress bar, stacked, promise toast, custom icon
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ const TOAST_TYPES = {
8
+ success: {
9
+ icon: ED.createElement('svg', {
10
+ className: 'ed-w-5 ed-h-5',
11
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
12
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M5 13l4 4L19 7' }),
13
+ }),
14
+ iconColor: 'ed-text-green-500',
15
+ bg: 'ed-bg-green-50 ed-border-green-200',
16
+ progressColor: 'ed-bg-green-500',
17
+ },
18
+ error: {
19
+ icon: ED.createElement('svg', {
20
+ className: 'ed-w-5 ed-h-5',
21
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
22
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z' }),
23
+ }),
24
+ iconColor: 'ed-text-red-500',
25
+ bg: 'ed-bg-red-50 ed-border-red-200',
26
+ progressColor: 'ed-bg-red-500',
27
+ },
28
+ warning: {
29
+ icon: ED.createElement('svg', {
30
+ className: 'ed-w-5 ed-h-5',
31
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
32
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }),
33
+ }),
34
+ iconColor: 'ed-text-yellow-500',
35
+ bg: 'ed-bg-yellow-50 ed-border-yellow-200',
36
+ progressColor: 'ed-bg-yellow-500',
37
+ },
38
+ info: {
39
+ icon: ED.createElement('svg', {
40
+ className: 'ed-w-5 ed-h-5',
41
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
42
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }),
43
+ }),
44
+ iconColor: 'ed-text-blue-500',
45
+ bg: 'ed-bg-blue-50 ed-border-blue-200',
46
+ progressColor: 'ed-bg-blue-500',
47
+ },
48
+ loading: {
49
+ icon: null,
50
+ iconColor: 'ed-text-blue-500',
51
+ bg: 'ed-bg-blue-50 ed-border-blue-200',
52
+ progressColor: 'ed-bg-blue-500',
53
+ },
54
+ };
55
+
56
+ const TOAST_POSITIONS = {
57
+ 'top-left': 'ed-top-4 ed-left-4',
58
+ 'top-center': 'ed-top-4 ed-left-1/2 ed--translate-x-1/2',
59
+ 'top-right': 'ed-top-4 ed-right-4',
60
+ 'bottom-left': 'ed-bottom-4 ed-left-4',
61
+ 'bottom-center': 'ed-bottom-4 ed-left-1/2 ed--translate-x-1/2',
62
+ 'bottom-right': 'ed-bottom-4 ed-right-4',
63
+ };
64
+
65
+ const loadingSpinner = ED.createElement('div', {
66
+ className: 'ed-w-5 ed-h-5 ed-border-2 ed-border-blue-500 ed-border-t-transparent ed-rounded-full ed-animate-spin',
67
+ });
68
+
69
+ function ToastItem(props) {
70
+ const {
71
+ id,
72
+ type = 'info',
73
+ message,
74
+ description,
75
+ icon,
76
+ closable = true,
77
+ onClose,
78
+ autoClose,
79
+ duration = 5000,
80
+ progressBar = true,
81
+ onClick,
82
+ className = '',
83
+ style = {},
84
+ action,
85
+ title,
86
+ promise,
87
+ } = props;
88
+
89
+ const toastType = TOAST_TYPES[type] || TOAST_TYPES.info;
90
+
91
+ const toastClasses = [
92
+ 'ed-flex ed-items-start ed-gap-3 ed-px-4 ed-py-3 ed-rounded-lg ed-border ed-shadow-lg ed-min-w-[300px] ed-max-w-[420px]',
93
+ toastType.bg,
94
+ 'ed-animate-slide-in-right',
95
+ onClick ? 'ed-cursor-pointer' : '',
96
+ className,
97
+ ].filter(Boolean).join(' ');
98
+
99
+ const iconElement = icon !== undefined ? icon
100
+ : type === 'loading'
101
+ ? loadingSpinner
102
+ : ED.createElement('span', { className: `ed-flex-shrink-0 ed-mt-0.5 ${toastType.iconColor}` },
103
+ toastType.icon
104
+ );
105
+
106
+ const closeElement = closable
107
+ ? ED.createElement('button', {
108
+ className: 'ed-flex-shrink-0 ed-p-0.5 ed-rounded ed-text-gray-400 hover:ed-text-gray-600 ed-transition-colors',
109
+ onClick: (e) => { e.stopPropagation(); onClose?.(id); },
110
+ 'aria-label': 'Close notification',
111
+ children: ED.createElement('svg', {
112
+ className: 'ed-w-4 ed-h-4',
113
+ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor',
114
+ children: ED.createElement('path', { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: 2, d: 'M6 18L18 6M6 6l12 12' }),
115
+ }),
116
+ })
117
+ : null;
118
+
119
+ const progressBarElement = progressBar && autoClose
120
+ ? ED.createElement('div', {
121
+ className: 'ed-absolute ed-bottom-0 ed-left-0 ed-right-0 ed-h-1 ed-rounded-b-lg ed-overflow-hidden',
122
+ children: ED.createElement('div', {
123
+ className: `ed-h-full ${toastType.progressColor} ed-opacity-50`,
124
+ style: {
125
+ animation: `ed-shrink ${duration}ms linear forwards`,
126
+ },
127
+ }),
128
+ })
129
+ : null;
130
+
131
+ const actionElement = action
132
+ ? ED.createElement('div', { className: 'ed-flex-shrink-0 ed-ml-2' }, action)
133
+ : null;
134
+
135
+ return ED.createElement('div', {
136
+ className: toastClasses,
137
+ style: { position: 'relative', ...style },
138
+ onClick: onClick ? () => onClick(id) : undefined,
139
+ role: type === 'error' ? 'alert' : 'status',
140
+ children: [
141
+ iconElement,
142
+ ED.createElement('div', { className: 'ed-flex-1 ed-min-w-0' }, [
143
+ title || (typeof message === 'string' && message.length > 0)
144
+ ? ED.createElement('p', { key: 'message', className: 'ed-text-sm ed-font-medium ed-text-gray-900' }, title || message)
145
+ : null,
146
+ description || (title && message)
147
+ ? ED.createElement('p', { key: 'desc', className: 'ed-text-xs ed-text-gray-600 ed-mt-0.5' }, description || message)
148
+ : null,
149
+ ].filter(Boolean)),
150
+ actionElement,
151
+ closeElement,
152
+ progressBarElement,
153
+ ],
154
+ });
155
+ }
156
+
157
+ ToastItem.displayName = 'ToastItem';
158
+
159
+ function ToastContainer(props) {
160
+ const {
161
+ toasts = [],
162
+ position = 'top-right',
163
+ onClose,
164
+ stacked = false,
165
+ className = '',
166
+ style = {},
167
+ maxToasts = 5,
168
+ } = props;
169
+
170
+ const positionClass = TOAST_POSITIONS[position] || TOAST_POSITIONS['top-right'];
171
+
172
+ const visibleToasts = stacked ? toasts.slice(0, maxToasts) : toasts;
173
+
174
+ return ED.createElement('div', {
175
+ className: [
176
+ 'ed-fixed ed-z-[9999] ed-pointer-events-none',
177
+ positionClass,
178
+ className,
179
+ ].filter(Boolean).join(' '),
180
+ style,
181
+ children: ED.createElement('div', {
182
+ className: `ed-flex ed-flex-col ed-gap-2 ed-pointer-events-auto ${stacked ? '' : ''}`,
183
+ children: visibleToasts.map((toast) =>
184
+ ED.createElement(ToastItem, {
185
+ key: toast.id,
186
+ ...toast,
187
+ onClose,
188
+ })
189
+ ),
190
+ }),
191
+ });
192
+ }
193
+
194
+ ToastContainer.displayName = 'ToastContainer';
195
+
196
+ let toastId = 0;
197
+ const toastStore = { toasts: [], listeners: [] };
198
+
199
+ function Toast(props) {
200
+ const {
201
+ position = 'top-right',
202
+ stacked = false,
203
+ maxToasts = 5,
204
+ className = '',
205
+ style = {},
206
+ } = props;
207
+
208
+ return ED.createElement(ToastContainer, {
209
+ toasts: toastStore.toasts,
210
+ position,
211
+ stacked,
212
+ maxToasts,
213
+ className,
214
+ style,
215
+ onClose: Toast.remove,
216
+ });
217
+ }
218
+
219
+ Toast.show = (options) => {
220
+ const id = ++toastId;
221
+ const toast = { id, ...options };
222
+ toastStore.toasts = [...toastStore.toasts, toast];
223
+ toastStore.listeners.forEach(fn => fn(toastStore.toasts));
224
+
225
+ if (options.autoClose !== false && options.type !== 'loading') {
226
+ const duration = options.duration || 5000;
227
+ setTimeout(() => Toast.remove(id), duration);
228
+ }
229
+
230
+ return id;
231
+ };
232
+
233
+ Toast.success = (message, options = {}) => Toast.show({ type: 'success', message, ...options });
234
+ Toast.error = (message, options = {}) => Toast.show({ type: 'error', message, ...options });
235
+ Toast.warning = (message, options = {}) => Toast.show({ type: 'warning', message, ...options });
236
+ Toast.info = (message, options = {}) => Toast.show({ type: 'info', message, ...options });
237
+ Toast.loading = (message, options = {}) => Toast.show({ type: 'loading', message, autoClose: false, ...options });
238
+
239
+ Toast.remove = (id) => {
240
+ toastStore.toasts = toastStore.toasts.filter(t => t.id !== id);
241
+ toastStore.listeners.forEach(fn => fn(toastStore.toasts));
242
+ };
243
+
244
+ Toast.clear = () => {
245
+ toastStore.toasts = [];
246
+ toastStore.listeners.forEach(fn => fn(toastStore.toasts));
247
+ };
248
+
249
+ Toast.promise = (promise, options = {}) => {
250
+ const id = Toast.loading(options.loading || 'Loading...');
251
+ return promise
252
+ .then((result) => {
253
+ Toast.remove(id);
254
+ Toast.success(options.success || 'Success!');
255
+ return result;
256
+ })
257
+ .catch((error) => {
258
+ Toast.remove(id);
259
+ Toast.error(options.error || 'Something went wrong');
260
+ throw error;
261
+ });
262
+ };
263
+
264
+ Toast.displayName = 'Toast';
265
+ Toast.Container = ToastContainer;
266
+ Toast.Item = ToastItem;
267
+ Toast.TYPES = TOAST_TYPES;
268
+ Toast.POSITIONS = TOAST_POSITIONS;
269
+
270
+ module.exports = Toast;
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Tooltip Component for ElementDrawing Framework
3
+ * Supports positions, trigger, arrow, delay, HTML content, theme (dark, light)
4
+ */
5
+ const ED = require('../core/element');
6
+
7
+ const POSITION_STYLES = {
8
+ top: {
9
+ tooltip: 'ed-bottom-full ed-left-1/2 ed--translate-x-1/2 ed-mb-2',
10
+ arrow: 'ed-top-full ed-left-1/2 ed--translate-x-1/2 ed-border-t-current ed-border-x-transparent ed-border-b-transparent',
11
+ },
12
+ 'top-start': {
13
+ tooltip: 'ed-bottom-full ed-left-0 ed-mb-2',
14
+ arrow: 'ed-top-full ed-left-3 ed-border-t-current ed-border-x-transparent ed-border-b-transparent',
15
+ },
16
+ 'top-end': {
17
+ tooltip: 'ed-bottom-full ed-right-0 ed-mb-2',
18
+ arrow: 'ed-top-full ed-right-3 ed-border-t-current ed-border-x-transparent ed-border-b-transparent',
19
+ },
20
+ bottom: {
21
+ tooltip: 'ed-top-full ed-left-1/2 ed--translate-x-1/2 ed-mt-2',
22
+ arrow: 'ed-bottom-full ed-left-1/2 ed--translate-x-1/2 ed-border-b-current ed-border-x-transparent ed-border-t-transparent',
23
+ },
24
+ 'bottom-start': {
25
+ tooltip: 'ed-top-full ed-left-0 ed-mt-2',
26
+ arrow: 'ed-bottom-full ed-left-3 ed-border-b-current ed-border-x-transparent ed-border-t-transparent',
27
+ },
28
+ 'bottom-end': {
29
+ tooltip: 'ed-top-full ed-right-0 ed-mt-2',
30
+ arrow: 'ed-bottom-full ed-right-3 ed-border-b-current ed-border-x-transparent ed-border-t-transparent',
31
+ },
32
+ left: {
33
+ tooltip: 'ed-right-full ed-top-1/2 ed--translate-y-1/2 ed-mr-2',
34
+ arrow: 'ed-left-full ed-top-1/2 ed--translate-y-1/2 ed-border-l-current ed-border-y-transparent ed-border-r-transparent',
35
+ },
36
+ 'left-start': {
37
+ tooltip: 'ed-right-full ed-top-0 ed-mr-2',
38
+ arrow: 'ed-left-full ed-top-3 ed-border-l-current ed-border-y-transparent ed-border-r-transparent',
39
+ },
40
+ 'left-end': {
41
+ tooltip: 'ed-right-full ed-bottom-0 ed-mr-2',
42
+ arrow: 'ed-left-full ed-bottom-3 ed-border-l-current ed-border-y-transparent ed-border-r-transparent',
43
+ },
44
+ right: {
45
+ tooltip: 'ed-left-full ed-top-1/2 ed--translate-y-1/2 ed-ml-2',
46
+ arrow: 'ed-right-full ed-top-1/2 ed--translate-y-1/2 ed-border-r-current ed-border-y-transparent ed-border-l-transparent',
47
+ },
48
+ 'right-start': {
49
+ tooltip: 'ed-left-full ed-top-0 ed-ml-2',
50
+ arrow: 'ed-right-full ed-top-3 ed-border-r-current ed-border-y-transparent ed-border-l-transparent',
51
+ },
52
+ 'right-end': {
53
+ tooltip: 'ed-left-full ed-bottom-0 ed-ml-2',
54
+ arrow: 'ed-right-full ed-bottom-3 ed-border-r-current ed-border-y-transparent ed-border-l-transparent',
55
+ },
56
+ };
57
+
58
+ const THEME_CLASSES = {
59
+ dark: {
60
+ tooltip: 'ed-bg-gray-900 ed-text-white',
61
+ arrow: 'ed-text-gray-900',
62
+ },
63
+ light: {
64
+ tooltip: 'ed-bg-white ed-text-gray-800 ed-shadow-lg ed-border ed-border-gray-200',
65
+ arrow: 'ed-text-white',
66
+ },
67
+ };
68
+
69
+ function Tooltip(props) {
70
+ const {
71
+ children,
72
+ title,
73
+ content,
74
+ position = 'top',
75
+ placement = position,
76
+ trigger = 'hover',
77
+ arrow = true,
78
+ delay = 0,
79
+ delayShow = delay,
80
+ delayHide = 0,
81
+ htmlContent,
82
+ theme = 'dark',
83
+ className = '',
84
+ style = {},
85
+ tooltipClassName = '',
86
+ tooltipStyle = {},
87
+ open,
88
+ defaultOpen = false,
89
+ onOpenChange,
90
+ disabled = false,
91
+ offset = 0,
92
+ zIndex = 50,
93
+ maxWidth = 250,
94
+ animation = true,
95
+ followCursor = false,
96
+ destroyTooltipOnHide = false,
97
+ onVisibleChange,
98
+ mouseEnterDelay = delayShow || 100,
99
+ mouseLeaveDelay = delayHide || 100,
100
+ clickToHide = true,
101
+ } = props;
102
+
103
+ const pos = POSITION_STYLES[placement || position] || POSITION_STYLES.top;
104
+ const themeConfig = THEME_CLASSES[theme] || THEME_CLASSES.dark;
105
+ const isVisible = open !== undefined ? open : defaultOpen;
106
+
107
+ if (disabled) return children;
108
+
109
+ const tooltipContent = title || content || htmlContent;
110
+
111
+ if (!tooltipContent) return children;
112
+
113
+ const tooltipClasses = [
114
+ 'ed-absolute ed-px-3 ed-py-1.5 ed-text-xs ed-font-medium ed-rounded-md ed-whitespace-normal ed-pointer-events-none',
115
+ 'ed-z-50 ed-transition-opacity ed-duration-200',
116
+ pos.tooltip,
117
+ themeConfig.tooltip,
118
+ isVisible ? 'ed-opacity-100' : 'ed-opacity-0 ed-invisible',
119
+ animation ? 'ed-transition-all ed-duration-200' : '',
120
+ tooltipClassName,
121
+ ].filter(Boolean).join(' ');
122
+
123
+ const arrowClasses = [
124
+ 'ed-absolute ed-w-0 ed-h-0 ed-border-4',
125
+ pos.arrow,
126
+ themeConfig.arrow,
127
+ ].join(' ');
128
+
129
+ const arrowElement = arrow
130
+ ? ED.createElement('div', {
131
+ className: arrowClasses,
132
+ 'aria-hidden': 'true',
133
+ })
134
+ : null;
135
+
136
+ const tooltipElement = ED.createElement('div', {
137
+ className: tooltipClasses,
138
+ style: {
139
+ maxWidth,
140
+ zIndex,
141
+ ...(offset ? { marginTop: position.includes('bottom') ? offset : 0, marginBottom: position.includes('top') ? offset : 0, marginLeft: position.includes('right') ? offset : 0, marginRight: position.includes('left') ? offset : 0 } : {}),
142
+ ...tooltipStyle,
143
+ },
144
+ role: 'tooltip',
145
+ children: [
146
+ htmlContent
147
+ ? ED.createElement('div', { dangerouslySetInnerHTML: { __html: htmlContent } })
148
+ : ED.createElement('span', null, tooltipContent),
149
+ arrowElement,
150
+ ],
151
+ });
152
+
153
+ const triggerEvents = {};
154
+ if (trigger === 'hover') {
155
+ triggerEvents.onMouseEnter = () => onOpenChange?.(true) || onVisibleChange?.(true);
156
+ triggerEvents.onMouseLeave = () => onOpenChange?.(false) || onVisibleChange?.(false);
157
+ } else if (trigger === 'click') {
158
+ triggerEvents.onClick = () => {
159
+ onOpenChange?.(!isVisible) || onVisibleChange?.(!isVisible);
160
+ };
161
+ } else if (trigger === 'focus') {
162
+ triggerEvents.onFocus = () => onOpenChange?.(true) || onVisibleChange?.(true);
163
+ triggerEvents.onBlur = () => onOpenChange?.(false) || onVisibleChange?.(false);
164
+ }
165
+
166
+ return ED.createElement('div', {
167
+ className: `ed-relative ed-inline-flex ${className}`,
168
+ style,
169
+ ...triggerEvents,
170
+ children: [children, tooltipElement],
171
+ });
172
+ }
173
+
174
+ Tooltip.displayName = 'Tooltip';
175
+ Tooltip.POSITIONS = POSITION_STYLES;
176
+ Tooltip.THEMES = THEME_CLASSES;
177
+
178
+ module.exports = Tooltip;