Inital code commit
Some checks failed
Build, Test, and Publish (to Private NPM Registry) UI Components Library / publish (push) Failing after 52m26s
Some checks failed
Build, Test, and Publish (to Private NPM Registry) UI Components Library / publish (push) Failing after 52m26s
This commit is contained in:
commit
5024375e20
32 changed files with 5379 additions and 0 deletions
180
src/components/drawer/drawer.js
Normal file
180
src/components/drawer/drawer.js
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/**
|
||||
* Drawer Web Component
|
||||
*
|
||||
* A simple drawer component that toggles the visibility of its content when the header is clicked or activated with the keyboard.
|
||||
*/
|
||||
class Drawer extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Event handler bindings for click and keydown events on the drawer handle (button)
|
||||
this._handleClick = this._clicked.bind(this);
|
||||
this._handleKeyDown = this._keyDown.bind(this)
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// Private Event Handlers
|
||||
// ----------------------
|
||||
|
||||
/** Click handler for the drawer "handle" (button) */
|
||||
_clicked(event) {
|
||||
event.preventDefault();
|
||||
|
||||
event.currentTarget.setAttribute('aria-expanded', event.currentTarget.getAttribute('aria-expanded') === 'true' ? 'false' : 'true');
|
||||
this.contentDiv.classList.toggle('hidden');
|
||||
}
|
||||
|
||||
/** Keydown handler for the drawer "handle" (button) */
|
||||
_keyDown(event) {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
|
||||
event.currentTarget.setAttribute('aria-expanded', event.currentTarget.getAttribute('aria-expanded') === 'true' ? 'false' : 'true');
|
||||
this.contentDiv.classList.toggle('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Web Component Lifecycle Methods
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* Does initial setup and adds event listeners for interactivity
|
||||
*
|
||||
* `connectedCallback` is a lifecycle method in web components that runs when the custom element is inserted into the document's Document Object Model (DOM).
|
||||
* It can be invoked multiple times if the element is removed and then re-inserted into the DOM.
|
||||
*
|
||||
* Timing: It is called after the element's constructor() but before the element's children are necessarily connected or fully rendered.
|
||||
* Purpose: It is the ideal place to set up tasks that should only occur when the element is actually present in the live document. Common uses include:
|
||||
*/
|
||||
connectedCallback() {
|
||||
const internals = this.attachInternals();
|
||||
|
||||
this.shadow = this.shadowRoot;
|
||||
if (!this.shadow) {
|
||||
this.shadow = this.attachShadow({ mode: 'open' });
|
||||
|
||||
// Defer execution until the browser finishes parsing the children
|
||||
setTimeout(() => {
|
||||
// Recreate the template using the shadow DOM that is only available through JavaScript
|
||||
this.createTemplateInJS(this.shadow);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
// Native Popover handles the click, enter, space, and dismiss logic.
|
||||
// You only need event listeners here if you want to trigger
|
||||
// custom analytics or highly specific behavior on open/close.
|
||||
const drawerHandle = this.shadow.querySelector('[aria-controls]');
|
||||
if (drawerHandle) {
|
||||
// Get the content div that the drawer handle controls so we can toggle it in the click and keydown handlers
|
||||
this.contentDiv = this.shadow.querySelector(`#${drawerHandle.getAttribute('aria-controls')}`);
|
||||
|
||||
// Add event listeners to the drawer handle (button)
|
||||
drawerHandle.addEventListener('click', this._handleClick);
|
||||
drawerHandle.addEventListener('keydown', this._handleKeyDown);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up event listeners when the component is removed from the DOM
|
||||
*
|
||||
* `disconnectedCallback` is a lifecycle method in web components that runs when the custom element is removed from the document's DOM.
|
||||
* It can be invoked multiple times if the element is removed and then re-inserted into the DOM.
|
||||
*
|
||||
* Timing: It is called after the element is removed from the DOM but before it is garbage collected.
|
||||
* Purpose: It is the ideal place to clean up any resources or event listeners that were set up in `connectedCallback`.
|
||||
*
|
||||
* Common uses include:
|
||||
* - Removing event listeners to prevent memory leaks
|
||||
* - Clearing timers or intervals
|
||||
* - Disconnecting from external data sources or APIs
|
||||
*/
|
||||
disconnectedCallback() {
|
||||
const drawerHandle = this.shadow.querySelector('[aria-controls]');
|
||||
if (drawerHandle) {
|
||||
drawerHandle.removeEventListener('click', this._handleClick);
|
||||
drawerHandle.removeEventListener('keydown', this._handleKeyDown);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreate the template in the shadow DOM through JavaScript instead of relying on the `shadowrootmode` attribute
|
||||
*
|
||||
* @param {ShadowRoot} shadow The shadow DOM to attach the template to
|
||||
*/
|
||||
createTemplateInJS(shadow) {
|
||||
// Retrieve the scoped data island
|
||||
const configScript = this.querySelector('script[data-drawer-config]');
|
||||
if (!configScript) {
|
||||
console.error('Drawer configuration missing. Cannot build JS fallback.');
|
||||
return;
|
||||
}
|
||||
|
||||
const config = JSON.parse(configScript.textContent);
|
||||
const { drawerExtraStyles, drawerExtraScripts, drawerId, componentsStyleHref } = config;
|
||||
|
||||
// Create and append the link tag for the components library CSS (including component specific styles)
|
||||
const drawerStyle = document.createElement('link');
|
||||
drawerStyle.rel = 'stylesheet';
|
||||
drawerStyle.type = 'text/css';
|
||||
drawerStyle.href = componentsStyleHref;
|
||||
shadow.appendChild(drawerStyle);
|
||||
|
||||
// Create additional link tags for any extra CSS files to include
|
||||
drawerExtraStyles.forEach(style => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = `/css/${style}.css`;
|
||||
shadow.appendChild(link);
|
||||
});
|
||||
|
||||
// Create and append script tags for any extra JavaScript files to include
|
||||
drawerExtraScripts.forEach(script => {
|
||||
const scriptElem = document.createElement('script');
|
||||
scriptElem.type = typeof script === 'object' && typeof script.module === 'boolean' && script.module ? 'module' : typeof script === 'string' && script.endsWith('.mjs') ? 'module' : 'application/javascript';
|
||||
scriptElem.src = typeof script === 'object' ? script.script.startsWith('http') ? script.script : `/js/${script.script.endsWith('.mjs') ? script.script : script.script + '.js'}` : script.startsWith('http') ? script : `/js/${script.endsWith('.mjs') ? script : script + '.js'}`;
|
||||
shadow.appendChild(scriptElem);
|
||||
});
|
||||
|
||||
// Create the container div for the drawer component
|
||||
const drawerContainerDiv = document.createElement('div');
|
||||
drawerContainerDiv.id = drawerId;
|
||||
drawerContainerDiv.classList.add('drawer');
|
||||
|
||||
// Create the button element for the tooltip trigger
|
||||
const drawerBtnElem = document.createElement('div');
|
||||
drawerBtnElem.id = `${drawerId}-drawer-header`;
|
||||
drawerBtnElem.role = 'button';
|
||||
drawerBtnElem.classList.add('drawer-header');
|
||||
drawerBtnElem.tabIndex = 0;
|
||||
drawerBtnElem.setAttribute('aria-expanded', 'false');
|
||||
drawerBtnElem.setAttribute('aria-controls', `${drawerId}-drawer-contents`);
|
||||
|
||||
const drawerHeaderSlotElem = document.createElement('slot');
|
||||
drawerHeaderSlotElem.name = 'drawer-header';
|
||||
|
||||
drawerBtnElem.appendChild(drawerHeaderSlotElem);
|
||||
|
||||
drawerContainerDiv.appendChild(drawerBtnElem);
|
||||
|
||||
// Create the div that will contain the drawer content
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.id = `${drawerId}-drawer-contents`;
|
||||
contentDiv.classList.add('drawer-contents', 'hidden');
|
||||
|
||||
const contentSlotElem = document.createElement('slot');
|
||||
contentSlotElem.name = 'drawer-contents';
|
||||
|
||||
contentDiv.appendChild(contentSlotElem);
|
||||
|
||||
drawerContainerDiv.appendChild(contentDiv);
|
||||
|
||||
// Finally, append the entire drawer container div to the shadow DOM of the component
|
||||
shadow.appendChild(drawerContainerDiv);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('ba-drawer', Drawer);
|
||||
Loading…
Add table
Add a link
Reference in a new issue