jasmincss 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 (76) hide show
  1. package/README.md +524 -0
  2. package/bin/jasmin.js +45 -0
  3. package/dist/index.d.ts +62 -0
  4. package/dist/index.js +14568 -0
  5. package/dist/index.mjs +14524 -0
  6. package/dist/jasmin.css +63308 -0
  7. package/dist/jasmin.min.css +1 -0
  8. package/dist/plugins/nextjs.js +14777 -0
  9. package/dist/plugins/nextjs.mjs +14747 -0
  10. package/dist/plugins/vite.js +14889 -0
  11. package/dist/plugins/vite.mjs +14860 -0
  12. package/package.json +101 -0
  13. package/src/cli/add.js +83 -0
  14. package/src/cli/init.js +210 -0
  15. package/src/cli/run.js +142 -0
  16. package/src/components/accordion.js +309 -0
  17. package/src/components/alerts.js +357 -0
  18. package/src/components/avatars.js +331 -0
  19. package/src/components/badges.js +353 -0
  20. package/src/components/buttons.js +412 -0
  21. package/src/components/cards.js +342 -0
  22. package/src/components/carousel.js +495 -0
  23. package/src/components/chips.js +440 -0
  24. package/src/components/command-palette.js +434 -0
  25. package/src/components/datepicker.js +517 -0
  26. package/src/components/dropdown.js +411 -0
  27. package/src/components/forms.js +516 -0
  28. package/src/components/index.js +81 -0
  29. package/src/components/modals.js +349 -0
  30. package/src/components/navigation.js +463 -0
  31. package/src/components/offcanvas.js +390 -0
  32. package/src/components/popover.js +427 -0
  33. package/src/components/progress.js +396 -0
  34. package/src/components/rating.js +394 -0
  35. package/src/components/skeleton.js +408 -0
  36. package/src/components/spinner.js +453 -0
  37. package/src/components/stepper.js +389 -0
  38. package/src/components/tables.js +304 -0
  39. package/src/components/timeline.js +452 -0
  40. package/src/components/timepicker.js +529 -0
  41. package/src/components/tooltips.js +345 -0
  42. package/src/components/upload.js +490 -0
  43. package/src/config/defaults.js +263 -0
  44. package/src/config/loader.js +109 -0
  45. package/src/core/base.js +241 -0
  46. package/src/core/compiler.js +135 -0
  47. package/src/core/utilities/accessibility.js +290 -0
  48. package/src/core/utilities/animations.js +205 -0
  49. package/src/core/utilities/background.js +109 -0
  50. package/src/core/utilities/colors.js +234 -0
  51. package/src/core/utilities/columns.js +161 -0
  52. package/src/core/utilities/effects.js +261 -0
  53. package/src/core/utilities/filters.js +135 -0
  54. package/src/core/utilities/icons.js +806 -0
  55. package/src/core/utilities/index.js +239 -0
  56. package/src/core/utilities/layout.js +321 -0
  57. package/src/core/utilities/scroll.js +205 -0
  58. package/src/core/utilities/spacing.js +120 -0
  59. package/src/core/utilities/svg.js +191 -0
  60. package/src/core/utilities/transforms.js +116 -0
  61. package/src/core/utilities/typography.js +188 -0
  62. package/src/index.js +7 -0
  63. package/src/js/components/accordion.js +154 -0
  64. package/src/js/components/alert.js +198 -0
  65. package/src/js/components/carousel.js +226 -0
  66. package/src/js/components/dropdown.js +166 -0
  67. package/src/js/components/modal.js +169 -0
  68. package/src/js/components/offcanvas.js +175 -0
  69. package/src/js/components/popover.js +221 -0
  70. package/src/js/components/tabs.js +163 -0
  71. package/src/js/components/tooltip.js +200 -0
  72. package/src/js/index.js +79 -0
  73. package/src/js/types/config.d.ts +228 -0
  74. package/src/js/types/index.d.ts +439 -0
  75. package/src/plugins/nextjs.js +100 -0
  76. package/src/plugins/vite.js +133 -0
@@ -0,0 +1,188 @@
1
+ export function generateTypographyUtilities(config) {
2
+ const classes = [];
3
+
4
+ // Font Family
5
+ classes.push({ name: 'font-sans', css: '.font-sans { font-family: var(--j-font-sans); }' });
6
+ classes.push({ name: 'font-mono', css: '.font-mono { font-family: var(--j-font-mono); }' });
7
+ classes.push({ name: 'font-serif', css: '.font-serif { font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; }' });
8
+
9
+ // Font Size
10
+ const fontSizes = {
11
+ 'xs': ['0.75rem', '1rem'],
12
+ 'sm': ['0.875rem', '1.25rem'],
13
+ 'base': ['1rem', '1.5rem'],
14
+ 'lg': ['1.125rem', '1.75rem'],
15
+ 'xl': ['1.25rem', '1.75rem'],
16
+ '2xl': ['1.5rem', '2rem'],
17
+ '3xl': ['1.875rem', '2.25rem'],
18
+ '4xl': ['2.25rem', '2.5rem'],
19
+ '5xl': ['3rem', '1'],
20
+ '6xl': ['3.75rem', '1'],
21
+ '7xl': ['4.5rem', '1'],
22
+ '8xl': ['6rem', '1'],
23
+ '9xl': ['8rem', '1']
24
+ };
25
+ Object.entries(fontSizes).forEach(([size, [fontSize, lineHeight]]) => {
26
+ classes.push({
27
+ name: `text-${size}`,
28
+ css: `.text-${size} { font-size: ${fontSize}; line-height: ${lineHeight}; }`
29
+ });
30
+ });
31
+
32
+ // Font Weight
33
+ const fontWeights = {
34
+ 'thin': '100',
35
+ 'extralight': '200',
36
+ 'light': '300',
37
+ 'normal': '400',
38
+ 'medium': '500',
39
+ 'semibold': '600',
40
+ 'bold': '700',
41
+ 'extrabold': '800',
42
+ 'black': '900'
43
+ };
44
+ Object.entries(fontWeights).forEach(([name, value]) => {
45
+ classes.push({ name: `font-${name}`, css: `.font-${name} { font-weight: ${value}; }` });
46
+ });
47
+
48
+ // Font Style
49
+ classes.push({ name: 'italic', css: '.italic { font-style: italic; }' });
50
+ classes.push({ name: 'not-italic', css: '.not-italic { font-style: normal; }' });
51
+
52
+ // Font Variant Numeric
53
+ classes.push({ name: 'normal-nums', css: '.normal-nums { font-variant-numeric: normal; }' });
54
+ classes.push({ name: 'ordinal', css: '.ordinal { font-variant-numeric: ordinal; }' });
55
+ classes.push({ name: 'slashed-zero', css: '.slashed-zero { font-variant-numeric: slashed-zero; }' });
56
+ classes.push({ name: 'lining-nums', css: '.lining-nums { font-variant-numeric: lining-nums; }' });
57
+ classes.push({ name: 'oldstyle-nums', css: '.oldstyle-nums { font-variant-numeric: oldstyle-nums; }' });
58
+ classes.push({ name: 'proportional-nums', css: '.proportional-nums { font-variant-numeric: proportional-nums; }' });
59
+ classes.push({ name: 'tabular-nums', css: '.tabular-nums { font-variant-numeric: tabular-nums; }' });
60
+ classes.push({ name: 'diagonal-fractions', css: '.diagonal-fractions { font-variant-numeric: diagonal-fractions; }' });
61
+ classes.push({ name: 'stacked-fractions', css: '.stacked-fractions { font-variant-numeric: stacked-fractions; }' });
62
+
63
+ // Letter Spacing
64
+ const tracking = {
65
+ 'tighter': '-0.05em',
66
+ 'tight': '-0.025em',
67
+ 'normal': '0em',
68
+ 'wide': '0.025em',
69
+ 'wider': '0.05em',
70
+ 'widest': '0.1em'
71
+ };
72
+ Object.entries(tracking).forEach(([name, value]) => {
73
+ classes.push({ name: `tracking-${name}`, css: `.tracking-${name} { letter-spacing: ${value}; }` });
74
+ });
75
+
76
+ // Line Height
77
+ const leading = {
78
+ 'none': '1',
79
+ 'tight': '1.25',
80
+ 'snug': '1.375',
81
+ 'normal': '1.5',
82
+ 'relaxed': '1.625',
83
+ 'loose': '2',
84
+ '3': '.75rem',
85
+ '4': '1rem',
86
+ '5': '1.25rem',
87
+ '6': '1.5rem',
88
+ '7': '1.75rem',
89
+ '8': '2rem',
90
+ '9': '2.25rem',
91
+ '10': '2.5rem'
92
+ };
93
+ Object.entries(leading).forEach(([name, value]) => {
94
+ classes.push({ name: `leading-${name}`, css: `.leading-${name} { line-height: ${value}; }` });
95
+ });
96
+
97
+ // Text Align
98
+ ['left', 'center', 'right', 'justify', 'start', 'end'].forEach(align => {
99
+ classes.push({ name: `text-${align}`, css: `.text-${align} { text-align: ${align}; }` });
100
+ });
101
+
102
+ // Text Decoration
103
+ classes.push({ name: 'underline', css: '.underline { text-decoration-line: underline; }' });
104
+ classes.push({ name: 'overline', css: '.overline { text-decoration-line: overline; }' });
105
+ classes.push({ name: 'line-through', css: '.line-through { text-decoration-line: line-through; }' });
106
+ classes.push({ name: 'no-underline', css: '.no-underline { text-decoration-line: none; }' });
107
+
108
+ // Text Decoration Style
109
+ ['solid', 'double', 'dotted', 'dashed', 'wavy'].forEach(style => {
110
+ classes.push({ name: `decoration-${style}`, css: `.decoration-${style} { text-decoration-style: ${style}; }` });
111
+ });
112
+
113
+ // Text Decoration Thickness
114
+ ['auto', 'from-font', '0', '1', '2', '4', '8'].forEach(thickness => {
115
+ const value = ['auto', 'from-font'].includes(thickness) ? thickness : `${thickness}px`;
116
+ classes.push({ name: `decoration-${thickness}`, css: `.decoration-${thickness} { text-decoration-thickness: ${value}; }` });
117
+ });
118
+
119
+ // Text Underline Offset
120
+ ['auto', '0', '1', '2', '4', '8'].forEach(offset => {
121
+ const value = offset === 'auto' ? 'auto' : `${offset}px`;
122
+ classes.push({ name: `underline-offset-${offset}`, css: `.underline-offset-${offset} { text-underline-offset: ${value}; }` });
123
+ });
124
+
125
+ // Text Transform
126
+ classes.push({ name: 'uppercase', css: '.uppercase { text-transform: uppercase; }' });
127
+ classes.push({ name: 'lowercase', css: '.lowercase { text-transform: lowercase; }' });
128
+ classes.push({ name: 'capitalize', css: '.capitalize { text-transform: capitalize; }' });
129
+ classes.push({ name: 'normal-case', css: '.normal-case { text-transform: none; }' });
130
+
131
+ // Text Overflow
132
+ classes.push({ name: 'truncate', css: '.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }' });
133
+ classes.push({ name: 'text-ellipsis', css: '.text-ellipsis { text-overflow: ellipsis; }' });
134
+ classes.push({ name: 'text-clip', css: '.text-clip { text-overflow: clip; }' });
135
+
136
+ // Line Clamp
137
+ for (let i = 1; i <= 6; i++) {
138
+ classes.push({
139
+ name: `line-clamp-${i}`,
140
+ css: `.line-clamp-${i} { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: ${i}; }`
141
+ });
142
+ }
143
+ classes.push({ name: 'line-clamp-none', css: '.line-clamp-none { overflow: visible; display: block; -webkit-box-orient: horizontal; -webkit-line-clamp: none; }' });
144
+
145
+ // Vertical Align
146
+ ['baseline', 'top', 'middle', 'bottom', 'text-top', 'text-bottom', 'sub', 'super'].forEach(align => {
147
+ classes.push({ name: `align-${align}`, css: `.align-${align} { vertical-align: ${align}; }` });
148
+ });
149
+
150
+ // Whitespace
151
+ ['normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'break-spaces'].forEach(ws => {
152
+ classes.push({ name: `whitespace-${ws}`, css: `.whitespace-${ws} { white-space: ${ws}; }` });
153
+ });
154
+
155
+ // Word Break
156
+ classes.push({ name: 'break-normal', css: '.break-normal { overflow-wrap: normal; word-break: normal; }' });
157
+ classes.push({ name: 'break-words', css: '.break-words { overflow-wrap: break-word; }' });
158
+ classes.push({ name: 'break-all', css: '.break-all { word-break: break-all; }' });
159
+ classes.push({ name: 'break-keep', css: '.break-keep { word-break: keep-all; }' });
160
+
161
+ // Hyphens
162
+ ['none', 'manual', 'auto'].forEach(h => {
163
+ classes.push({ name: `hyphens-${h}`, css: `.hyphens-${h} { hyphens: ${h}; }` });
164
+ });
165
+
166
+ // Text Wrap
167
+ classes.push({ name: 'text-wrap', css: '.text-wrap { text-wrap: wrap; }' });
168
+ classes.push({ name: 'text-nowrap', css: '.text-nowrap { text-wrap: nowrap; }' });
169
+ classes.push({ name: 'text-balance', css: '.text-balance { text-wrap: balance; }' });
170
+ classes.push({ name: 'text-pretty', css: '.text-pretty { text-wrap: pretty; }' });
171
+
172
+ // Content
173
+ classes.push({ name: 'content-none', css: '.content-none { content: none; }' });
174
+
175
+ // List Style Type
176
+ classes.push({ name: 'list-none', css: '.list-none { list-style-type: none; }' });
177
+ classes.push({ name: 'list-disc', css: '.list-disc { list-style-type: disc; }' });
178
+ classes.push({ name: 'list-decimal', css: '.list-decimal { list-style-type: decimal; }' });
179
+
180
+ // List Style Position
181
+ classes.push({ name: 'list-inside', css: '.list-inside { list-style-position: inside; }' });
182
+ classes.push({ name: 'list-outside', css: '.list-outside { list-style-position: outside; }' });
183
+
184
+ return {
185
+ css: classes.map(c => c.css).join('\n'),
186
+ classes
187
+ };
188
+ }
package/src/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // JasminCSS - Main Entry Point
2
+ export { compileCSS, scanForUsedClasses } from './core/compiler.js';
3
+ export { loadConfig, resolveConfig } from './config/loader.js';
4
+ export { defaultConfig, templates } from './config/defaults.js';
5
+ export { generateUtilities } from './core/utilities/index.js';
6
+ export { generateComponents } from './components/index.js';
7
+ export { generateBase } from './core/base.js';
@@ -0,0 +1,154 @@
1
+ /**
2
+ * JasminCSS Accordion Component
3
+ * Handles collapsible accordion panels with accessibility
4
+ */
5
+
6
+ class Accordion {
7
+ constructor(element, options = {}) {
8
+ this.element = element;
9
+ this.options = {
10
+ alwaysOpen: false, // Allow multiple panels open
11
+ ...options
12
+ };
13
+ this.items = [];
14
+
15
+ this.init();
16
+ }
17
+
18
+ init() {
19
+ const items = this.element.querySelectorAll('.accordion-item');
20
+
21
+ items.forEach((item, index) => {
22
+ const trigger = item.querySelector('.accordion-trigger, [data-accordion-trigger]');
23
+ const content = item.querySelector('.accordion-content, [data-accordion-content]');
24
+
25
+ if (!trigger || !content) return;
26
+
27
+ // Generate IDs if not present
28
+ const triggerId = trigger.id || `accordion-trigger-${Date.now()}-${index}`;
29
+ const contentId = content.id || `accordion-content-${Date.now()}-${index}`;
30
+
31
+ trigger.id = triggerId;
32
+ content.id = contentId;
33
+
34
+ // Set ARIA attributes
35
+ trigger.setAttribute('aria-expanded', item.classList.contains('open') ? 'true' : 'false');
36
+ trigger.setAttribute('aria-controls', contentId);
37
+ content.setAttribute('aria-labelledby', triggerId);
38
+ content.setAttribute('role', 'region');
39
+
40
+ // Store reference
41
+ this.items.push({ item, trigger, content });
42
+
43
+ // Click handler
44
+ trigger.addEventListener('click', (e) => {
45
+ e.preventDefault();
46
+ this.toggleItem(index);
47
+ });
48
+
49
+ // Keyboard handler
50
+ trigger.addEventListener('keydown', (e) => this.handleKeydown(e, index));
51
+ });
52
+ }
53
+
54
+ toggleItem(index) {
55
+ const { item, trigger, content } = this.items[index];
56
+ const isOpen = item.classList.contains('open');
57
+
58
+ if (isOpen) {
59
+ this.closeItem(index);
60
+ } else {
61
+ // Close others if not alwaysOpen
62
+ if (!this.options.alwaysOpen) {
63
+ this.items.forEach((_, i) => {
64
+ if (i !== index) this.closeItem(i);
65
+ });
66
+ }
67
+ this.openItem(index);
68
+ }
69
+ }
70
+
71
+ openItem(index) {
72
+ const { item, trigger, content } = this.items[index];
73
+
74
+ item.classList.add('open');
75
+ trigger.setAttribute('aria-expanded', 'true');
76
+
77
+ // Dispatch event
78
+ this.element.dispatchEvent(new CustomEvent('accordion:open', {
79
+ detail: { index, item, trigger, content }
80
+ }));
81
+ }
82
+
83
+ closeItem(index) {
84
+ const { item, trigger, content } = this.items[index];
85
+
86
+ item.classList.remove('open');
87
+ trigger.setAttribute('aria-expanded', 'false');
88
+
89
+ // Dispatch event
90
+ this.element.dispatchEvent(new CustomEvent('accordion:close', {
91
+ detail: { index, item, trigger, content }
92
+ }));
93
+ }
94
+
95
+ openAll() {
96
+ this.items.forEach((_, i) => this.openItem(i));
97
+ }
98
+
99
+ closeAll() {
100
+ this.items.forEach((_, i) => this.closeItem(i));
101
+ }
102
+
103
+ handleKeydown(e, index) {
104
+ const triggers = this.items.map(i => i.trigger);
105
+
106
+ switch (e.key) {
107
+ case 'ArrowDown':
108
+ e.preventDefault();
109
+ const nextIndex = index < triggers.length - 1 ? index + 1 : 0;
110
+ triggers[nextIndex].focus();
111
+ break;
112
+
113
+ case 'ArrowUp':
114
+ e.preventDefault();
115
+ const prevIndex = index > 0 ? index - 1 : triggers.length - 1;
116
+ triggers[prevIndex].focus();
117
+ break;
118
+
119
+ case 'Home':
120
+ e.preventDefault();
121
+ triggers[0].focus();
122
+ break;
123
+
124
+ case 'End':
125
+ e.preventDefault();
126
+ triggers[triggers.length - 1].focus();
127
+ break;
128
+ }
129
+ }
130
+
131
+ destroy() {
132
+ this.items.forEach(({ trigger }) => {
133
+ trigger.removeAttribute('aria-expanded');
134
+ trigger.removeAttribute('aria-controls');
135
+ });
136
+ }
137
+
138
+ // Static methods
139
+ static initAll(selector = '[data-accordion]') {
140
+ document.querySelectorAll(selector).forEach(el => {
141
+ if (!el._jasminAccordion) {
142
+ el._jasminAccordion = new Accordion(el, {
143
+ alwaysOpen: el.dataset.accordionAlwaysOpen === 'true'
144
+ });
145
+ }
146
+ });
147
+ }
148
+
149
+ static getInstance(element) {
150
+ return element._jasminAccordion;
151
+ }
152
+ }
153
+
154
+ export default Accordion;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * JasminCSS Alert & Toast Component
3
+ * Handles dismissible alerts and toast notifications
4
+ */
5
+
6
+ class Alert {
7
+ constructor(element, options = {}) {
8
+ this.element = element;
9
+ this.options = {
10
+ dismissible: true,
11
+ autoDismiss: false,
12
+ autoDismissDelay: 5000,
13
+ ...options
14
+ };
15
+
16
+ this.init();
17
+ }
18
+
19
+ init() {
20
+ // Set ARIA
21
+ this.element.setAttribute('role', 'alert');
22
+
23
+ // Close button
24
+ if (this.options.dismissible) {
25
+ const closeBtn = this.element.querySelector('[data-alert-close], .alert-close, .btn-close');
26
+ if (closeBtn) {
27
+ closeBtn.addEventListener('click', () => this.dismiss());
28
+ }
29
+ }
30
+
31
+ // Auto dismiss
32
+ if (this.options.autoDismiss) {
33
+ setTimeout(() => this.dismiss(), this.options.autoDismissDelay);
34
+ }
35
+ }
36
+
37
+ dismiss() {
38
+ this.element.dispatchEvent(new CustomEvent('alert:dismiss', { detail: { alert: this } }));
39
+
40
+ this.element.classList.add('dismissing');
41
+
42
+ setTimeout(() => {
43
+ this.element.remove();
44
+ this.element.dispatchEvent(new CustomEvent('alert:dismissed', { detail: { alert: this } }));
45
+ }, 150);
46
+ }
47
+
48
+ // Static methods
49
+ static initAll(selector = '[data-alert]') {
50
+ document.querySelectorAll(selector).forEach(el => {
51
+ if (!el._jasminAlert) {
52
+ el._jasminAlert = new Alert(el, {
53
+ dismissible: el.dataset.alertDismissible !== 'false',
54
+ autoDismiss: el.dataset.alertAutoDismiss === 'true',
55
+ autoDismissDelay: parseInt(el.dataset.alertDelay) || 5000
56
+ });
57
+ }
58
+ });
59
+ }
60
+
61
+ static getInstance(element) {
62
+ return element._jasminAlert;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Toast notification system
68
+ */
69
+ class Toast {
70
+ static container = null;
71
+ static queue = [];
72
+
73
+ static getContainer(position = 'top-right') {
74
+ if (!Toast.container || Toast.container.dataset.position !== position) {
75
+ Toast.container?.remove();
76
+
77
+ Toast.container = document.createElement('div');
78
+ Toast.container.className = `toast-container toast-${position}`;
79
+ Toast.container.dataset.position = position;
80
+ Toast.container.setAttribute('aria-live', 'polite');
81
+ Toast.container.setAttribute('aria-atomic', 'true');
82
+ document.body.appendChild(Toast.container);
83
+ }
84
+
85
+ return Toast.container;
86
+ }
87
+
88
+ static show(options = {}) {
89
+ const config = {
90
+ title: '',
91
+ message: '',
92
+ type: 'default', // default, success, error, warning, info
93
+ duration: 5000,
94
+ position: 'top-right',
95
+ dismissible: true,
96
+ icon: true,
97
+ ...options
98
+ };
99
+
100
+ const container = Toast.getContainer(config.position);
101
+
102
+ // Create toast element
103
+ const toast = document.createElement('div');
104
+ toast.className = `toast toast-${config.type}`;
105
+ toast.setAttribute('role', 'alert');
106
+
107
+ // Icon
108
+ const icons = {
109
+ success: '✓',
110
+ error: '✕',
111
+ warning: '⚠',
112
+ info: 'ℹ',
113
+ default: ''
114
+ };
115
+
116
+ let html = '';
117
+
118
+ if (config.icon && icons[config.type]) {
119
+ html += `<div class="toast-icon">${icons[config.type]}</div>`;
120
+ }
121
+
122
+ html += '<div class="toast-content">';
123
+ if (config.title) {
124
+ html += `<div class="toast-title">${config.title}</div>`;
125
+ }
126
+ if (config.message) {
127
+ html += `<div class="toast-message">${config.message}</div>`;
128
+ }
129
+ html += '</div>';
130
+
131
+ if (config.dismissible) {
132
+ html += '<button class="toast-close" aria-label="Close">×</button>';
133
+ }
134
+
135
+ toast.innerHTML = html;
136
+
137
+ // Add to container
138
+ container.appendChild(toast);
139
+
140
+ // Trigger animation
141
+ requestAnimationFrame(() => {
142
+ toast.classList.add('show');
143
+ });
144
+
145
+ // Close button
146
+ const closeBtn = toast.querySelector('.toast-close');
147
+ if (closeBtn) {
148
+ closeBtn.addEventListener('click', () => Toast.hide(toast));
149
+ }
150
+
151
+ // Auto dismiss
152
+ if (config.duration > 0) {
153
+ setTimeout(() => Toast.hide(toast), config.duration);
154
+ }
155
+
156
+ return toast;
157
+ }
158
+
159
+ static hide(toast) {
160
+ toast.classList.remove('show');
161
+ toast.classList.add('hiding');
162
+
163
+ setTimeout(() => {
164
+ toast.remove();
165
+
166
+ // Remove container if empty
167
+ if (Toast.container && Toast.container.children.length === 0) {
168
+ Toast.container.remove();
169
+ Toast.container = null;
170
+ }
171
+ }, 300);
172
+ }
173
+
174
+ static success(message, options = {}) {
175
+ return Toast.show({ message, type: 'success', ...options });
176
+ }
177
+
178
+ static error(message, options = {}) {
179
+ return Toast.show({ message, type: 'error', ...options });
180
+ }
181
+
182
+ static warning(message, options = {}) {
183
+ return Toast.show({ message, type: 'warning', ...options });
184
+ }
185
+
186
+ static info(message, options = {}) {
187
+ return Toast.show({ message, type: 'info', ...options });
188
+ }
189
+
190
+ static clearAll() {
191
+ if (Toast.container) {
192
+ Array.from(Toast.container.children).forEach(toast => Toast.hide(toast));
193
+ }
194
+ }
195
+ }
196
+
197
+ export { Alert, Toast };
198
+ export default Alert;