@countermeasure-platform/web-components 1.2.2-dev.14.1 → 1.2.2-dev.15.1
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.
- package/dist/{component-CBXhVCNE.js → component-COshXojx.js} +2 -2
- package/dist/{component-CBXhVCNE.js.map → component-COshXojx.js.map} +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/tree-view/index.d.ts.map +1 -1
- package/dist/components/tree-view/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/react/sidebar.js +1 -1
- package/dist/sidebar/index.js +1 -1
- package/dist/styles/sidebar.css +18 -6
- package/dist/{tree-view-SpbHG9Yi.js → tree-view-gML5wGlz.js} +3 -3
- package/dist/{tree-view-SpbHG9Yi.js.map → tree-view-gML5wGlz.js.map} +1 -1
- package/package.json +1 -1
- package/src/components/tree-view/index.ts +0 -2
- package/src/sidebar/component.ts +2 -2
- package/src/styles/sidebar.css +18 -6
|
@@ -7,7 +7,7 @@ function r(e) {
|
|
|
7
7
|
}
|
|
8
8
|
function i(t, n) {
|
|
9
9
|
let r = "http://www.w3.org/2000/svg", i = document.createElementNS(r, "svg");
|
|
10
|
-
i.setAttribute("xmlns", r), i.setAttribute("width", "
|
|
10
|
+
i.setAttribute("xmlns", r), i.setAttribute("width", "16"), i.setAttribute("height", "16"), i.setAttribute("viewBox", "0 0 24 24"), i.setAttribute("fill", "none"), i.setAttribute("stroke", "currentColor"), i.setAttribute("stroke-width", "2"), i.setAttribute("stroke-linecap", "round"), i.setAttribute("stroke-linejoin", "round"), i.classList.add("sidebar__item-icon"), i.setAttribute("data-icon", n), i.setAttribute("aria-hidden", "true");
|
|
11
11
|
let a = e(t, n);
|
|
12
12
|
return a && (i.innerHTML = a), i;
|
|
13
13
|
}
|
|
@@ -160,4 +160,4 @@ var a = class {
|
|
|
160
160
|
//#endregion
|
|
161
161
|
export { a as t };
|
|
162
162
|
|
|
163
|
-
//# sourceMappingURL=component-
|
|
163
|
+
//# sourceMappingURL=component-COshXojx.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-CBXhVCNE.js","names":[],"sources":["../src/sidebar/component.ts"],"sourcesContent":["/**\n * SidebarComponent - Programmatic sidebar with sections, badges, and footer\n * @countermeasure/web-components/sidebar/component\n *\n * Unlike sidebar/enhance (which enhances server-rendered HTML), this module\n * builds the sidebar DOM from a config object. Use it when the sidebar\n * structure is driven entirely by client-side data.\n *\n * Usage:\n * import { SidebarComponent } from '@countermeasure/web-components/sidebar/component'\n *\n * const sidebar = new SidebarComponent({\n * container: '#sidebar-root',\n * sections: [{ id: 'main', label: 'Nav', items: [...] }],\n * user: { name: 'Alice' },\n * onNavigate: item => console.log(item.id),\n * })\n * sidebar.setActive('dashboard')\n */\n\nimport { createBrandLockupElement } from '../components/brand'\nimport { getIconSvgInner, type IconSet } from '../icons/index'\nimport { resolveContainer } from '../primitives/utils'\nimport { type SidebarComponentConfig, type SidebarSection } from './types'\n\nfunction toSidebarVariantClassName(variant: string): string {\n const normalized = variant.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')\n return `sidebar--${normalized || 'app'}`\n}\n\n/** Create an SVG element populated with icon content from the registry. */\nfunction createIconSvg(iconSet: IconSet, name: string): SVGElement {\n const svgNs = 'http://www.w3.org/2000/svg'\n const svg = document.createElementNS(svgNs, 'svg')\n svg.setAttribute('xmlns', svgNs)\n svg.setAttribute('width', '18')\n svg.setAttribute('height', '18')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n svg.classList.add('sidebar__item-icon')\n svg.setAttribute('data-icon', name)\n svg.setAttribute('aria-hidden', 'true')\n\n const inner = getIconSvgInner(iconSet, name)\n if (inner) {\n svg.innerHTML = inner\n }\n\n return svg\n}\n\nexport class SidebarComponent {\n private readonly container: HTMLElement\n private readonly aside: HTMLElement\n private readonly config: SidebarComponentConfig\n private readonly iconSet: IconSet\n private collapseButton: HTMLButtonElement | null = null\n\n constructor(config: SidebarComponentConfig) {\n this.config = config\n this.container = resolveContainer(config.container)\n this.iconSet = config.iconSet ?? 'lucide'\n\n this.aside = document.createElement('aside')\n const variant = config.variant ?? 'app'\n this.aside.classList.add('sidebar', toSidebarVariantClassName(variant))\n this.aside.setAttribute('data-sidebar', variant)\n if (config.collapsed === true) {\n this.aside.classList.add('sidebar--collapsed')\n }\n\n this.render()\n this.container.appendChild(this.aside)\n }\n\n private render(): void {\n if (this.config.collapsible === true) {\n this.renderCollapseButton()\n }\n\n this.renderHeader()\n\n const body = document.createElement('div')\n body.classList.add('sidebar__body')\n\n for (const section of this.config.sections) {\n body.appendChild(this.buildSection(section))\n }\n\n this.aside.appendChild(body)\n this.renderFooter()\n }\n\n private buildSection(section: SidebarSection): HTMLElement {\n const sectionEl = document.createElement('div')\n sectionEl.classList.add('sidebar__section')\n sectionEl.setAttribute('data-section-id', section.id)\n\n const collapsible = section.collapsible === true\n const expanded = section.defaultExpanded !== false\n\n if (collapsible && section.label) {\n sectionEl.classList.add('sidebar__section--collapsible')\n if (expanded) sectionEl.classList.add('sidebar__section--expanded')\n\n const header = document.createElement('button')\n header.type = 'button'\n header.classList.add('sidebar__group-header')\n header.setAttribute('aria-expanded', String(expanded))\n header.setAttribute('aria-controls', `sidebar-group-${section.id}`)\n\n if (section.icon) {\n header.appendChild(createIconSvg(this.iconSet, section.icon))\n }\n\n const labelSpan = document.createElement('span')\n labelSpan.classList.add('sidebar__group-label')\n labelSpan.textContent = section.label\n header.appendChild(labelSpan)\n\n const chevron = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n chevron.setAttribute('class', 'sidebar__group-chevron')\n chevron.setAttribute('viewBox', '0 0 24 24')\n chevron.setAttribute('width', '14')\n chevron.setAttribute('height', '14')\n chevron.setAttribute('fill', 'none')\n chevron.setAttribute('stroke', 'currentColor')\n chevron.setAttribute('stroke-width', '2')\n chevron.setAttribute('stroke-linecap', 'round')\n chevron.setAttribute('stroke-linejoin', 'round')\n chevron.setAttribute('aria-hidden', 'true')\n const chevronPath = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n chevronPath.setAttribute('d', 'm6 9 6 6 6-6')\n chevron.appendChild(chevronPath)\n header.appendChild(chevron)\n\n header.addEventListener('click', () => {\n const nowExpanded = sectionEl.classList.toggle('sidebar__section--expanded')\n header.setAttribute('aria-expanded', String(nowExpanded))\n })\n\n sectionEl.appendChild(header)\n } else if (section.label) {\n const labelEl = document.createElement('div')\n labelEl.classList.add('sidebar__section-label')\n labelEl.textContent = section.label\n sectionEl.appendChild(labelEl)\n }\n\n const itemsWrap = document.createElement('div')\n itemsWrap.classList.add('sidebar__items')\n itemsWrap.id = `sidebar-group-${section.id}`\n\n for (const item of section.items) {\n const link = document.createElement('a')\n link.classList.add('sidebar__item')\n link.setAttribute('data-item-id', item.id)\n link.setAttribute('title', item.label)\n\n if (item.icon) {\n const icon = createIconSvg(this.iconSet, item.icon)\n link.appendChild(icon)\n }\n\n const label = document.createElement('span')\n label.classList.add('sidebar__item-label')\n label.textContent = item.label\n link.appendChild(label)\n\n if (item.badge !== undefined && item.badge > 0) {\n const badge = document.createElement('span')\n const tone = item.badgeTone ?? 'primary'\n link.setAttribute('data-badge-visible', 'true')\n link.setAttribute('data-badge-tone', tone)\n badge.classList.add('sidebar__badge', `sidebar__badge--${tone}`)\n badge.setAttribute('data-badge', item.id)\n badge.textContent = String(item.badge)\n link.appendChild(badge)\n }\n\n link.addEventListener('click', () => {\n if (this.config.onNavigate) {\n this.config.onNavigate(item)\n }\n if (item.onClick) {\n item.onClick()\n }\n })\n\n itemsWrap.appendChild(link)\n }\n\n sectionEl.appendChild(itemsWrap)\n return sectionEl\n }\n\n private renderHeader(): void {\n const brand = this.config.brand\n if (brand === undefined) {\n return\n }\n\n const header = document.createElement('div')\n header.classList.add('sidebar__header')\n\n const row = document.createElement('div')\n row.classList.add('sidebar__header-row')\n\n const brandEl =\n brand.href !== undefined\n ? document.createElement('a')\n : brand.onClick !== undefined\n ? document.createElement('button')\n : document.createElement('span')\n\n brandEl.classList.add('sidebar__brand')\n\n if (brandEl instanceof HTMLAnchorElement) {\n brandEl.href = brand.href ?? '#'\n brandEl.setAttribute('aria-label', brand.label ?? 'CounterMeasure')\n }\n\n if (brandEl instanceof HTMLButtonElement) {\n brandEl.type = 'button'\n brandEl.setAttribute('aria-label', brand.label ?? 'CounterMeasure')\n }\n\n if (brand.onClick !== undefined) {\n brandEl.addEventListener('click', brand.onClick)\n }\n\n brandEl.appendChild(\n createBrandLockupElement({\n label: brand.label ?? 'CounterMeasure',\n markSize: brand.markSize ?? 22,\n showWordmark: brand.showWordmark ?? true,\n decorative: brand.decorative ?? false,\n })\n )\n\n row.appendChild(brandEl)\n header.appendChild(row)\n this.aside.appendChild(header)\n }\n\n private renderCollapseButton(): void {\n const button = document.createElement('button')\n button.type = 'button'\n button.classList.add('sidebar__collapse-btn', 'sidebar__collapse-btn--edge')\n button.setAttribute('data-sidebar-toggle', '')\n\n if (this.config.collapseButtonVisible === true) {\n button.setAttribute('data-visible', 'true')\n }\n\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('aria-hidden', 'true')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n svg.appendChild(path)\n button.appendChild(svg)\n\n button.addEventListener('click', () => {\n this.toggleCollapse()\n })\n\n this.collapseButton = button\n this.updateCollapseButton()\n this.aside.appendChild(button)\n }\n\n private renderFooter(): void {\n const hasUser = this.config.user !== undefined\n const hasActions =\n this.config.footerActions !== undefined && this.config.footerActions.length > 0\n\n if (!hasUser && !hasActions) {\n return\n }\n\n const footer = document.createElement('div')\n footer.classList.add('sidebar__footer')\n\n const footerBar = document.createElement('div')\n footerBar.classList.add('sidebar__footer-bar')\n\n if (hasUser && this.config.user) {\n const userSpan = document.createElement('span')\n userSpan.classList.add('sidebar__footer-user')\n\n const nameSpan = document.createElement('span')\n nameSpan.classList.add('sidebar__footer-name')\n nameSpan.textContent = this.config.user.name\n userSpan.appendChild(nameSpan)\n\n footerBar.appendChild(userSpan)\n }\n\n if (hasActions && this.config.footerActions) {\n const iconsSpan = document.createElement('span')\n iconsSpan.classList.add('sidebar__footer-icons')\n\n for (const action of this.config.footerActions) {\n const button = document.createElement('button')\n button.classList.add('sidebar__footer-icon')\n if (action.danger === true) {\n button.classList.add('sidebar__footer-icon--danger')\n }\n button.setAttribute('aria-label', action.label)\n\n const icon = createIconSvg(this.iconSet, action.icon)\n button.appendChild(icon)\n\n if (action.onClick) {\n const handler = action.onClick\n button.addEventListener('click', () => {\n handler()\n })\n }\n\n iconsSpan.appendChild(button)\n }\n\n footerBar.appendChild(iconsSpan)\n }\n\n footer.appendChild(footerBar)\n this.aside.appendChild(footer)\n }\n\n /** Mark a nav item as active, removing active state from all others. */\n setActive(itemId: string): void {\n this.aside.querySelectorAll<HTMLElement>('.sidebar__item').forEach(el => {\n el.classList.remove('sidebar__item--active')\n })\n\n const target = this.aside.querySelector<HTMLElement>(`[data-item-id=\"${itemId}\"]`)\n if (target) {\n target.classList.add('sidebar__item--active')\n }\n }\n\n /** Update the badge count for a nav item (empty string when count is 0). */\n updateBadge(itemId: string, count: number): void {\n const badge = this.aside.querySelector<HTMLElement>(`[data-badge=\"${itemId}\"]`)\n if (badge) {\n badge.textContent = count > 0 ? String(count) : ''\n const item = badge.closest<HTMLElement>('.sidebar__item')\n item?.setAttribute('data-badge-visible', String(count > 0))\n }\n }\n\n /** Toggle the sidebar between collapsed and expanded states. */\n toggleCollapse(): void {\n const isCollapsed = this.aside.classList.toggle('sidebar--collapsed')\n this.updateCollapseButton()\n if (this.config.onCollapse) {\n this.config.onCollapse(isCollapsed)\n }\n }\n\n /** Collapse the sidebar. */\n collapse(): void {\n const wasCollapsed = this.aside.classList.contains('sidebar--collapsed')\n this.aside.classList.add('sidebar--collapsed')\n this.updateCollapseButton()\n if (!wasCollapsed && this.config.onCollapse) {\n this.config.onCollapse(true)\n }\n }\n\n /** Expand the sidebar. */\n expand(): void {\n const wasCollapsed = this.aside.classList.contains('sidebar--collapsed')\n this.aside.classList.remove('sidebar--collapsed')\n this.updateCollapseButton()\n if (wasCollapsed && this.config.onCollapse) {\n this.config.onCollapse(false)\n }\n }\n\n /** Remove the sidebar from the DOM. */\n destroy(): void {\n this.aside.remove()\n }\n\n private updateCollapseButton(): void {\n if (this.collapseButton === null) {\n return\n }\n\n const isCollapsed = this.aside.classList.contains('sidebar--collapsed')\n const label = isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'\n this.collapseButton.setAttribute('aria-label', label)\n this.collapseButton.setAttribute('aria-expanded', String(!isCollapsed))\n this.collapseButton.setAttribute('title', label)\n this.collapseButton.setAttribute('data-collapsed', String(isCollapsed))\n this.collapseButton\n .querySelector('path')\n ?.setAttribute('d', isCollapsed ? 'm9 18 6-6-6-6' : 'm15 18-6-6 6-6')\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"component-COshXojx.js","names":[],"sources":["../src/sidebar/component.ts"],"sourcesContent":["/**\n * SidebarComponent - Programmatic sidebar with sections, badges, and footer\n * @countermeasure/web-components/sidebar/component\n *\n * Unlike sidebar/enhance (which enhances server-rendered HTML), this module\n * builds the sidebar DOM from a config object. Use it when the sidebar\n * structure is driven entirely by client-side data.\n *\n * Usage:\n * import { SidebarComponent } from '@countermeasure/web-components/sidebar/component'\n *\n * const sidebar = new SidebarComponent({\n * container: '#sidebar-root',\n * sections: [{ id: 'main', label: 'Nav', items: [...] }],\n * user: { name: 'Alice' },\n * onNavigate: item => console.log(item.id),\n * })\n * sidebar.setActive('dashboard')\n */\n\nimport { createBrandLockupElement } from '../components/brand'\nimport { getIconSvgInner, type IconSet } from '../icons/index'\nimport { resolveContainer } from '../primitives/utils'\nimport { type SidebarComponentConfig, type SidebarSection } from './types'\n\nfunction toSidebarVariantClassName(variant: string): string {\n const normalized = variant.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')\n return `sidebar--${normalized || 'app'}`\n}\n\n/** Create an SVG element populated with icon content from the registry. */\nfunction createIconSvg(iconSet: IconSet, name: string): SVGElement {\n const svgNs = 'http://www.w3.org/2000/svg'\n const svg = document.createElementNS(svgNs, 'svg')\n svg.setAttribute('xmlns', svgNs)\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n svg.classList.add('sidebar__item-icon')\n svg.setAttribute('data-icon', name)\n svg.setAttribute('aria-hidden', 'true')\n\n const inner = getIconSvgInner(iconSet, name)\n if (inner) {\n svg.innerHTML = inner\n }\n\n return svg\n}\n\nexport class SidebarComponent {\n private readonly container: HTMLElement\n private readonly aside: HTMLElement\n private readonly config: SidebarComponentConfig\n private readonly iconSet: IconSet\n private collapseButton: HTMLButtonElement | null = null\n\n constructor(config: SidebarComponentConfig) {\n this.config = config\n this.container = resolveContainer(config.container)\n this.iconSet = config.iconSet ?? 'lucide'\n\n this.aside = document.createElement('aside')\n const variant = config.variant ?? 'app'\n this.aside.classList.add('sidebar', toSidebarVariantClassName(variant))\n this.aside.setAttribute('data-sidebar', variant)\n if (config.collapsed === true) {\n this.aside.classList.add('sidebar--collapsed')\n }\n\n this.render()\n this.container.appendChild(this.aside)\n }\n\n private render(): void {\n if (this.config.collapsible === true) {\n this.renderCollapseButton()\n }\n\n this.renderHeader()\n\n const body = document.createElement('div')\n body.classList.add('sidebar__body')\n\n for (const section of this.config.sections) {\n body.appendChild(this.buildSection(section))\n }\n\n this.aside.appendChild(body)\n this.renderFooter()\n }\n\n private buildSection(section: SidebarSection): HTMLElement {\n const sectionEl = document.createElement('div')\n sectionEl.classList.add('sidebar__section')\n sectionEl.setAttribute('data-section-id', section.id)\n\n const collapsible = section.collapsible === true\n const expanded = section.defaultExpanded !== false\n\n if (collapsible && section.label) {\n sectionEl.classList.add('sidebar__section--collapsible')\n if (expanded) sectionEl.classList.add('sidebar__section--expanded')\n\n const header = document.createElement('button')\n header.type = 'button'\n header.classList.add('sidebar__group-header')\n header.setAttribute('aria-expanded', String(expanded))\n header.setAttribute('aria-controls', `sidebar-group-${section.id}`)\n\n if (section.icon) {\n header.appendChild(createIconSvg(this.iconSet, section.icon))\n }\n\n const labelSpan = document.createElement('span')\n labelSpan.classList.add('sidebar__group-label')\n labelSpan.textContent = section.label\n header.appendChild(labelSpan)\n\n const chevron = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n chevron.setAttribute('class', 'sidebar__group-chevron')\n chevron.setAttribute('viewBox', '0 0 24 24')\n chevron.setAttribute('width', '14')\n chevron.setAttribute('height', '14')\n chevron.setAttribute('fill', 'none')\n chevron.setAttribute('stroke', 'currentColor')\n chevron.setAttribute('stroke-width', '2')\n chevron.setAttribute('stroke-linecap', 'round')\n chevron.setAttribute('stroke-linejoin', 'round')\n chevron.setAttribute('aria-hidden', 'true')\n const chevronPath = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n chevronPath.setAttribute('d', 'm6 9 6 6 6-6')\n chevron.appendChild(chevronPath)\n header.appendChild(chevron)\n\n header.addEventListener('click', () => {\n const nowExpanded = sectionEl.classList.toggle('sidebar__section--expanded')\n header.setAttribute('aria-expanded', String(nowExpanded))\n })\n\n sectionEl.appendChild(header)\n } else if (section.label) {\n const labelEl = document.createElement('div')\n labelEl.classList.add('sidebar__section-label')\n labelEl.textContent = section.label\n sectionEl.appendChild(labelEl)\n }\n\n const itemsWrap = document.createElement('div')\n itemsWrap.classList.add('sidebar__items')\n itemsWrap.id = `sidebar-group-${section.id}`\n\n for (const item of section.items) {\n const link = document.createElement('a')\n link.classList.add('sidebar__item')\n link.setAttribute('data-item-id', item.id)\n link.setAttribute('title', item.label)\n\n if (item.icon) {\n const icon = createIconSvg(this.iconSet, item.icon)\n link.appendChild(icon)\n }\n\n const label = document.createElement('span')\n label.classList.add('sidebar__item-label')\n label.textContent = item.label\n link.appendChild(label)\n\n if (item.badge !== undefined && item.badge > 0) {\n const badge = document.createElement('span')\n const tone = item.badgeTone ?? 'primary'\n link.setAttribute('data-badge-visible', 'true')\n link.setAttribute('data-badge-tone', tone)\n badge.classList.add('sidebar__badge', `sidebar__badge--${tone}`)\n badge.setAttribute('data-badge', item.id)\n badge.textContent = String(item.badge)\n link.appendChild(badge)\n }\n\n link.addEventListener('click', () => {\n if (this.config.onNavigate) {\n this.config.onNavigate(item)\n }\n if (item.onClick) {\n item.onClick()\n }\n })\n\n itemsWrap.appendChild(link)\n }\n\n sectionEl.appendChild(itemsWrap)\n return sectionEl\n }\n\n private renderHeader(): void {\n const brand = this.config.brand\n if (brand === undefined) {\n return\n }\n\n const header = document.createElement('div')\n header.classList.add('sidebar__header')\n\n const row = document.createElement('div')\n row.classList.add('sidebar__header-row')\n\n const brandEl =\n brand.href !== undefined\n ? document.createElement('a')\n : brand.onClick !== undefined\n ? document.createElement('button')\n : document.createElement('span')\n\n brandEl.classList.add('sidebar__brand')\n\n if (brandEl instanceof HTMLAnchorElement) {\n brandEl.href = brand.href ?? '#'\n brandEl.setAttribute('aria-label', brand.label ?? 'CounterMeasure')\n }\n\n if (brandEl instanceof HTMLButtonElement) {\n brandEl.type = 'button'\n brandEl.setAttribute('aria-label', brand.label ?? 'CounterMeasure')\n }\n\n if (brand.onClick !== undefined) {\n brandEl.addEventListener('click', brand.onClick)\n }\n\n brandEl.appendChild(\n createBrandLockupElement({\n label: brand.label ?? 'CounterMeasure',\n markSize: brand.markSize ?? 22,\n showWordmark: brand.showWordmark ?? true,\n decorative: brand.decorative ?? false,\n })\n )\n\n row.appendChild(brandEl)\n header.appendChild(row)\n this.aside.appendChild(header)\n }\n\n private renderCollapseButton(): void {\n const button = document.createElement('button')\n button.type = 'button'\n button.classList.add('sidebar__collapse-btn', 'sidebar__collapse-btn--edge')\n button.setAttribute('data-sidebar-toggle', '')\n\n if (this.config.collapseButtonVisible === true) {\n button.setAttribute('data-visible', 'true')\n }\n\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('aria-hidden', 'true')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n svg.appendChild(path)\n button.appendChild(svg)\n\n button.addEventListener('click', () => {\n this.toggleCollapse()\n })\n\n this.collapseButton = button\n this.updateCollapseButton()\n this.aside.appendChild(button)\n }\n\n private renderFooter(): void {\n const hasUser = this.config.user !== undefined\n const hasActions =\n this.config.footerActions !== undefined && this.config.footerActions.length > 0\n\n if (!hasUser && !hasActions) {\n return\n }\n\n const footer = document.createElement('div')\n footer.classList.add('sidebar__footer')\n\n const footerBar = document.createElement('div')\n footerBar.classList.add('sidebar__footer-bar')\n\n if (hasUser && this.config.user) {\n const userSpan = document.createElement('span')\n userSpan.classList.add('sidebar__footer-user')\n\n const nameSpan = document.createElement('span')\n nameSpan.classList.add('sidebar__footer-name')\n nameSpan.textContent = this.config.user.name\n userSpan.appendChild(nameSpan)\n\n footerBar.appendChild(userSpan)\n }\n\n if (hasActions && this.config.footerActions) {\n const iconsSpan = document.createElement('span')\n iconsSpan.classList.add('sidebar__footer-icons')\n\n for (const action of this.config.footerActions) {\n const button = document.createElement('button')\n button.classList.add('sidebar__footer-icon')\n if (action.danger === true) {\n button.classList.add('sidebar__footer-icon--danger')\n }\n button.setAttribute('aria-label', action.label)\n\n const icon = createIconSvg(this.iconSet, action.icon)\n button.appendChild(icon)\n\n if (action.onClick) {\n const handler = action.onClick\n button.addEventListener('click', () => {\n handler()\n })\n }\n\n iconsSpan.appendChild(button)\n }\n\n footerBar.appendChild(iconsSpan)\n }\n\n footer.appendChild(footerBar)\n this.aside.appendChild(footer)\n }\n\n /** Mark a nav item as active, removing active state from all others. */\n setActive(itemId: string): void {\n this.aside.querySelectorAll<HTMLElement>('.sidebar__item').forEach(el => {\n el.classList.remove('sidebar__item--active')\n })\n\n const target = this.aside.querySelector<HTMLElement>(`[data-item-id=\"${itemId}\"]`)\n if (target) {\n target.classList.add('sidebar__item--active')\n }\n }\n\n /** Update the badge count for a nav item (empty string when count is 0). */\n updateBadge(itemId: string, count: number): void {\n const badge = this.aside.querySelector<HTMLElement>(`[data-badge=\"${itemId}\"]`)\n if (badge) {\n badge.textContent = count > 0 ? String(count) : ''\n const item = badge.closest<HTMLElement>('.sidebar__item')\n item?.setAttribute('data-badge-visible', String(count > 0))\n }\n }\n\n /** Toggle the sidebar between collapsed and expanded states. */\n toggleCollapse(): void {\n const isCollapsed = this.aside.classList.toggle('sidebar--collapsed')\n this.updateCollapseButton()\n if (this.config.onCollapse) {\n this.config.onCollapse(isCollapsed)\n }\n }\n\n /** Collapse the sidebar. */\n collapse(): void {\n const wasCollapsed = this.aside.classList.contains('sidebar--collapsed')\n this.aside.classList.add('sidebar--collapsed')\n this.updateCollapseButton()\n if (!wasCollapsed && this.config.onCollapse) {\n this.config.onCollapse(true)\n }\n }\n\n /** Expand the sidebar. */\n expand(): void {\n const wasCollapsed = this.aside.classList.contains('sidebar--collapsed')\n this.aside.classList.remove('sidebar--collapsed')\n this.updateCollapseButton()\n if (wasCollapsed && this.config.onCollapse) {\n this.config.onCollapse(false)\n }\n }\n\n /** Remove the sidebar from the DOM. */\n destroy(): void {\n this.aside.remove()\n }\n\n private updateCollapseButton(): void {\n if (this.collapseButton === null) {\n return\n }\n\n const isCollapsed = this.aside.classList.contains('sidebar--collapsed')\n const label = isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'\n this.collapseButton.setAttribute('aria-label', label)\n this.collapseButton.setAttribute('aria-expanded', String(!isCollapsed))\n this.collapseButton.setAttribute('title', label)\n this.collapseButton.setAttribute('data-collapsed', String(isCollapsed))\n this.collapseButton\n .querySelector('path')\n ?.setAttribute('d', isCollapsed ? 'm9 18 6-6-6-6' : 'm15 18-6-6 6-6')\n }\n}\n"],"mappings":"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"}
|
package/dist/components/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "../data-table-YEkMufAM.js";
|
|
2
|
-
import { t } from "../tree-view-
|
|
2
|
+
import { t } from "../tree-view-gML5wGlz.js";
|
|
3
3
|
import { TabPanel as n } from "./tabs/index.js";
|
|
4
4
|
import { SplitPane as r } from "./split-pane/index.js";
|
|
5
5
|
import { Modal as i } from "./modal/index.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/tree-view/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAO5D,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAsBvD,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAA0B;IAG5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,SAAS,CAAkD;gBAEvD,MAAM,EAAE,cAAc;IAgBlC,sCAAsC;IACtC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI;IAQ/B,mCAAmC;IACnC,SAAS,IAAI,IAAI;IAMjB,qCAAqC;IACrC,WAAW,IAAI,IAAI;IAMnB,mCAAmC;IACnC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAS5B,qCAAqC;IACrC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAS9B,sDAAsD;IACtD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK9B,0CAA0C;IAC1C,eAAe,IAAI,QAAQ,GAAG,IAAI;IAIlC,gDAAgD;IAChD,OAAO,IAAI,IAAI;IASf,2CAA2C;IAC3C,OAAO,CAAC,cAAc;IAItB,gCAAgC;IAChC,OAAO,CAAC,UAAU;IAWlB,kDAAkD;IAClD,OAAO,CAAC,oBAAoB;IAc5B,oCAAoC;IACpC,OAAO,CAAC,YAAY;IAepB,mEAAmE;IACnE,OAAO,CAAC,iBAAiB;IAoBzB,+DAA+D;IAC/D,OAAO,CAAC,cAAc;IAStB,wCAAwC;IACxC,OAAO,CAAC,YAAY;IAmBpB,6CAA6C;IAC7C,OAAO,CAAC,WAAW;IAInB,yDAAyD;IACzD,OAAO,CAAC,kBAAkB;IAoB1B,iDAAiD;IACjD,OAAO,CAAC,aAAa;IAuHrB,4BAA4B;IAC5B,OAAO,CAAC,SAAS;IAgBjB,kCAAkC;IAClC,OAAO,CAAC,UAAU;IAQlB,oCAAoC;IACpC,OAAO,CAAC,gBAAgB;IAuBxB,8CAA8C;IAC9C,OAAO,CAAC,MAAM;IA6Dd,mDAAmD;IACnD,OAAO,CAAC,WAAW;IAyCnB,6DAA6D;IAC7D,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/tree-view/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAO5D,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAsBvD,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAA0B;IAG5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,SAAS,CAAkD;gBAEvD,MAAM,EAAE,cAAc;IAgBlC,sCAAsC;IACtC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI;IAQ/B,mCAAmC;IACnC,SAAS,IAAI,IAAI;IAMjB,qCAAqC;IACrC,WAAW,IAAI,IAAI;IAMnB,mCAAmC;IACnC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAS5B,qCAAqC;IACrC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAS9B,sDAAsD;IACtD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK9B,0CAA0C;IAC1C,eAAe,IAAI,QAAQ,GAAG,IAAI;IAIlC,gDAAgD;IAChD,OAAO,IAAI,IAAI;IASf,2CAA2C;IAC3C,OAAO,CAAC,cAAc;IAItB,gCAAgC;IAChC,OAAO,CAAC,UAAU;IAWlB,kDAAkD;IAClD,OAAO,CAAC,oBAAoB;IAc5B,oCAAoC;IACpC,OAAO,CAAC,YAAY;IAepB,mEAAmE;IACnE,OAAO,CAAC,iBAAiB;IAoBzB,+DAA+D;IAC/D,OAAO,CAAC,cAAc;IAStB,wCAAwC;IACxC,OAAO,CAAC,YAAY;IAmBpB,6CAA6C;IAC7C,OAAO,CAAC,WAAW;IAInB,yDAAyD;IACzD,OAAO,CAAC,kBAAkB;IAoB1B,iDAAiD;IACjD,OAAO,CAAC,aAAa;IAuHrB,4BAA4B;IAC5B,OAAO,CAAC,SAAS;IAgBjB,kCAAkC;IAClC,OAAO,CAAC,UAAU;IAQlB,oCAAoC;IACpC,OAAO,CAAC,gBAAgB;IAuBxB,8CAA8C;IAC9C,OAAO,CAAC,MAAM;IA6Dd,mDAAmD;IACnD,OAAO,CAAC,WAAW;IAyCnB,6DAA6D;IAC7D,OAAO,CAAC,gBAAgB;IA0HxB,4DAA4D;YAC9C,YAAY;IAmB1B,6CAA6C;IAC7C,OAAO,CAAC,aAAa;CAatB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "../../tree-view-
|
|
1
|
+
import { t as e } from "../../tree-view-gML5wGlz.js";
|
|
2
2
|
export { e as TreeView };
|
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { YamlEditor as gn } from "./editors/yaml-editor.js";
|
|
|
24
24
|
import { DiffViewer as _n } from "./editors/diff-viewer.js";
|
|
25
25
|
import "./editors/index.js";
|
|
26
26
|
import { t as vn } from "./data-table-YEkMufAM.js";
|
|
27
|
-
import { t as yn } from "./tree-view-
|
|
27
|
+
import { t as yn } from "./tree-view-gML5wGlz.js";
|
|
28
28
|
import { TabPanel as bn } from "./components/tabs/index.js";
|
|
29
29
|
import { SplitPane as xn } from "./components/split-pane/index.js";
|
|
30
30
|
import { Modal as Sn } from "./components/modal/index.js";
|
package/dist/react/sidebar.js
CHANGED
package/dist/sidebar/index.js
CHANGED
package/dist/styles/sidebar.css
CHANGED
|
@@ -458,8 +458,8 @@ button.sidebar__brand {
|
|
|
458
458
|
|
|
459
459
|
.sidebar__item-icon {
|
|
460
460
|
flex-shrink: 0;
|
|
461
|
-
width:
|
|
462
|
-
height:
|
|
461
|
+
width: 16px;
|
|
462
|
+
height: 16px;
|
|
463
463
|
color: var(--color-text-muted);
|
|
464
464
|
transition: color 0.2s;
|
|
465
465
|
}
|
|
@@ -1037,11 +1037,11 @@ button.sidebar__brand {
|
|
|
1037
1037
|
|
|
1038
1038
|
.sidebar--collapsed .sidebar__item {
|
|
1039
1039
|
justify-content: center;
|
|
1040
|
-
padding: 0;
|
|
1040
|
+
padding: 0.5rem;
|
|
1041
1041
|
border-left: none;
|
|
1042
|
-
width:
|
|
1043
|
-
height:
|
|
1044
|
-
border-radius:
|
|
1042
|
+
width: auto;
|
|
1043
|
+
height: auto;
|
|
1044
|
+
border-radius: 0.375rem;
|
|
1045
1045
|
margin: 0 auto;
|
|
1046
1046
|
}
|
|
1047
1047
|
|
|
@@ -1049,6 +1049,18 @@ button.sidebar__brand {
|
|
|
1049
1049
|
margin: 0;
|
|
1050
1050
|
}
|
|
1051
1051
|
|
|
1052
|
+
/* ============================================================================
|
|
1053
|
+
TOUCH DEVICE ENHANCEMENTS (WCAG 2.5.5 - 44px minimum)
|
|
1054
|
+
Desktop keeps compact 32px collapsed items; touch devices get a 44px
|
|
1055
|
+
minimum tap target without altering the visual icon size.
|
|
1056
|
+
============================================================================ */
|
|
1057
|
+
@media (pointer: coarse) {
|
|
1058
|
+
.sidebar--collapsed .sidebar__item {
|
|
1059
|
+
min-width: var(--cmm-touch-target-min, 44px);
|
|
1060
|
+
min-height: var(--cmm-touch-target-min, 44px);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1052
1064
|
.sidebar--collapsed .sidebar__section {
|
|
1053
1065
|
margin-top: 0.25rem;
|
|
1054
1066
|
}
|
|
@@ -240,9 +240,9 @@ var c = class {
|
|
|
240
240
|
let s = e.children !== void 0 || e.lazyLoad !== void 0, c = e.expandable !== !1 && s;
|
|
241
241
|
c && a.setAttribute("aria-expanded", e.expanded === !0 ? "true" : "false"), this.selectedNode !== null && this.selectedNode.id === e.id ? a.setAttribute("aria-selected", "true") : a.setAttribute("aria-selected", "false");
|
|
242
242
|
let l = document.createElement("span");
|
|
243
|
-
if (l.className = "cmm-tree-node__toggle", l.setAttribute("aria-hidden", "true"), c ?
|
|
243
|
+
if (l.className = "cmm-tree-node__toggle", l.setAttribute("aria-hidden", "true"), c ? l.addEventListener("click", (t) => {
|
|
244
244
|
t.stopPropagation(), this.handleToggle(e);
|
|
245
|
-
})
|
|
245
|
+
}) : l.style.visibility = "hidden", a.appendChild(l), e.icon !== void 0) {
|
|
246
246
|
let t = document.createElement("span");
|
|
247
247
|
t.className = "cmm-tree-node__icon", t.setAttribute("aria-hidden", "true"), t.textContent = e.icon, a.appendChild(t);
|
|
248
248
|
}
|
|
@@ -273,4 +273,4 @@ var c = class {
|
|
|
273
273
|
//#endregion
|
|
274
274
|
export { c as t };
|
|
275
275
|
|
|
276
|
-
//# sourceMappingURL=tree-view-
|
|
276
|
+
//# sourceMappingURL=tree-view-gML5wGlz.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-view-SpbHG9Yi.js","names":[],"sources":["../src/components/tree-view/lazy.ts","../src/components/tree-view/index.ts"],"sourcesContent":["/**\n * TreeView lazy loading helper\n * @countermeasure/web-components/components/tree-view\n */\n\nimport { type TreeNode } from './types'\n\n/**\n * Load children for a tree node using its lazyLoad function.\n * Caches the result on node.children after the first load.\n */\nexport async function loadChildren(node: TreeNode): Promise<TreeNode[]> {\n if (node.children !== undefined && node.children.length > 0) {\n return node.children\n }\n\n if (node.lazyLoad === undefined) {\n return []\n }\n\n const children = await node.lazyLoad()\n node.children = children\n return children\n}\n","/**\n * TreeView - Accessible expandable tree component with lazy loading and search\n * @countermeasure/web-components/components/tree-view\n *\n * Implements ARIA Tree pattern with full keyboard navigation.\n * WCAG 2.1 AA compliant.\n */\n\nimport { type TreeNode, type TreeViewConfig } from './types'\nimport { loadChildren } from './lazy'\nimport { uniqueId } from '../../primitives/utils'\nimport { escapeHtml } from '../../utils/sanitize'\nimport { announce, createLiveRegion, type LiveRegion } from '../../utils/a11y'\nimport { Keys, createTypeAhead } from '../../utils/keyboard'\n\nexport type { TreeNode, TreeViewConfig } from './types'\n\n/** Resolve a container reference to an HTMLElement */\nfunction resolveContainer(container: HTMLElement | string): HTMLElement {\n if (typeof container === 'string') {\n const el = document.querySelector<HTMLElement>(container)\n if (!el) {\n throw new Error(`TreeView container not found: ${container}`)\n }\n return el\n }\n return container\n}\n\n/** Flatten tree nodes with their depth for keyboard navigation */\ninterface FlatNode {\n node: TreeNode\n depth: number\n parent: TreeNode | null\n index: number\n}\n\nexport class TreeView {\n private readonly container: HTMLElement\n private readonly config: TreeViewConfig\n private data: TreeNode[] = []\n private selectedNode: TreeNode | null = null\n private focusedNode: TreeNode | null = null\n private wrapperEl: HTMLElement | null = null\n private highlightQuery = ''\n private liveRegion: LiveRegion | null = null\n\n // Accessibility state\n private readonly treeId: string\n private readonly labelId: string\n private flatNodes: FlatNode[] = []\n private typeAhead: ReturnType<typeof createTypeAhead> | null = null\n\n constructor(config: TreeViewConfig) {\n this.container = resolveContainer(config.container)\n this.config = config\n this.treeId = uniqueId('cmm-tree')\n this.labelId = uniqueId('cmm-tree-label')\n\n if (config.data !== undefined) {\n this.data = config.data\n }\n\n // Create live region for announcements\n this.liveRegion = createLiveRegion('polite')\n\n this.render()\n }\n\n /** Replace all nodes and re-render */\n setData(data: TreeNode[]): void {\n this.data = data\n this.selectedNode = null\n this.focusedNode = null\n this.render()\n this.announceUpdate(`Tree updated with ${String(this.countNodes(data))} items`)\n }\n\n /** Expand all nodes recursively */\n expandAll(): void {\n this.setExpandedRecursive(this.data, true)\n this.render()\n this.announceUpdate('All nodes expanded')\n }\n\n /** Collapse all nodes recursively */\n collapseAll(): void {\n this.setExpandedRecursive(this.data, false)\n this.render()\n this.announceUpdate('All nodes collapsed')\n }\n\n /** Expand a specific node by id */\n expandNode(id: string): void {\n const node = this.findNodeById(this.data, id)\n if (node) {\n node.expanded = true\n this.render()\n this.announceUpdate(`${node.label} expanded`)\n }\n }\n\n /** Collapse a specific node by id */\n collapseNode(id: string): void {\n const node = this.findNodeById(this.data, id)\n if (node) {\n node.expanded = false\n this.render()\n this.announceUpdate(`${node.label} collapsed`)\n }\n }\n\n /** Highlight nodes matching the given query string */\n highlight(query: string): void {\n this.highlightQuery = query.toLowerCase()\n this.render()\n }\n\n /** Get the last selected node, or null */\n getSelectedNode(): TreeNode | null {\n return this.selectedNode\n }\n\n /** Remove the tree from the DOM and clean up */\n destroy(): void {\n this.container.innerHTML = ''\n this.wrapperEl = null\n this.selectedNode = null\n this.focusedNode = null\n this.liveRegion?.destroy()\n this.liveRegion = null\n }\n\n /** Announce an update to screen readers */\n private announceUpdate(message: string): void {\n this.liveRegion?.announce(message)\n }\n\n /** Count total nodes in tree */\n private countNodes(nodes: TreeNode[]): number {\n let count = 0\n for (const node of nodes) {\n count++\n if (node.children !== undefined) {\n count += this.countNodes(node.children)\n }\n }\n return count\n }\n\n /** Set expanded state on all nodes recursively */\n private setExpandedRecursive(nodes: TreeNode[], expanded: boolean): void {\n for (const node of nodes) {\n if (\n node.expandable !== false &&\n (node.children !== undefined || node.lazyLoad !== undefined)\n ) {\n node.expanded = expanded\n }\n if (node.children !== undefined) {\n this.setExpandedRecursive(node.children, expanded)\n }\n }\n }\n\n /** Find a node by id in the tree */\n private findNodeById(nodes: TreeNode[], id: string): TreeNode | null {\n for (const node of nodes) {\n if (node.id === id) {\n return node\n }\n if (node.children !== undefined) {\n const found = this.findNodeById(node.children, id)\n if (found) {\n return found\n }\n }\n }\n return null\n }\n\n /** Check if a node or any descendant matches the current search */\n private nodeMatchesSearch(node: TreeNode, query: string): boolean {\n if (query === '') {\n return true\n }\n\n if (node.label.toLowerCase().includes(query)) {\n return true\n }\n\n if (node.children !== undefined) {\n for (const child of node.children) {\n if (this.nodeMatchesSearch(child, query)) {\n return true\n }\n }\n }\n\n return false\n }\n\n /** Build flat list of visible nodes for keyboard navigation */\n private buildFlatNodes(): void {\n this.flatNodes = []\n this.flattenNodes(this.data, 0, null)\n\n // Update type-ahead with labels\n const items = this.flatNodes.map(fn => ({ label: fn.node.label }))\n this.typeAhead = createTypeAhead({ items })\n }\n\n /** Recursively flatten visible nodes */\n private flattenNodes(nodes: TreeNode[], depth: number, parent: TreeNode | null): void {\n for (const node of nodes) {\n if (this.highlightQuery !== '' && !this.nodeMatchesSearch(node, this.highlightQuery)) {\n continue\n }\n\n this.flatNodes.push({\n node,\n depth,\n parent,\n index: this.flatNodes.length,\n })\n\n if (node.expanded === true && node.children !== undefined && node.children.length > 0) {\n this.flattenNodes(node.children, depth + 1, node)\n }\n }\n }\n\n /** Get the flat node info for a tree node */\n private getFlatNode(node: TreeNode): FlatNode | null {\n return this.flatNodes.find(fn => fn.node.id === node.id) ?? null\n }\n\n /** Get visible nodes (for aria-setsize) at same level */\n private getSiblingsAtLevel(node: TreeNode): TreeNode[] {\n const flatNode = this.getFlatNode(node)\n if (flatNode === null) {\n return []\n }\n\n const parent = flatNode.parent\n if (parent === null) {\n // Root level\n return this.data.filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n }\n\n // Get parent's children\n return (parent.children ?? []).filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n }\n\n /** Handle keyboard navigation within the tree */\n private handleKeyDown(event: KeyboardEvent): void {\n if (this.flatNodes.length === 0 || this.focusedNode === null) {\n return\n }\n\n const currentFlatNode = this.getFlatNode(this.focusedNode)\n if (currentFlatNode === null) {\n return\n }\n\n const currentIndex = currentFlatNode.index\n const node = this.focusedNode\n const hasChildren = node.children !== undefined || node.lazyLoad !== undefined\n const isExpanded = node.expanded === true\n\n switch (event.key) {\n case Keys.ARROW_DOWN: {\n event.preventDefault()\n // Move to next visible node\n if (currentIndex < this.flatNodes.length - 1) {\n const nextFlatNode = this.flatNodes[currentIndex + 1]\n if (nextFlatNode !== undefined) {\n this.focusNode(nextFlatNode.node)\n }\n }\n break\n }\n case Keys.ARROW_UP: {\n event.preventDefault()\n // Move to previous visible node\n if (currentIndex > 0) {\n const prevFlatNode = this.flatNodes[currentIndex - 1]\n if (prevFlatNode !== undefined) {\n this.focusNode(prevFlatNode.node)\n }\n }\n break\n }\n case Keys.ARROW_RIGHT: {\n event.preventDefault()\n if (hasChildren) {\n if (!isExpanded) {\n // Expand closed node\n void this.handleToggle(node)\n } else if (node.children !== undefined && node.children.length > 0) {\n // Move to first child\n const firstChild = node.children[0]\n if (firstChild !== undefined) {\n this.focusNode(firstChild)\n }\n }\n }\n break\n }\n case Keys.ARROW_LEFT: {\n event.preventDefault()\n if (isExpanded && hasChildren) {\n // Collapse expanded node\n void this.handleToggle(node)\n } else if (currentFlatNode.parent !== null) {\n // Move to parent\n this.focusNode(currentFlatNode.parent)\n }\n break\n }\n case Keys.HOME: {\n event.preventDefault()\n // Move to first visible node\n const firstFlatNode = this.flatNodes[0]\n if (firstFlatNode !== undefined) {\n this.focusNode(firstFlatNode.node)\n }\n break\n }\n case Keys.END: {\n event.preventDefault()\n // Move to last visible node\n const lastFlatNode = this.flatNodes[this.flatNodes.length - 1]\n if (lastFlatNode !== undefined) {\n this.focusNode(lastFlatNode.node)\n }\n break\n }\n case Keys.ENTER:\n case Keys.SPACE: {\n event.preventDefault()\n // Select/activate the node\n this.selectNode(node)\n break\n }\n case '*': {\n event.preventDefault()\n // Expand all siblings at this level\n const siblings = this.getSiblingsAtLevel(node)\n for (const sibling of siblings) {\n if (sibling.expandable !== false && sibling.children !== undefined) {\n sibling.expanded = true\n }\n }\n this.render()\n this.announceUpdate('All siblings expanded')\n break\n }\n default: {\n // Type-ahead: focus node starting with typed character\n if (event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey) {\n const matchIndex = this.typeAhead?.handleKey(event.key)\n if (matchIndex !== undefined && matchIndex >= 0) {\n const matchedFlatNode = this.flatNodes[matchIndex]\n if (matchedFlatNode !== undefined) {\n this.focusNode(matchedFlatNode.node)\n }\n }\n }\n break\n }\n }\n }\n\n /** Focus a specific node */\n private focusNode(node: TreeNode): void {\n this.focusedNode = node\n this.updateFocusState()\n\n // Announce the node\n const hasChildren = node.children !== undefined || node.lazyLoad !== undefined\n const isExpanded = node.expanded === true\n let announcement = node.label\n\n if (hasChildren) {\n announcement += isExpanded ? ', expanded' : ', collapsed'\n }\n\n announce(announcement, 'polite')\n }\n\n /** Select a node (activate it) */\n private selectNode(node: TreeNode): void {\n this.selectedNode = node\n this.focusedNode = node\n this.config.onSelect?.(node)\n this.render()\n this.announceUpdate(`${node.label} selected`)\n }\n\n /** Update focus state in the DOM */\n private updateFocusState(): void {\n if (!this.wrapperEl) {\n return\n }\n\n // Remove focus from all nodes\n const allItems = this.wrapperEl.querySelectorAll<HTMLElement>('[role=\"treeitem\"]')\n for (const item of allItems) {\n item.setAttribute('tabindex', '-1')\n }\n\n // Set focus on current node\n if (this.focusedNode !== null) {\n const focusedEl = this.wrapperEl.querySelector<HTMLElement>(\n `[data-node-id=\"${this.focusedNode.id}\"] > [role=\"treeitem\"]`\n )\n if (focusedEl !== null) {\n focusedEl.setAttribute('tabindex', '0')\n focusedEl.focus()\n }\n }\n }\n\n /** Render the full tree into the container */\n private render(): void {\n this.container.innerHTML = ''\n\n const wrapper = document.createElement('div')\n wrapper.className = 'cmm-tree-view'\n this.wrapperEl = wrapper\n\n // Accessible label (hidden)\n const label = document.createElement('span')\n label.id = this.labelId\n label.className = 'cmm-sr-only'\n label.textContent =\n 'Tree navigation. Use arrow keys to navigate. Press Enter or Space to select.'\n wrapper.appendChild(label)\n\n // Search input\n if (this.config.searchable === true) {\n const searchBox = document.createElement('div')\n searchBox.className = 'cmm-tree-view__search'\n\n const searchLabel = document.createElement('label')\n searchLabel.className = 'cmm-sr-only'\n const searchId = uniqueId('cmm-tree-search')\n searchLabel.setAttribute('for', searchId)\n searchLabel.textContent = 'Filter tree'\n searchBox.appendChild(searchLabel)\n\n const input = document.createElement('input')\n input.type = 'text'\n input.id = searchId\n input.className = 'cmm-tree-view__search-input'\n input.placeholder = 'Filter...'\n input.value = this.highlightQuery\n input.setAttribute('aria-label', 'Filter tree items')\n input.addEventListener('input', () => {\n this.highlightQuery = input.value.toLowerCase()\n this.renderNodes()\n })\n\n searchBox.appendChild(input)\n wrapper.appendChild(searchBox)\n }\n\n // Tree container\n const treeContainer = document.createElement('div')\n treeContainer.className = 'cmm-tree-view__nodes'\n treeContainer.id = this.treeId\n treeContainer.setAttribute('role', 'tree')\n treeContainer.setAttribute('aria-labelledby', this.labelId)\n treeContainer.dataset.role = 'nodes'\n\n // Add keyboard handler\n treeContainer.addEventListener('keydown', e => {\n this.handleKeyDown(e)\n })\n\n wrapper.appendChild(treeContainer)\n this.container.appendChild(wrapper)\n this.renderNodes()\n }\n\n /** Re-render only the nodes portion of the tree */\n private renderNodes(): void {\n if (!this.wrapperEl) {\n return\n }\n\n const nodesContainer = this.wrapperEl.querySelector<HTMLElement>('[data-role=\"nodes\"]')\n if (!nodesContainer) {\n return\n }\n\n nodesContainer.innerHTML = ''\n\n // Build flat nodes for keyboard navigation\n this.buildFlatNodes()\n\n // Get visible root nodes for aria-setsize\n const visibleRootNodes = this.data.filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n\n let posInSet = 0\n for (const node of this.data) {\n if (this.highlightQuery !== '' && !this.nodeMatchesSearch(node, this.highlightQuery)) {\n continue\n }\n posInSet++\n const el = this.buildNodeElement(node, 0, posInSet, visibleRootNodes.length)\n nodesContainer.appendChild(el)\n }\n\n // Focus first node if none focused\n if (this.focusedNode === null && this.flatNodes.length > 0) {\n const firstNode = this.flatNodes[0]?.node\n if (firstNode !== undefined) {\n this.focusedNode = firstNode\n }\n }\n\n this.updateFocusState()\n }\n\n /** Build a DOM element for a single tree node (recursive) */\n private buildNodeElement(\n node: TreeNode,\n depth: number,\n posInSet: number,\n setSize: number\n ): HTMLElement {\n const nodeEl = document.createElement('div')\n nodeEl.className = 'cmm-tree-node'\n nodeEl.dataset.nodeId = node.id\n\n if (node.expanded === true) {\n nodeEl.classList.add('cmm-tree-node--expanded')\n }\n\n if (this.selectedNode !== null && this.selectedNode.id === node.id) {\n nodeEl.classList.add('cmm-tree-node--selected')\n }\n\n // The row is the treeitem\n const row = document.createElement('div')\n row.className = 'cmm-tree-node__row'\n row.setAttribute('role', 'treeitem')\n row.setAttribute('aria-level', String(depth + 1))\n row.setAttribute('aria-setsize', String(setSize))\n row.setAttribute('aria-posinset', String(posInSet))\n row.style.paddingLeft = `${String(depth * 16 + 8)}px`\n\n // Determine if node is focusable\n const isFocused = this.focusedNode !== null && this.focusedNode.id === node.id\n row.setAttribute('tabindex', isFocused ? '0' : '-1')\n\n // Handle expand/collapse state\n const hasChildren = node.children !== undefined || node.lazyLoad !== undefined\n const isExpandable = node.expandable !== false && hasChildren\n\n if (isExpandable) {\n row.setAttribute('aria-expanded', node.expanded === true ? 'true' : 'false')\n }\n\n // Selection state\n if (this.selectedNode !== null && this.selectedNode.id === node.id) {\n row.setAttribute('aria-selected', 'true')\n } else {\n row.setAttribute('aria-selected', 'false')\n }\n\n // Toggle button (for visual indication)\n const toggle = document.createElement('span')\n toggle.className = 'cmm-tree-node__toggle'\n toggle.setAttribute('aria-hidden', 'true')\n\n if (isExpandable) {\n toggle.textContent = node.expanded === true ? '\\u25BE' : '\\u25B8'\n toggle.addEventListener('click', (e: Event) => {\n e.stopPropagation()\n void this.handleToggle(node)\n })\n } else {\n toggle.textContent = ' '\n toggle.style.visibility = 'hidden'\n }\n\n row.appendChild(toggle)\n\n // Icon\n if (node.icon !== undefined) {\n const icon = document.createElement('span')\n icon.className = 'cmm-tree-node__icon'\n icon.setAttribute('aria-hidden', 'true')\n icon.textContent = node.icon\n row.appendChild(icon)\n }\n\n // Label\n const label = document.createElement('span')\n label.className = 'cmm-tree-node__label'\n\n if (this.highlightQuery !== '' && node.label.toLowerCase().includes(this.highlightQuery)) {\n label.innerHTML = this.highlightText(node.label, this.highlightQuery)\n } else {\n label.textContent = node.label\n }\n\n row.appendChild(label)\n\n // Click handlers\n row.addEventListener('click', () => {\n this.selectNode(node)\n })\n\n row.addEventListener('dblclick', () => {\n this.config.onDoubleClick?.(node)\n })\n\n nodeEl.appendChild(row)\n\n // Children (nested group)\n if (node.expanded === true && node.children !== undefined && node.children.length > 0) {\n const childrenEl = document.createElement('div')\n childrenEl.className = 'cmm-tree-node__children'\n childrenEl.setAttribute('role', 'group')\n\n // Get visible children for setsize\n const visibleChildren = node.children.filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n\n let childPosInSet = 0\n for (const child of node.children) {\n if (this.highlightQuery !== '' && !this.nodeMatchesSearch(child, this.highlightQuery)) {\n continue\n }\n childPosInSet++\n childrenEl.appendChild(\n this.buildNodeElement(child, depth + 1, childPosInSet, visibleChildren.length)\n )\n }\n\n nodeEl.appendChild(childrenEl)\n }\n\n return nodeEl\n }\n\n /** Handle expand/collapse toggle, including lazy loading */\n private async handleToggle(node: TreeNode): Promise<void> {\n if (node.expanded === true) {\n node.expanded = false\n this.announceUpdate(`${node.label} collapsed`)\n } else {\n node.expanded = true\n if (\n node.lazyLoad !== undefined &&\n (node.children === undefined || node.children.length === 0)\n ) {\n this.announceUpdate(`Loading ${node.label}`)\n await loadChildren(node)\n }\n this.announceUpdate(`${node.label} expanded`)\n this.config.onExpand?.(node)\n }\n this.render()\n }\n\n /** Highlight matching text within a label */\n private highlightText(text: string, query: string): string {\n const lowerText = text.toLowerCase()\n const idx = lowerText.indexOf(query)\n if (idx < 0) {\n return escapeHtml(text)\n }\n\n const before = text.slice(0, idx)\n const match = text.slice(idx, idx + query.length)\n const after = text.slice(idx + query.length)\n\n return `${escapeHtml(before)}<mark class=\"cmm-tree-node__highlight\">${escapeHtml(match)}</mark>${escapeHtml(after)}`\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tree-view-gML5wGlz.js","names":[],"sources":["../src/components/tree-view/lazy.ts","../src/components/tree-view/index.ts"],"sourcesContent":["/**\n * TreeView lazy loading helper\n * @countermeasure/web-components/components/tree-view\n */\n\nimport { type TreeNode } from './types'\n\n/**\n * Load children for a tree node using its lazyLoad function.\n * Caches the result on node.children after the first load.\n */\nexport async function loadChildren(node: TreeNode): Promise<TreeNode[]> {\n if (node.children !== undefined && node.children.length > 0) {\n return node.children\n }\n\n if (node.lazyLoad === undefined) {\n return []\n }\n\n const children = await node.lazyLoad()\n node.children = children\n return children\n}\n","/**\n * TreeView - Accessible expandable tree component with lazy loading and search\n * @countermeasure/web-components/components/tree-view\n *\n * Implements ARIA Tree pattern with full keyboard navigation.\n * WCAG 2.1 AA compliant.\n */\n\nimport { type TreeNode, type TreeViewConfig } from './types'\nimport { loadChildren } from './lazy'\nimport { uniqueId } from '../../primitives/utils'\nimport { escapeHtml } from '../../utils/sanitize'\nimport { announce, createLiveRegion, type LiveRegion } from '../../utils/a11y'\nimport { Keys, createTypeAhead } from '../../utils/keyboard'\n\nexport type { TreeNode, TreeViewConfig } from './types'\n\n/** Resolve a container reference to an HTMLElement */\nfunction resolveContainer(container: HTMLElement | string): HTMLElement {\n if (typeof container === 'string') {\n const el = document.querySelector<HTMLElement>(container)\n if (!el) {\n throw new Error(`TreeView container not found: ${container}`)\n }\n return el\n }\n return container\n}\n\n/** Flatten tree nodes with their depth for keyboard navigation */\ninterface FlatNode {\n node: TreeNode\n depth: number\n parent: TreeNode | null\n index: number\n}\n\nexport class TreeView {\n private readonly container: HTMLElement\n private readonly config: TreeViewConfig\n private data: TreeNode[] = []\n private selectedNode: TreeNode | null = null\n private focusedNode: TreeNode | null = null\n private wrapperEl: HTMLElement | null = null\n private highlightQuery = ''\n private liveRegion: LiveRegion | null = null\n\n // Accessibility state\n private readonly treeId: string\n private readonly labelId: string\n private flatNodes: FlatNode[] = []\n private typeAhead: ReturnType<typeof createTypeAhead> | null = null\n\n constructor(config: TreeViewConfig) {\n this.container = resolveContainer(config.container)\n this.config = config\n this.treeId = uniqueId('cmm-tree')\n this.labelId = uniqueId('cmm-tree-label')\n\n if (config.data !== undefined) {\n this.data = config.data\n }\n\n // Create live region for announcements\n this.liveRegion = createLiveRegion('polite')\n\n this.render()\n }\n\n /** Replace all nodes and re-render */\n setData(data: TreeNode[]): void {\n this.data = data\n this.selectedNode = null\n this.focusedNode = null\n this.render()\n this.announceUpdate(`Tree updated with ${String(this.countNodes(data))} items`)\n }\n\n /** Expand all nodes recursively */\n expandAll(): void {\n this.setExpandedRecursive(this.data, true)\n this.render()\n this.announceUpdate('All nodes expanded')\n }\n\n /** Collapse all nodes recursively */\n collapseAll(): void {\n this.setExpandedRecursive(this.data, false)\n this.render()\n this.announceUpdate('All nodes collapsed')\n }\n\n /** Expand a specific node by id */\n expandNode(id: string): void {\n const node = this.findNodeById(this.data, id)\n if (node) {\n node.expanded = true\n this.render()\n this.announceUpdate(`${node.label} expanded`)\n }\n }\n\n /** Collapse a specific node by id */\n collapseNode(id: string): void {\n const node = this.findNodeById(this.data, id)\n if (node) {\n node.expanded = false\n this.render()\n this.announceUpdate(`${node.label} collapsed`)\n }\n }\n\n /** Highlight nodes matching the given query string */\n highlight(query: string): void {\n this.highlightQuery = query.toLowerCase()\n this.render()\n }\n\n /** Get the last selected node, or null */\n getSelectedNode(): TreeNode | null {\n return this.selectedNode\n }\n\n /** Remove the tree from the DOM and clean up */\n destroy(): void {\n this.container.innerHTML = ''\n this.wrapperEl = null\n this.selectedNode = null\n this.focusedNode = null\n this.liveRegion?.destroy()\n this.liveRegion = null\n }\n\n /** Announce an update to screen readers */\n private announceUpdate(message: string): void {\n this.liveRegion?.announce(message)\n }\n\n /** Count total nodes in tree */\n private countNodes(nodes: TreeNode[]): number {\n let count = 0\n for (const node of nodes) {\n count++\n if (node.children !== undefined) {\n count += this.countNodes(node.children)\n }\n }\n return count\n }\n\n /** Set expanded state on all nodes recursively */\n private setExpandedRecursive(nodes: TreeNode[], expanded: boolean): void {\n for (const node of nodes) {\n if (\n node.expandable !== false &&\n (node.children !== undefined || node.lazyLoad !== undefined)\n ) {\n node.expanded = expanded\n }\n if (node.children !== undefined) {\n this.setExpandedRecursive(node.children, expanded)\n }\n }\n }\n\n /** Find a node by id in the tree */\n private findNodeById(nodes: TreeNode[], id: string): TreeNode | null {\n for (const node of nodes) {\n if (node.id === id) {\n return node\n }\n if (node.children !== undefined) {\n const found = this.findNodeById(node.children, id)\n if (found) {\n return found\n }\n }\n }\n return null\n }\n\n /** Check if a node or any descendant matches the current search */\n private nodeMatchesSearch(node: TreeNode, query: string): boolean {\n if (query === '') {\n return true\n }\n\n if (node.label.toLowerCase().includes(query)) {\n return true\n }\n\n if (node.children !== undefined) {\n for (const child of node.children) {\n if (this.nodeMatchesSearch(child, query)) {\n return true\n }\n }\n }\n\n return false\n }\n\n /** Build flat list of visible nodes for keyboard navigation */\n private buildFlatNodes(): void {\n this.flatNodes = []\n this.flattenNodes(this.data, 0, null)\n\n // Update type-ahead with labels\n const items = this.flatNodes.map(fn => ({ label: fn.node.label }))\n this.typeAhead = createTypeAhead({ items })\n }\n\n /** Recursively flatten visible nodes */\n private flattenNodes(nodes: TreeNode[], depth: number, parent: TreeNode | null): void {\n for (const node of nodes) {\n if (this.highlightQuery !== '' && !this.nodeMatchesSearch(node, this.highlightQuery)) {\n continue\n }\n\n this.flatNodes.push({\n node,\n depth,\n parent,\n index: this.flatNodes.length,\n })\n\n if (node.expanded === true && node.children !== undefined && node.children.length > 0) {\n this.flattenNodes(node.children, depth + 1, node)\n }\n }\n }\n\n /** Get the flat node info for a tree node */\n private getFlatNode(node: TreeNode): FlatNode | null {\n return this.flatNodes.find(fn => fn.node.id === node.id) ?? null\n }\n\n /** Get visible nodes (for aria-setsize) at same level */\n private getSiblingsAtLevel(node: TreeNode): TreeNode[] {\n const flatNode = this.getFlatNode(node)\n if (flatNode === null) {\n return []\n }\n\n const parent = flatNode.parent\n if (parent === null) {\n // Root level\n return this.data.filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n }\n\n // Get parent's children\n return (parent.children ?? []).filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n }\n\n /** Handle keyboard navigation within the tree */\n private handleKeyDown(event: KeyboardEvent): void {\n if (this.flatNodes.length === 0 || this.focusedNode === null) {\n return\n }\n\n const currentFlatNode = this.getFlatNode(this.focusedNode)\n if (currentFlatNode === null) {\n return\n }\n\n const currentIndex = currentFlatNode.index\n const node = this.focusedNode\n const hasChildren = node.children !== undefined || node.lazyLoad !== undefined\n const isExpanded = node.expanded === true\n\n switch (event.key) {\n case Keys.ARROW_DOWN: {\n event.preventDefault()\n // Move to next visible node\n if (currentIndex < this.flatNodes.length - 1) {\n const nextFlatNode = this.flatNodes[currentIndex + 1]\n if (nextFlatNode !== undefined) {\n this.focusNode(nextFlatNode.node)\n }\n }\n break\n }\n case Keys.ARROW_UP: {\n event.preventDefault()\n // Move to previous visible node\n if (currentIndex > 0) {\n const prevFlatNode = this.flatNodes[currentIndex - 1]\n if (prevFlatNode !== undefined) {\n this.focusNode(prevFlatNode.node)\n }\n }\n break\n }\n case Keys.ARROW_RIGHT: {\n event.preventDefault()\n if (hasChildren) {\n if (!isExpanded) {\n // Expand closed node\n void this.handleToggle(node)\n } else if (node.children !== undefined && node.children.length > 0) {\n // Move to first child\n const firstChild = node.children[0]\n if (firstChild !== undefined) {\n this.focusNode(firstChild)\n }\n }\n }\n break\n }\n case Keys.ARROW_LEFT: {\n event.preventDefault()\n if (isExpanded && hasChildren) {\n // Collapse expanded node\n void this.handleToggle(node)\n } else if (currentFlatNode.parent !== null) {\n // Move to parent\n this.focusNode(currentFlatNode.parent)\n }\n break\n }\n case Keys.HOME: {\n event.preventDefault()\n // Move to first visible node\n const firstFlatNode = this.flatNodes[0]\n if (firstFlatNode !== undefined) {\n this.focusNode(firstFlatNode.node)\n }\n break\n }\n case Keys.END: {\n event.preventDefault()\n // Move to last visible node\n const lastFlatNode = this.flatNodes[this.flatNodes.length - 1]\n if (lastFlatNode !== undefined) {\n this.focusNode(lastFlatNode.node)\n }\n break\n }\n case Keys.ENTER:\n case Keys.SPACE: {\n event.preventDefault()\n // Select/activate the node\n this.selectNode(node)\n break\n }\n case '*': {\n event.preventDefault()\n // Expand all siblings at this level\n const siblings = this.getSiblingsAtLevel(node)\n for (const sibling of siblings) {\n if (sibling.expandable !== false && sibling.children !== undefined) {\n sibling.expanded = true\n }\n }\n this.render()\n this.announceUpdate('All siblings expanded')\n break\n }\n default: {\n // Type-ahead: focus node starting with typed character\n if (event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey) {\n const matchIndex = this.typeAhead?.handleKey(event.key)\n if (matchIndex !== undefined && matchIndex >= 0) {\n const matchedFlatNode = this.flatNodes[matchIndex]\n if (matchedFlatNode !== undefined) {\n this.focusNode(matchedFlatNode.node)\n }\n }\n }\n break\n }\n }\n }\n\n /** Focus a specific node */\n private focusNode(node: TreeNode): void {\n this.focusedNode = node\n this.updateFocusState()\n\n // Announce the node\n const hasChildren = node.children !== undefined || node.lazyLoad !== undefined\n const isExpanded = node.expanded === true\n let announcement = node.label\n\n if (hasChildren) {\n announcement += isExpanded ? ', expanded' : ', collapsed'\n }\n\n announce(announcement, 'polite')\n }\n\n /** Select a node (activate it) */\n private selectNode(node: TreeNode): void {\n this.selectedNode = node\n this.focusedNode = node\n this.config.onSelect?.(node)\n this.render()\n this.announceUpdate(`${node.label} selected`)\n }\n\n /** Update focus state in the DOM */\n private updateFocusState(): void {\n if (!this.wrapperEl) {\n return\n }\n\n // Remove focus from all nodes\n const allItems = this.wrapperEl.querySelectorAll<HTMLElement>('[role=\"treeitem\"]')\n for (const item of allItems) {\n item.setAttribute('tabindex', '-1')\n }\n\n // Set focus on current node\n if (this.focusedNode !== null) {\n const focusedEl = this.wrapperEl.querySelector<HTMLElement>(\n `[data-node-id=\"${this.focusedNode.id}\"] > [role=\"treeitem\"]`\n )\n if (focusedEl !== null) {\n focusedEl.setAttribute('tabindex', '0')\n focusedEl.focus()\n }\n }\n }\n\n /** Render the full tree into the container */\n private render(): void {\n this.container.innerHTML = ''\n\n const wrapper = document.createElement('div')\n wrapper.className = 'cmm-tree-view'\n this.wrapperEl = wrapper\n\n // Accessible label (hidden)\n const label = document.createElement('span')\n label.id = this.labelId\n label.className = 'cmm-sr-only'\n label.textContent =\n 'Tree navigation. Use arrow keys to navigate. Press Enter or Space to select.'\n wrapper.appendChild(label)\n\n // Search input\n if (this.config.searchable === true) {\n const searchBox = document.createElement('div')\n searchBox.className = 'cmm-tree-view__search'\n\n const searchLabel = document.createElement('label')\n searchLabel.className = 'cmm-sr-only'\n const searchId = uniqueId('cmm-tree-search')\n searchLabel.setAttribute('for', searchId)\n searchLabel.textContent = 'Filter tree'\n searchBox.appendChild(searchLabel)\n\n const input = document.createElement('input')\n input.type = 'text'\n input.id = searchId\n input.className = 'cmm-tree-view__search-input'\n input.placeholder = 'Filter...'\n input.value = this.highlightQuery\n input.setAttribute('aria-label', 'Filter tree items')\n input.addEventListener('input', () => {\n this.highlightQuery = input.value.toLowerCase()\n this.renderNodes()\n })\n\n searchBox.appendChild(input)\n wrapper.appendChild(searchBox)\n }\n\n // Tree container\n const treeContainer = document.createElement('div')\n treeContainer.className = 'cmm-tree-view__nodes'\n treeContainer.id = this.treeId\n treeContainer.setAttribute('role', 'tree')\n treeContainer.setAttribute('aria-labelledby', this.labelId)\n treeContainer.dataset.role = 'nodes'\n\n // Add keyboard handler\n treeContainer.addEventListener('keydown', e => {\n this.handleKeyDown(e)\n })\n\n wrapper.appendChild(treeContainer)\n this.container.appendChild(wrapper)\n this.renderNodes()\n }\n\n /** Re-render only the nodes portion of the tree */\n private renderNodes(): void {\n if (!this.wrapperEl) {\n return\n }\n\n const nodesContainer = this.wrapperEl.querySelector<HTMLElement>('[data-role=\"nodes\"]')\n if (!nodesContainer) {\n return\n }\n\n nodesContainer.innerHTML = ''\n\n // Build flat nodes for keyboard navigation\n this.buildFlatNodes()\n\n // Get visible root nodes for aria-setsize\n const visibleRootNodes = this.data.filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n\n let posInSet = 0\n for (const node of this.data) {\n if (this.highlightQuery !== '' && !this.nodeMatchesSearch(node, this.highlightQuery)) {\n continue\n }\n posInSet++\n const el = this.buildNodeElement(node, 0, posInSet, visibleRootNodes.length)\n nodesContainer.appendChild(el)\n }\n\n // Focus first node if none focused\n if (this.focusedNode === null && this.flatNodes.length > 0) {\n const firstNode = this.flatNodes[0]?.node\n if (firstNode !== undefined) {\n this.focusedNode = firstNode\n }\n }\n\n this.updateFocusState()\n }\n\n /** Build a DOM element for a single tree node (recursive) */\n private buildNodeElement(\n node: TreeNode,\n depth: number,\n posInSet: number,\n setSize: number\n ): HTMLElement {\n const nodeEl = document.createElement('div')\n nodeEl.className = 'cmm-tree-node'\n nodeEl.dataset.nodeId = node.id\n\n if (node.expanded === true) {\n nodeEl.classList.add('cmm-tree-node--expanded')\n }\n\n if (this.selectedNode !== null && this.selectedNode.id === node.id) {\n nodeEl.classList.add('cmm-tree-node--selected')\n }\n\n // The row is the treeitem\n const row = document.createElement('div')\n row.className = 'cmm-tree-node__row'\n row.setAttribute('role', 'treeitem')\n row.setAttribute('aria-level', String(depth + 1))\n row.setAttribute('aria-setsize', String(setSize))\n row.setAttribute('aria-posinset', String(posInSet))\n row.style.paddingLeft = `${String(depth * 16 + 8)}px`\n\n // Determine if node is focusable\n const isFocused = this.focusedNode !== null && this.focusedNode.id === node.id\n row.setAttribute('tabindex', isFocused ? '0' : '-1')\n\n // Handle expand/collapse state\n const hasChildren = node.children !== undefined || node.lazyLoad !== undefined\n const isExpandable = node.expandable !== false && hasChildren\n\n if (isExpandable) {\n row.setAttribute('aria-expanded', node.expanded === true ? 'true' : 'false')\n }\n\n // Selection state\n if (this.selectedNode !== null && this.selectedNode.id === node.id) {\n row.setAttribute('aria-selected', 'true')\n } else {\n row.setAttribute('aria-selected', 'false')\n }\n\n // Toggle button (for visual indication)\n const toggle = document.createElement('span')\n toggle.className = 'cmm-tree-node__toggle'\n toggle.setAttribute('aria-hidden', 'true')\n\n if (isExpandable) {\n toggle.addEventListener('click', (e: Event) => {\n e.stopPropagation()\n void this.handleToggle(node)\n })\n } else {\n toggle.style.visibility = 'hidden'\n }\n\n row.appendChild(toggle)\n\n // Icon\n if (node.icon !== undefined) {\n const icon = document.createElement('span')\n icon.className = 'cmm-tree-node__icon'\n icon.setAttribute('aria-hidden', 'true')\n icon.textContent = node.icon\n row.appendChild(icon)\n }\n\n // Label\n const label = document.createElement('span')\n label.className = 'cmm-tree-node__label'\n\n if (this.highlightQuery !== '' && node.label.toLowerCase().includes(this.highlightQuery)) {\n label.innerHTML = this.highlightText(node.label, this.highlightQuery)\n } else {\n label.textContent = node.label\n }\n\n row.appendChild(label)\n\n // Click handlers\n row.addEventListener('click', () => {\n this.selectNode(node)\n })\n\n row.addEventListener('dblclick', () => {\n this.config.onDoubleClick?.(node)\n })\n\n nodeEl.appendChild(row)\n\n // Children (nested group)\n if (node.expanded === true && node.children !== undefined && node.children.length > 0) {\n const childrenEl = document.createElement('div')\n childrenEl.className = 'cmm-tree-node__children'\n childrenEl.setAttribute('role', 'group')\n\n // Get visible children for setsize\n const visibleChildren = node.children.filter(\n n => this.highlightQuery === '' || this.nodeMatchesSearch(n, this.highlightQuery)\n )\n\n let childPosInSet = 0\n for (const child of node.children) {\n if (this.highlightQuery !== '' && !this.nodeMatchesSearch(child, this.highlightQuery)) {\n continue\n }\n childPosInSet++\n childrenEl.appendChild(\n this.buildNodeElement(child, depth + 1, childPosInSet, visibleChildren.length)\n )\n }\n\n nodeEl.appendChild(childrenEl)\n }\n\n return nodeEl\n }\n\n /** Handle expand/collapse toggle, including lazy loading */\n private async handleToggle(node: TreeNode): Promise<void> {\n if (node.expanded === true) {\n node.expanded = false\n this.announceUpdate(`${node.label} collapsed`)\n } else {\n node.expanded = true\n if (\n node.lazyLoad !== undefined &&\n (node.children === undefined || node.children.length === 0)\n ) {\n this.announceUpdate(`Loading ${node.label}`)\n await loadChildren(node)\n }\n this.announceUpdate(`${node.label} expanded`)\n this.config.onExpand?.(node)\n }\n this.render()\n }\n\n /** Highlight matching text within a label */\n private highlightText(text: string, query: string): string {\n const lowerText = text.toLowerCase()\n const idx = lowerText.indexOf(query)\n if (idx < 0) {\n return escapeHtml(text)\n }\n\n const before = text.slice(0, idx)\n const match = text.slice(idx, idx + query.length)\n const after = text.slice(idx + query.length)\n\n return `${escapeHtml(before)}<mark class=\"cmm-tree-node__highlight\">${escapeHtml(match)}</mark>${escapeHtml(after)}`\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@countermeasure-platform/web-components",
|
|
3
|
-
"version": "1.2.2-dev.
|
|
3
|
+
"version": "1.2.2-dev.15.1",
|
|
4
4
|
"description": "Shared web components for CounterMeasure applications - consolidates common frontend functionality across projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -583,13 +583,11 @@ export class TreeView {
|
|
|
583
583
|
toggle.setAttribute('aria-hidden', 'true')
|
|
584
584
|
|
|
585
585
|
if (isExpandable) {
|
|
586
|
-
toggle.textContent = node.expanded === true ? '\u25BE' : '\u25B8'
|
|
587
586
|
toggle.addEventListener('click', (e: Event) => {
|
|
588
587
|
e.stopPropagation()
|
|
589
588
|
void this.handleToggle(node)
|
|
590
589
|
})
|
|
591
590
|
} else {
|
|
592
|
-
toggle.textContent = ' '
|
|
593
591
|
toggle.style.visibility = 'hidden'
|
|
594
592
|
}
|
|
595
593
|
|
package/src/sidebar/component.ts
CHANGED
|
@@ -33,8 +33,8 @@ function createIconSvg(iconSet: IconSet, name: string): SVGElement {
|
|
|
33
33
|
const svgNs = 'http://www.w3.org/2000/svg'
|
|
34
34
|
const svg = document.createElementNS(svgNs, 'svg')
|
|
35
35
|
svg.setAttribute('xmlns', svgNs)
|
|
36
|
-
svg.setAttribute('width', '
|
|
37
|
-
svg.setAttribute('height', '
|
|
36
|
+
svg.setAttribute('width', '16')
|
|
37
|
+
svg.setAttribute('height', '16')
|
|
38
38
|
svg.setAttribute('viewBox', '0 0 24 24')
|
|
39
39
|
svg.setAttribute('fill', 'none')
|
|
40
40
|
svg.setAttribute('stroke', 'currentColor')
|
package/src/styles/sidebar.css
CHANGED
|
@@ -458,8 +458,8 @@ button.sidebar__brand {
|
|
|
458
458
|
|
|
459
459
|
.sidebar__item-icon {
|
|
460
460
|
flex-shrink: 0;
|
|
461
|
-
width:
|
|
462
|
-
height:
|
|
461
|
+
width: 16px;
|
|
462
|
+
height: 16px;
|
|
463
463
|
color: var(--color-text-muted);
|
|
464
464
|
transition: color 0.2s;
|
|
465
465
|
}
|
|
@@ -1037,11 +1037,11 @@ button.sidebar__brand {
|
|
|
1037
1037
|
|
|
1038
1038
|
.sidebar--collapsed .sidebar__item {
|
|
1039
1039
|
justify-content: center;
|
|
1040
|
-
padding: 0;
|
|
1040
|
+
padding: 0.5rem;
|
|
1041
1041
|
border-left: none;
|
|
1042
|
-
width:
|
|
1043
|
-
height:
|
|
1044
|
-
border-radius:
|
|
1042
|
+
width: auto;
|
|
1043
|
+
height: auto;
|
|
1044
|
+
border-radius: 0.375rem;
|
|
1045
1045
|
margin: 0 auto;
|
|
1046
1046
|
}
|
|
1047
1047
|
|
|
@@ -1049,6 +1049,18 @@ button.sidebar__brand {
|
|
|
1049
1049
|
margin: 0;
|
|
1050
1050
|
}
|
|
1051
1051
|
|
|
1052
|
+
/* ============================================================================
|
|
1053
|
+
TOUCH DEVICE ENHANCEMENTS (WCAG 2.5.5 - 44px minimum)
|
|
1054
|
+
Desktop keeps compact 32px collapsed items; touch devices get a 44px
|
|
1055
|
+
minimum tap target without altering the visual icon size.
|
|
1056
|
+
============================================================================ */
|
|
1057
|
+
@media (pointer: coarse) {
|
|
1058
|
+
.sidebar--collapsed .sidebar__item {
|
|
1059
|
+
min-width: var(--cmm-touch-target-min, 44px);
|
|
1060
|
+
min-height: var(--cmm-touch-target-min, 44px);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1052
1064
|
.sidebar--collapsed .sidebar__section {
|
|
1053
1065
|
margin-top: 0.25rem;
|
|
1054
1066
|
}
|