mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-12 13:18:22 +00:00
Improving registration and initialization of components, updating docs
This commit is contained in:
@@ -491,11 +491,13 @@
|
||||
> footer {
|
||||
@apply flex flex-col-reverse gap-2 sm:flex-row sm:justify-end;
|
||||
}
|
||||
> button,
|
||||
> form[method='dialog'] {
|
||||
@apply absolute top-4 right-4;
|
||||
> button {
|
||||
@apply ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
}
|
||||
> button,
|
||||
> form[method='dialog'] > button {
|
||||
@apply ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
(() => {
|
||||
const componentRegistry = {};
|
||||
let observer = null;
|
||||
|
||||
const registerComponent = (name, selector, initFunction) => {
|
||||
componentRegistry[name] = {
|
||||
selector,
|
||||
init: initFunction
|
||||
};
|
||||
};
|
||||
|
||||
const initComponent = (element, componentName) => {
|
||||
const component = componentRegistry[componentName];
|
||||
if (!component) return;
|
||||
|
||||
try {
|
||||
component.init(element);
|
||||
} catch (error) {
|
||||
console.error(`Failed to initialize ${componentName}:`, error);
|
||||
}
|
||||
};
|
||||
|
||||
const initAllComponents = () => {
|
||||
Object.entries(componentRegistry).forEach(([name, { selector, init }]) => {
|
||||
document.querySelectorAll(selector).forEach(init);
|
||||
});
|
||||
};
|
||||
|
||||
const initNewComponents = (node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
||||
|
||||
Object.entries(componentRegistry).forEach(([name, { selector, init }]) => {
|
||||
if (node.matches(selector)) {
|
||||
init(node);
|
||||
}
|
||||
node.querySelectorAll(selector).forEach(init);
|
||||
});
|
||||
};
|
||||
|
||||
const startObserver = () => {
|
||||
if (observer) return;
|
||||
|
||||
observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach(initNewComponents);
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
};
|
||||
|
||||
const stopObserver = () => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
observer = null;
|
||||
}
|
||||
};
|
||||
|
||||
const reinitComponent = (componentName) => {
|
||||
const component = componentRegistry[componentName];
|
||||
if (!component) {
|
||||
console.warn(`Component '${componentName}' not found in registry`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear initialization flag for this component
|
||||
const flag = `data-${componentName}-initialized`;
|
||||
document.querySelectorAll(`[${flag}]`).forEach(el => {
|
||||
el.removeAttribute(flag);
|
||||
});
|
||||
|
||||
document.querySelectorAll(component.selector).forEach(component.init);
|
||||
};
|
||||
|
||||
const reinitAll = () => {
|
||||
// Clear all initialization flags using the registry
|
||||
Object.entries(componentRegistry).forEach(([name, { selector }]) => {
|
||||
const flag = `data-${name}-initialized`;
|
||||
document.querySelectorAll(`[${flag}]`).forEach(el => {
|
||||
el.removeAttribute(flag);
|
||||
});
|
||||
});
|
||||
|
||||
initAllComponents();
|
||||
};
|
||||
|
||||
window.basecoat = {
|
||||
register: registerComponent,
|
||||
init: reinitComponent,
|
||||
initAll: reinitAll,
|
||||
start: startObserver,
|
||||
stop: stopObserver
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initAllComponents();
|
||||
startObserver();
|
||||
});
|
||||
})();
|
||||
+3
-15
@@ -165,19 +165,7 @@
|
||||
dropdownMenuComponent.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
||||
};
|
||||
|
||||
document.querySelectorAll('.dropdown-menu:not([data-dropdown-menu-initialized])').forEach(initDropdownMenu);
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
||||
if (node.matches('.dropdown-menu:not([data-dropdown-menu-initialized])')) {
|
||||
initDropdownMenu(node);
|
||||
}
|
||||
node.querySelectorAll('.dropdown-menu:not([data-dropdown-menu-initialized])').forEach(initDropdownMenu);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('dropdown-menu', '.dropdown-menu:not([data-dropdown-menu-initialized])', initDropdownMenu);
|
||||
}
|
||||
})();
|
||||
+3
-15
@@ -67,19 +67,7 @@
|
||||
popoverComponent.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
||||
};
|
||||
|
||||
document.querySelectorAll('.popover:not([data-popover-initialized])').forEach(initPopover);
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
||||
if (node.matches('.popover:not([data-popover-initialized])')) {
|
||||
initPopover(node);
|
||||
}
|
||||
node.querySelectorAll('.popover:not([data-popover-initialized])').forEach(initPopover);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('popover', '.popover:not([data-popover-initialized])', initPopover);
|
||||
}
|
||||
})();
|
||||
|
||||
+3
-16
@@ -272,20 +272,7 @@
|
||||
selectComponent.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
||||
};
|
||||
|
||||
document.querySelectorAll('div.select:not([data-select-initialized])').forEach(initSelect);
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
if (node.matches('div.select:not([data-select-initialized])')) {
|
||||
initSelect(node);
|
||||
}
|
||||
node.querySelectorAll('div.select:not([data-select-initialized])').forEach(initSelect);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('select', 'div.select:not([data-select-initialized])', initSelect);
|
||||
}
|
||||
})();
|
||||
+3
-16
@@ -98,20 +98,7 @@
|
||||
sidebarComponent.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
||||
};
|
||||
|
||||
document.querySelectorAll('.sidebar:not([data-sidebar-initialized])').forEach(initSidebar);
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
if (node.matches('.sidebar:not([data-sidebar-initialized])')) {
|
||||
initSidebar(node);
|
||||
}
|
||||
node.querySelectorAll('.sidebar:not([data-sidebar-initialized])').forEach(initSidebar);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('sidebar', '.sidebar:not([data-sidebar-initialized])', initSidebar);
|
||||
}
|
||||
})();
|
||||
+3
-16
@@ -57,20 +57,7 @@
|
||||
tabsComponent.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
||||
};
|
||||
|
||||
document.querySelectorAll('.tabs:not([data-tabs-initialized])').forEach(initTabs);
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
if (node.matches('.tabs:not([data-tabs-initialized])')) {
|
||||
initTabs(node);
|
||||
}
|
||||
node.querySelectorAll('.tabs:not([data-tabs-initialized])').forEach(initTabs);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('tabs', '.tabs:not([data-tabs-initialized])', initTabs);
|
||||
}
|
||||
})();
|
||||
+4
-20
@@ -164,9 +164,6 @@
|
||||
return template.content.firstChild;
|
||||
}
|
||||
|
||||
const initialToaster = document.getElementById('toaster');
|
||||
if (initialToaster) initToaster(initialToaster);
|
||||
|
||||
document.addEventListener('basecoat:toast', (event) => {
|
||||
if (!toaster) {
|
||||
console.error('Cannot create toast: toaster container not found on page.');
|
||||
@@ -177,21 +174,8 @@
|
||||
toaster.appendChild(toastElement);
|
||||
});
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
||||
|
||||
if (node.matches('#toaster')) {
|
||||
initToaster(node);
|
||||
}
|
||||
|
||||
if (toaster && node.matches('.toast:not([data-toast-initialized])')) {
|
||||
initToast(node);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('toaster', '#toaster:not([data-toaster-initialized])', initToaster);
|
||||
window.basecoat.register('toast', '.toast:not([data-toast-initialized])', initToast);
|
||||
}
|
||||
})();
|
||||
@@ -83,11 +83,9 @@
|
||||
</footer>
|
||||
{% endif %}
|
||||
{% if close_button %}
|
||||
<form method="dialog">
|
||||
<button aria-label="Close dialog">
|
||||
<svg 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" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
<button type="button" aria-label="Close dialog" onclick="this.closest('dialog').close()">
|
||||
<svg 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" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</article>
|
||||
</dialog>
|
||||
|
||||
Reference in New Issue
Block a user