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,226 @@
1
+ /**
2
+ * JasminCSS Carousel Component
3
+ * Handles image/content carousels with autoplay and accessibility
4
+ */
5
+
6
+ class Carousel {
7
+ constructor(element, options = {}) {
8
+ this.element = element;
9
+ this.options = {
10
+ autoplay: false,
11
+ interval: 5000,
12
+ pause: 'hover',
13
+ wrap: true,
14
+ keyboard: true,
15
+ touch: true,
16
+ ...options
17
+ };
18
+
19
+ this.inner = element.querySelector('.carousel-inner');
20
+ this.items = Array.from(element.querySelectorAll('.carousel-item'));
21
+ this.indicators = Array.from(element.querySelectorAll('.carousel-indicator, [data-carousel-slide-to]'));
22
+ this.prevBtn = element.querySelector('.carousel-control-prev, [data-carousel-prev]');
23
+ this.nextBtn = element.querySelector('.carousel-control-next, [data-carousel-next]');
24
+
25
+ this.currentIndex = 0;
26
+ this.isAnimating = false;
27
+ this.autoplayInterval = null;
28
+ this.touchStartX = 0;
29
+ this.touchEndX = 0;
30
+
31
+ this.init();
32
+ }
33
+
34
+ init() {
35
+ // Find initial active item
36
+ this.items.forEach((item, index) => {
37
+ if (item.classList.contains('active')) {
38
+ this.currentIndex = index;
39
+ }
40
+ item.setAttribute('role', 'group');
41
+ item.setAttribute('aria-roledescription', 'slide');
42
+ item.setAttribute('aria-label', `Slide ${index + 1} of ${this.items.length}`);
43
+ });
44
+
45
+ // Set ARIA
46
+ this.element.setAttribute('role', 'region');
47
+ this.element.setAttribute('aria-roledescription', 'carousel');
48
+
49
+ // Control buttons
50
+ if (this.prevBtn) {
51
+ this.prevBtn.addEventListener('click', (e) => {
52
+ e.preventDefault();
53
+ this.prev();
54
+ });
55
+ }
56
+
57
+ if (this.nextBtn) {
58
+ this.nextBtn.addEventListener('click', (e) => {
59
+ e.preventDefault();
60
+ this.next();
61
+ });
62
+ }
63
+
64
+ // Indicators
65
+ this.indicators.forEach((indicator, index) => {
66
+ indicator.addEventListener('click', (e) => {
67
+ e.preventDefault();
68
+ this.goTo(index);
69
+ });
70
+ });
71
+
72
+ // Keyboard navigation
73
+ if (this.options.keyboard) {
74
+ this.element.setAttribute('tabindex', '0');
75
+ this.element.addEventListener('keydown', (e) => {
76
+ if (e.key === 'ArrowLeft') {
77
+ e.preventDefault();
78
+ this.prev();
79
+ } else if (e.key === 'ArrowRight') {
80
+ e.preventDefault();
81
+ this.next();
82
+ }
83
+ });
84
+ }
85
+
86
+ // Touch support
87
+ if (this.options.touch) {
88
+ this.element.addEventListener('touchstart', (e) => {
89
+ this.touchStartX = e.touches[0].clientX;
90
+ }, { passive: true });
91
+
92
+ this.element.addEventListener('touchend', (e) => {
93
+ this.touchEndX = e.changedTouches[0].clientX;
94
+ this.handleSwipe();
95
+ }, { passive: true });
96
+ }
97
+
98
+ // Pause on hover
99
+ if (this.options.pause === 'hover') {
100
+ this.element.addEventListener('mouseenter', () => this.stopAutoplay());
101
+ this.element.addEventListener('mouseleave', () => {
102
+ if (this.options.autoplay) this.startAutoplay();
103
+ });
104
+ }
105
+
106
+ // Start autoplay
107
+ if (this.options.autoplay) {
108
+ this.startAutoplay();
109
+ }
110
+
111
+ // Set initial state
112
+ this.updateSlide();
113
+ }
114
+
115
+ goTo(index) {
116
+ if (this.isAnimating || index === this.currentIndex) return;
117
+
118
+ if (!this.options.wrap) {
119
+ if (index < 0 || index >= this.items.length) return;
120
+ }
121
+
122
+ // Handle wrapping
123
+ if (index < 0) {
124
+ index = this.items.length - 1;
125
+ } else if (index >= this.items.length) {
126
+ index = 0;
127
+ }
128
+
129
+ this.isAnimating = true;
130
+ const direction = index > this.currentIndex ? 'next' : 'prev';
131
+
132
+ // Dispatch before event
133
+ this.element.dispatchEvent(new CustomEvent('carousel:slide', {
134
+ detail: { from: this.currentIndex, to: index, direction }
135
+ }));
136
+
137
+ this.currentIndex = index;
138
+ this.updateSlide();
139
+
140
+ setTimeout(() => {
141
+ this.isAnimating = false;
142
+ this.element.dispatchEvent(new CustomEvent('carousel:slid', {
143
+ detail: { index: this.currentIndex }
144
+ }));
145
+ }, 600);
146
+ }
147
+
148
+ next() {
149
+ this.goTo(this.currentIndex + 1);
150
+ }
151
+
152
+ prev() {
153
+ this.goTo(this.currentIndex - 1);
154
+ }
155
+
156
+ updateSlide() {
157
+ // Update items
158
+ this.items.forEach((item, index) => {
159
+ item.classList.toggle('active', index === this.currentIndex);
160
+ item.setAttribute('aria-hidden', index !== this.currentIndex);
161
+ });
162
+
163
+ // Update indicators
164
+ this.indicators.forEach((indicator, index) => {
165
+ indicator.classList.toggle('active', index === this.currentIndex);
166
+ indicator.setAttribute('aria-selected', index === this.currentIndex);
167
+ });
168
+
169
+ // Update live region
170
+ this.element.setAttribute('aria-live', 'polite');
171
+ }
172
+
173
+ handleSwipe() {
174
+ const diff = this.touchStartX - this.touchEndX;
175
+ const threshold = 50;
176
+
177
+ if (Math.abs(diff) > threshold) {
178
+ if (diff > 0) {
179
+ this.next();
180
+ } else {
181
+ this.prev();
182
+ }
183
+ }
184
+ }
185
+
186
+ startAutoplay() {
187
+ if (this.autoplayInterval) return;
188
+
189
+ this.autoplayInterval = setInterval(() => {
190
+ this.next();
191
+ }, this.options.interval);
192
+ }
193
+
194
+ stopAutoplay() {
195
+ if (this.autoplayInterval) {
196
+ clearInterval(this.autoplayInterval);
197
+ this.autoplayInterval = null;
198
+ }
199
+ }
200
+
201
+ destroy() {
202
+ this.stopAutoplay();
203
+ }
204
+
205
+ // Static methods
206
+ static initAll(selector = '[data-carousel]') {
207
+ document.querySelectorAll(selector).forEach(el => {
208
+ if (!el._jasminCarousel) {
209
+ el._jasminCarousel = new Carousel(el, {
210
+ autoplay: el.dataset.carouselAutoplay === 'true',
211
+ interval: parseInt(el.dataset.carouselInterval) || 5000,
212
+ pause: el.dataset.carouselPause || 'hover',
213
+ wrap: el.dataset.carouselWrap !== 'false',
214
+ keyboard: el.dataset.carouselKeyboard !== 'false',
215
+ touch: el.dataset.carouselTouch !== 'false'
216
+ });
217
+ }
218
+ });
219
+ }
220
+
221
+ static getInstance(element) {
222
+ return element._jasminCarousel;
223
+ }
224
+ }
225
+
226
+ export default Carousel;
@@ -0,0 +1,166 @@
1
+ /**
2
+ * JasminCSS Dropdown Component
3
+ * Handles dropdown menus with keyboard navigation and accessibility
4
+ */
5
+
6
+ class Dropdown {
7
+ constructor(element, options = {}) {
8
+ this.element = element;
9
+ this.toggle = element.querySelector('[data-dropdown-toggle]') || element.querySelector('.dropdown-toggle');
10
+ this.menu = element.querySelector('[data-dropdown-menu]') || element.querySelector('.dropdown-menu');
11
+ this.isOpen = false;
12
+ this.options = {
13
+ closeOnClickOutside: true,
14
+ closeOnEscape: true,
15
+ closeOnItemClick: true,
16
+ ...options
17
+ };
18
+
19
+ if (!this.toggle || !this.menu) return;
20
+
21
+ this.init();
22
+ }
23
+
24
+ init() {
25
+ // Toggle click
26
+ this.toggle.addEventListener('click', (e) => {
27
+ e.preventDefault();
28
+ e.stopPropagation();
29
+ this.toggle();
30
+ });
31
+
32
+ // Keyboard navigation
33
+ this.element.addEventListener('keydown', (e) => this.handleKeydown(e));
34
+
35
+ // Close on click outside
36
+ if (this.options.closeOnClickOutside) {
37
+ document.addEventListener('click', (e) => {
38
+ if (this.isOpen && !this.element.contains(e.target)) {
39
+ this.close();
40
+ }
41
+ });
42
+ }
43
+
44
+ // Close on escape
45
+ if (this.options.closeOnEscape) {
46
+ document.addEventListener('keydown', (e) => {
47
+ if (e.key === 'Escape' && this.isOpen) {
48
+ this.close();
49
+ this.toggle.focus();
50
+ }
51
+ });
52
+ }
53
+
54
+ // Close on item click
55
+ if (this.options.closeOnItemClick) {
56
+ this.menu.querySelectorAll('.dropdown-item').forEach(item => {
57
+ item.addEventListener('click', () => this.close());
58
+ });
59
+ }
60
+
61
+ // Set ARIA attributes
62
+ this.toggle.setAttribute('aria-haspopup', 'true');
63
+ this.toggle.setAttribute('aria-expanded', 'false');
64
+ this.menu.setAttribute('role', 'menu');
65
+ this.menu.querySelectorAll('.dropdown-item').forEach(item => {
66
+ item.setAttribute('role', 'menuitem');
67
+ });
68
+ }
69
+
70
+ toggle() {
71
+ this.isOpen ? this.close() : this.open();
72
+ }
73
+
74
+ open() {
75
+ this.isOpen = true;
76
+ this.element.classList.add('open');
77
+ this.menu.classList.add('show');
78
+ this.toggle.setAttribute('aria-expanded', 'true');
79
+
80
+ // Focus first item
81
+ const firstItem = this.menu.querySelector('.dropdown-item:not([disabled])');
82
+ if (firstItem) firstItem.focus();
83
+
84
+ // Dispatch event
85
+ this.element.dispatchEvent(new CustomEvent('dropdown:open', { detail: { dropdown: this } }));
86
+ }
87
+
88
+ close() {
89
+ this.isOpen = false;
90
+ this.element.classList.remove('open');
91
+ this.menu.classList.remove('show');
92
+ this.toggle.setAttribute('aria-expanded', 'false');
93
+
94
+ // Dispatch event
95
+ this.element.dispatchEvent(new CustomEvent('dropdown:close', { detail: { dropdown: this } }));
96
+ }
97
+
98
+ handleKeydown(e) {
99
+ const items = Array.from(this.menu.querySelectorAll('.dropdown-item:not([disabled])'));
100
+ const currentIndex = items.indexOf(document.activeElement);
101
+
102
+ switch (e.key) {
103
+ case 'ArrowDown':
104
+ e.preventDefault();
105
+ if (!this.isOpen) {
106
+ this.open();
107
+ } else {
108
+ const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
109
+ items[nextIndex]?.focus();
110
+ }
111
+ break;
112
+
113
+ case 'ArrowUp':
114
+ e.preventDefault();
115
+ if (this.isOpen) {
116
+ const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
117
+ items[prevIndex]?.focus();
118
+ }
119
+ break;
120
+
121
+ case 'Home':
122
+ e.preventDefault();
123
+ items[0]?.focus();
124
+ break;
125
+
126
+ case 'End':
127
+ e.preventDefault();
128
+ items[items.length - 1]?.focus();
129
+ break;
130
+
131
+ case 'Enter':
132
+ case ' ':
133
+ if (document.activeElement === this.toggle) {
134
+ e.preventDefault();
135
+ this.toggle();
136
+ }
137
+ break;
138
+
139
+ case 'Tab':
140
+ this.close();
141
+ break;
142
+ }
143
+ }
144
+
145
+ destroy() {
146
+ this.close();
147
+ this.toggle.removeAttribute('aria-haspopup');
148
+ this.toggle.removeAttribute('aria-expanded');
149
+ }
150
+
151
+ // Static method to initialize all dropdowns
152
+ static initAll(selector = '[data-dropdown]') {
153
+ document.querySelectorAll(selector).forEach(el => {
154
+ if (!el._jasminDropdown) {
155
+ el._jasminDropdown = new Dropdown(el);
156
+ }
157
+ });
158
+ }
159
+
160
+ // Static method to get instance
161
+ static getInstance(element) {
162
+ return element._jasminDropdown;
163
+ }
164
+ }
165
+
166
+ export default Dropdown;
@@ -0,0 +1,169 @@
1
+ /**
2
+ * JasminCSS Modal Component
3
+ * Handles modal dialogs with focus trapping and accessibility
4
+ */
5
+
6
+ class Modal {
7
+ constructor(element, options = {}) {
8
+ this.element = element;
9
+ this.isOpen = false;
10
+ this.previousActiveElement = null;
11
+ this.options = {
12
+ backdrop: true,
13
+ keyboard: true,
14
+ focus: true,
15
+ ...options
16
+ };
17
+
18
+ this.backdrop = null;
19
+ this.init();
20
+ }
21
+
22
+ init() {
23
+ // Set ARIA attributes
24
+ this.element.setAttribute('role', 'dialog');
25
+ this.element.setAttribute('aria-modal', 'true');
26
+ this.element.setAttribute('tabindex', '-1');
27
+
28
+ // Close button handlers
29
+ this.element.querySelectorAll('[data-modal-close], .modal-close').forEach(btn => {
30
+ btn.addEventListener('click', () => this.close());
31
+ });
32
+
33
+ // Keyboard handling
34
+ this.element.addEventListener('keydown', (e) => {
35
+ if (e.key === 'Escape' && this.options.keyboard) {
36
+ this.close();
37
+ }
38
+ if (e.key === 'Tab') {
39
+ this.trapFocus(e);
40
+ }
41
+ });
42
+ }
43
+
44
+ open() {
45
+ if (this.isOpen) return;
46
+
47
+ this.previousActiveElement = document.activeElement;
48
+ this.isOpen = true;
49
+
50
+ // Create backdrop
51
+ if (this.options.backdrop) {
52
+ this.backdrop = document.createElement('div');
53
+ this.backdrop.className = 'modal-backdrop show';
54
+ document.body.appendChild(this.backdrop);
55
+
56
+ if (this.options.backdrop !== 'static') {
57
+ this.backdrop.addEventListener('click', () => this.close());
58
+ }
59
+ }
60
+
61
+ // Show modal
62
+ this.element.classList.add('show');
63
+ this.element.style.display = 'block';
64
+ document.body.classList.add('modal-open');
65
+ document.body.style.overflow = 'hidden';
66
+
67
+ // Focus management
68
+ if (this.options.focus) {
69
+ const autoFocus = this.element.querySelector('[autofocus]');
70
+ const firstFocusable = this.getFocusableElements()[0];
71
+ (autoFocus || firstFocusable || this.element).focus();
72
+ }
73
+
74
+ // Dispatch event
75
+ this.element.dispatchEvent(new CustomEvent('modal:open', { detail: { modal: this } }));
76
+ }
77
+
78
+ close() {
79
+ if (!this.isOpen) return;
80
+
81
+ this.isOpen = false;
82
+
83
+ // Remove backdrop
84
+ if (this.backdrop) {
85
+ this.backdrop.classList.remove('show');
86
+ setTimeout(() => {
87
+ this.backdrop?.remove();
88
+ this.backdrop = null;
89
+ }, 150);
90
+ }
91
+
92
+ // Hide modal
93
+ this.element.classList.remove('show');
94
+ setTimeout(() => {
95
+ if (!this.isOpen) {
96
+ this.element.style.display = 'none';
97
+ }
98
+ }, 150);
99
+
100
+ document.body.classList.remove('modal-open');
101
+ document.body.style.overflow = '';
102
+
103
+ // Restore focus
104
+ if (this.previousActiveElement) {
105
+ this.previousActiveElement.focus();
106
+ }
107
+
108
+ // Dispatch event
109
+ this.element.dispatchEvent(new CustomEvent('modal:close', { detail: { modal: this } }));
110
+ }
111
+
112
+ toggle() {
113
+ this.isOpen ? this.close() : this.open();
114
+ }
115
+
116
+ getFocusableElements() {
117
+ return Array.from(this.element.querySelectorAll(
118
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
119
+ )).filter(el => !el.disabled && el.offsetParent !== null);
120
+ }
121
+
122
+ trapFocus(e) {
123
+ const focusable = this.getFocusableElements();
124
+ const firstFocusable = focusable[0];
125
+ const lastFocusable = focusable[focusable.length - 1];
126
+
127
+ if (e.shiftKey) {
128
+ if (document.activeElement === firstFocusable) {
129
+ e.preventDefault();
130
+ lastFocusable.focus();
131
+ }
132
+ } else {
133
+ if (document.activeElement === lastFocusable) {
134
+ e.preventDefault();
135
+ firstFocusable.focus();
136
+ }
137
+ }
138
+ }
139
+
140
+ destroy() {
141
+ this.close();
142
+ }
143
+
144
+ // Static methods
145
+ static initAll(selector = '[data-modal]') {
146
+ document.querySelectorAll(selector).forEach(el => {
147
+ if (!el._jasminModal) {
148
+ el._jasminModal = new Modal(el);
149
+ }
150
+ });
151
+
152
+ // Init triggers
153
+ document.querySelectorAll('[data-modal-target]').forEach(trigger => {
154
+ trigger.addEventListener('click', (e) => {
155
+ e.preventDefault();
156
+ const target = document.querySelector(trigger.dataset.modalTarget);
157
+ if (target?._jasminModal) {
158
+ target._jasminModal.open();
159
+ }
160
+ });
161
+ });
162
+ }
163
+
164
+ static getInstance(element) {
165
+ return element._jasminModal;
166
+ }
167
+ }
168
+
169
+ export default Modal;