basecoat-css 0.1.2 → 0.2.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.
@@ -0,0 +1,75 @@
1
+ (() => {
2
+ const initTabs = (tabsComponent) => {
3
+ const tablist = tabsComponent.querySelector('[role="tablist"]');
4
+ if (!tablist) return;
5
+
6
+ const tabs = Array.from(tablist.querySelectorAll('[role="tab"]'));
7
+ const panels = tabs.map(tab => document.getElementById(tab.getAttribute('aria-controls'))).filter(Boolean);
8
+
9
+ const selectTab = (tabToSelect) => {
10
+ tabs.forEach((tab, index) => {
11
+ tab.setAttribute('aria-selected', 'false');
12
+ tab.setAttribute('tabindex', '-1');
13
+ if (panels[index]) panels[index].hidden = true;
14
+ });
15
+
16
+ tabToSelect.setAttribute('aria-selected', 'true');
17
+ tabToSelect.setAttribute('tabindex', '0');
18
+ const activePanel = document.getElementById(tabToSelect.getAttribute('aria-controls'));
19
+ if (activePanel) activePanel.hidden = false;
20
+ };
21
+
22
+ tablist.addEventListener('click', (e) => {
23
+ const clickedTab = e.target.closest('[role="tab"]');
24
+ if (clickedTab) selectTab(clickedTab);
25
+ });
26
+
27
+ tablist.addEventListener('keydown', (e) => {
28
+ const currentTab = e.target;
29
+ if (!tabs.includes(currentTab)) return;
30
+
31
+ let nextTab;
32
+ const currentIndex = tabs.indexOf(currentTab);
33
+
34
+ switch (e.key) {
35
+ case 'ArrowRight':
36
+ nextTab = tabs[(currentIndex + 1) % tabs.length];
37
+ break;
38
+ case 'ArrowLeft':
39
+ nextTab = tabs[(currentIndex - 1 + tabs.length) % tabs.length];
40
+ break;
41
+ case 'Home':
42
+ nextTab = tabs[0];
43
+ break;
44
+ case 'End':
45
+ nextTab = tabs[tabs.length - 1];
46
+ break;
47
+ default:
48
+ return;
49
+ }
50
+
51
+ e.preventDefault();
52
+ selectTab(nextTab);
53
+ nextTab.focus();
54
+ });
55
+
56
+ tabsComponent.dataset.tabsInitialized = true;
57
+ };
58
+
59
+ document.querySelectorAll('.tabs:not([data-tabs-initialized])').forEach(initTabs);
60
+
61
+ const observer = new MutationObserver((mutations) => {
62
+ mutations.forEach((mutation) => {
63
+ mutation.addedNodes.forEach((node) => {
64
+ if (node.nodeType === Node.ELEMENT_NODE) {
65
+ if (node.matches('.tabs:not([data-tabs-initialized])')) {
66
+ initTabs(node);
67
+ }
68
+ node.querySelectorAll('.tabs:not([data-tabs-initialized])').forEach(initTabs);
69
+ }
70
+ });
71
+ });
72
+ });
73
+
74
+ observer.observe(document.body, { childList: true, subtree: true });
75
+ })();
@@ -0,0 +1 @@
1
+ (()=>{const e=e=>{const t=e.querySelector('[role="tablist"]');if(!t)return;const a=Array.from(t.querySelectorAll('[role="tab"]')),r=a.map((e=>document.getElementById(e.getAttribute("aria-controls")))).filter(Boolean),n=e=>{a.forEach(((e,t)=>{e.setAttribute("aria-selected","false"),e.setAttribute("tabindex","-1"),r[t]&&(r[t].hidden=!0)})),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0");const t=document.getElementById(e.getAttribute("aria-controls"));t&&(t.hidden=!1)};t.addEventListener("click",(e=>{const t=e.target.closest('[role="tab"]');t&&n(t)})),t.addEventListener("keydown",(e=>{const t=e.target;if(!a.includes(t))return;let r;const o=a.indexOf(t);switch(e.key){case"ArrowRight":r=a[(o+1)%a.length];break;case"ArrowLeft":r=a[(o-1+a.length)%a.length];break;case"Home":r=a[0];break;case"End":r=a[a.length-1];break;default:return}e.preventDefault(),n(r),r.focus()})),e.dataset.tabsInitialized=!0};document.querySelectorAll(".tabs:not([data-tabs-initialized])").forEach(e);new MutationObserver((t=>{t.forEach((t=>{t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&(t.matches(".tabs:not([data-tabs-initialized])")&&e(t),t.querySelectorAll(".tabs:not([data-tabs-initialized])").forEach(e))}))}))})).observe(document.body,{childList:!0,subtree:!0})})();
@@ -0,0 +1,196 @@
1
+ (() => {
2
+ let toaster;
3
+ const toasts = new WeakMap();
4
+ let isPaused = false;
5
+ const ICONS = {
6
+ success: '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',
7
+ error: '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',
8
+ info: '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',
9
+ warning: '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>'
10
+ };
11
+
12
+ function initToaster(toasterElement) {
13
+ if (toasterElement.dataset.toasterInitialized) return;
14
+ toaster = toasterElement;
15
+
16
+ toaster.addEventListener('mouseenter', pauseAllTimeouts);
17
+ toaster.addEventListener('mouseleave', resumeAllTimeouts);
18
+ toaster.addEventListener('click', (e) => {
19
+ const actionLink = e.target.closest('.toast footer a');
20
+ const actionButton = e.target.closest('.toast footer button');
21
+ if (actionLink || actionButton) {
22
+ closeToast(e.target.closest('.toast'));
23
+ }
24
+ });
25
+
26
+ toaster.querySelectorAll('.toast:not([data-toast-initialized])').forEach(initToast);
27
+ toaster.dataset.toasterInitialized = 'true';
28
+ }
29
+
30
+ function initToast(element) {
31
+ if (element.dataset.toastInitialized) return;
32
+
33
+ const duration = parseInt(element.dataset.duration);
34
+ const timeoutDuration = duration !== -1
35
+ ? duration || (element.dataset.category === 'error' ? 5000 : 3000)
36
+ : -1;
37
+
38
+ const state = {
39
+ remainingTime: timeoutDuration,
40
+ timeoutId: null,
41
+ startTime: null,
42
+ };
43
+
44
+ if (timeoutDuration !== -1) {
45
+ if (isPaused) {
46
+ state.timeoutId = null;
47
+ } else {
48
+ state.startTime = Date.now();
49
+ state.timeoutId = setTimeout(() => closeToast(element), timeoutDuration);
50
+ }
51
+ }
52
+ toasts.set(element, state);
53
+
54
+ element.dataset.toastInitialized = 'true';
55
+ }
56
+
57
+ function pauseAllTimeouts() {
58
+ if (isPaused) return;
59
+
60
+ isPaused = true;
61
+
62
+ toaster.querySelectorAll('.toast:not([aria-hidden="true"])').forEach(element => {
63
+ if (!toasts.has(element)) return;
64
+
65
+ const state = toasts.get(element);
66
+ if (state.timeoutId) {
67
+ clearTimeout(state.timeoutId);
68
+ state.timeoutId = null;
69
+ state.remainingTime -= Date.now() - state.startTime;
70
+ }
71
+ });
72
+ }
73
+
74
+ function resumeAllTimeouts() {
75
+ if (!isPaused) return;
76
+
77
+ isPaused = false;
78
+
79
+ toaster.querySelectorAll('.toast:not([aria-hidden="true"])').forEach(element => {
80
+ if (!toasts.has(element)) return;
81
+
82
+ const state = toasts.get(element);
83
+ if (state.remainingTime !== -1 && !state.timeoutId) {
84
+ if (state.remainingTime > 0) {
85
+ state.startTime = Date.now();
86
+ state.timeoutId = setTimeout(() => closeToast(element), state.remainingTime);
87
+ } else {
88
+ closeToast(element);
89
+ }
90
+ }
91
+ });
92
+ }
93
+
94
+ function closeToast(element) {
95
+ if (!toasts.has(element)) return;
96
+
97
+ const state = toasts.get(element);
98
+ clearTimeout(state.timeoutId);
99
+ toasts.delete(element);
100
+
101
+ if (document.activeElement) document.activeElement.blur();
102
+ element.setAttribute('aria-hidden', 'true');
103
+ element.addEventListener('transitionend', () => element.remove(), { once: true });
104
+ }
105
+
106
+ function executeAction(button, toast) {
107
+ const actionString = button.dataset.toastAction;
108
+ if (!actionString) return;
109
+ try {
110
+ const func = new Function('close', actionString);
111
+ func(() => closeToast(toast));
112
+ } catch (e) {
113
+ console.error('Error executing toast action:', e);
114
+ }
115
+ }
116
+
117
+ function createToast(config) {
118
+ const {
119
+ category = 'info',
120
+ title,
121
+ description,
122
+ action,
123
+ cancel,
124
+ duration,
125
+ icon,
126
+ } = config;
127
+
128
+ const iconHtml = icon || (category && ICONS[category]) || '';
129
+ const titleHtml = title ? `<h2>${title}</h2>` : '';
130
+ const descriptionHtml = description ? `<p>${description}</p>` : '';
131
+ const actionHtml = action?.href
132
+ ? `<a href="${action.href}" class="btn" data-toast-action>${action.label}</a>`
133
+ : action?.onclick
134
+ ? `<button type="button" class="btn" data-toast-action onclick="${action.onclick}">${action.label}</button>`
135
+ : '';
136
+ const cancelHtml = cancel
137
+ ? `<button type="button" class="btn-outline h-6 text-xs px-2.5 rounded-sm" data-toast-cancel onclick="${cancel?.onclick}">${cancel.label}</button>`
138
+ : '';
139
+
140
+ const footerHtml = actionHtml || cancelHtml ? `<footer>${actionHtml}${cancelHtml}</footer>` : '';
141
+
142
+ const html = `
143
+ <div
144
+ class="toast"
145
+ role="${category === 'error' ? 'alert' : 'status'}"
146
+ aria-atomic="true"
147
+ ${category ? `data-category="${category}"` : ''}
148
+ ${duration !== undefined ? `data-duration="${duration}"` : ''}
149
+ >
150
+ <div class="toast-content">
151
+ ${iconHtml}
152
+ <section>
153
+ ${titleHtml}
154
+ ${descriptionHtml}
155
+ </section>
156
+ ${footerHtml}
157
+ </div>
158
+ </div>
159
+ </div>
160
+ `;
161
+ const template = document.createElement('template');
162
+ template.innerHTML = html.trim();
163
+ return template.content.firstChild;
164
+ }
165
+
166
+ const initialToaster = document.getElementById('toaster');
167
+ if (initialToaster) initToaster(initialToaster);
168
+
169
+ window.addEventListener('basecoat:toast', (e) => {
170
+ if (!toaster) {
171
+ console.error('Cannot create toast: toaster container not found on page.');
172
+ return;
173
+ }
174
+ const config = e.detail?.config || {};
175
+ const toastElement = createToast(config);
176
+ toaster.appendChild(toastElement);
177
+ });
178
+
179
+ const observer = new MutationObserver((mutations) => {
180
+ mutations.forEach((mutation) => {
181
+ mutation.addedNodes.forEach((node) => {
182
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
183
+
184
+ if (node.matches('#toaster')) {
185
+ initToaster(node);
186
+ }
187
+
188
+ if (toaster && node.matches('.toast:not([data-toast-initialized])')) {
189
+ initToast(node);
190
+ }
191
+ });
192
+ });
193
+ });
194
+
195
+ observer.observe(document.body, { childList: true, subtree: true });
196
+ })();
@@ -0,0 +1 @@
1
+ (()=>{let t;const e=new WeakMap;let n=!1;const o={success:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',error:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',info:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',warning:'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>'};function i(e){e.dataset.toasterInitialized||(t=e,t.addEventListener("mouseenter",r),t.addEventListener("mouseleave",s),t.addEventListener("click",(t=>{const e=t.target.closest(".toast footer a"),n=t.target.closest(".toast footer button");(e||n)&&d(t.target.closest(".toast"))})),t.querySelectorAll(".toast:not([data-toast-initialized])").forEach(a),t.dataset.toasterInitialized="true")}function a(t){if(t.dataset.toastInitialized)return;const o=parseInt(t.dataset.duration),i=-1!==o?o||("error"===t.dataset.category?5e3:3e3):-1,a={remainingTime:i,timeoutId:null,startTime:null};-1!==i&&(n?a.timeoutId=null:(a.startTime=Date.now(),a.timeoutId=setTimeout((()=>d(t)),i))),e.set(t,a),t.dataset.toastInitialized="true"}function r(){n||(n=!0,t.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((t=>{if(!e.has(t))return;const n=e.get(t);n.timeoutId&&(clearTimeout(n.timeoutId),n.timeoutId=null,n.remainingTime-=Date.now()-n.startTime)})))}function s(){n&&(n=!1,t.querySelectorAll('.toast:not([aria-hidden="true"])').forEach((t=>{if(!e.has(t))return;const n=e.get(t);-1===n.remainingTime||n.timeoutId||(n.remainingTime>0?(n.startTime=Date.now(),n.timeoutId=setTimeout((()=>d(t)),n.remainingTime)):d(t))})))}function d(t){if(!e.has(t))return;const n=e.get(t);clearTimeout(n.timeoutId),e.delete(t),document.activeElement&&document.activeElement.blur(),t.setAttribute("aria-hidden","true"),t.addEventListener("transitionend",(()=>t.remove()),{once:!0})}const c=document.getElementById("toaster");c&&i(c),window.addEventListener("basecoat:toast",(e=>{if(!t)return void console.error("Cannot create toast: toaster container not found on page.");const n=function(t){const{category:e="info",title:n,description:i,action:a,cancel:r,duration:s,icon:d}=t,c=d||e&&o[e]||"",l=n?`<h2>${n}</h2>`:"",u=i?`<p>${i}</p>`:"",h=a?.href?`<a href="${a.href}" class="btn" data-toast-action>${a.label}</a>`:a?.onclick?`<button type="button" class="btn" data-toast-action onclick="${a.onclick}">${a.label}</button>`:"",m=r?`<button type="button" class="btn-outline h-6 text-xs px-2.5 rounded-sm" data-toast-cancel onclick="${r?.onclick}">${r.label}</button>`:"",g=`\n <div\n class="toast"\n role="${"error"===e?"alert":"status"}"\n aria-atomic="true"\n ${e?`data-category="${e}"`:""}\n ${void 0!==s?`data-duration="${s}"`:""}\n >\n <div class="toast-content">\n ${c}\n <section>\n ${l}\n ${u}\n </section>\n ${h||m?`<footer>${h}${m}</footer>`:""}\n </div>\n </div>\n </div>\n `,v=document.createElement("template");return v.innerHTML=g.trim(),v.content.firstChild}(e.detail?.config||{});t.appendChild(n)}));new MutationObserver((e=>{e.forEach((e=>{e.addedNodes.forEach((e=>{e.nodeType===Node.ELEMENT_NODE&&(e.matches("#toaster")&&i(e),t&&e.matches(".toast:not([data-toast-initialized])")&&a(e))}))}))})).observe(document.body,{childList:!0,subtree:!0})})();
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "basecoat-css",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Tailwind CSS for Basecoat components",
5
5
  "author": {
6
6
  "name": "hunvreus",
7
7
  "url": "https://x.com/hunvreus"
8
8
  },
9
9
  "license": "MIT",
10
+ "type": "module",
10
11
  "main": "dist/basecoat.css",
11
12
  "style": "dist/basecoat.css",
12
13
  "files": [
@@ -36,5 +37,24 @@
36
37
  "bugs": {
37
38
  "url": "https://github.com/hunvreus/basecoat/issues"
38
39
  },
39
- "homepage": "https://basecoatui.com/installation#install-css"
40
+ "homepage": "https://basecoatui.com/installation#install-npm",
41
+ "exports": {
42
+ ".": "./dist/basecoat.css",
43
+ "./css": "./dist/basecoat.css",
44
+ "./all": "./dist/js/all.js",
45
+ "./all.min": "./dist/js/all.min.js",
46
+ "./dropdown-menu": "./dist/js/dropdown-menu.js",
47
+ "./dropdown-menu.min": "./dist/js/dropdown-menu.min.js",
48
+ "./popover": "./dist/js/popover.js",
49
+ "./popover.min": "./dist/js/popover.min.js",
50
+ "./select": "./dist/js/select.js",
51
+ "./select.min": "./dist/js/select.min.js",
52
+ "./sidebar": "./dist/js/sidebar.js",
53
+ "./sidebar.min": "./dist/js/sidebar.min.js",
54
+ "./tabs": "./dist/js/tabs.js",
55
+ "./tabs.min": "./dist/js/tabs.min.js",
56
+ "./toast": "./dist/js/toast.js",
57
+ "./toast.min": "./dist/js/toast.min.js",
58
+ "./package.json": "./package.json"
59
+ }
40
60
  }