UNPKG

@agnos-ui/core

Version:

Framework-agnostic headless component library.

227 lines (226 loc) 7.1 kB
"use strict"; const tansu = require("@amadeus-it-group/tansu"); const services_navManager = require("./services/navManager.cjs"); const utils_directive = require("./dom-CuBx1JPZ.cjs"); const utils_func = require("./utils/func.cjs"); const utils_stores = require("./utils/stores.cjs"); const utils_writables = require("./writables-Bn3uhKEG.cjs"); const utils_widget = require("./utils/widget.cjs"); function getTreeDefaultConfig() { return { ...defaultTreeConfig }; } const defaultTreeConfig = { className: "", nodes: [], onExpandToggle: utils_func.noop, navSelector: (node) => node.querySelectorAll("button"), ariaLabelToggleFn: (label) => `Toggle ${label}` }; const configValidator = { className: utils_writables.typeString, nodes: utils_writables.typeArray, onExpandToggle: utils_writables.typeFunction, navSelector: utils_writables.typeFunction, ariaLabelToggleFn: utils_writables.typeFunction }; const createTree = utils_widget.createWidgetFactory("tree", (config) => { const [{ nodes$, onExpandToggle$, navSelector$, ariaLabelToggleFn$, ...stateProps }, patch] = utils_stores.writablesForProps( defaultTreeConfig, config, configValidator ); const treeMap = /* @__PURE__ */ new Map(); const _expandedMap = { get(item) { return item.isExpanded; } }; const _toggleChange$ = tansu.writable({}); const expandedMap$ = tansu.computed(() => { normalizedNodes$(); _toggleChange$(); return _expandedMap; }); const { elementsInDomOrder$, directive: navDirective, refreshElements, focusIndex, focusPrevious, focusNext, focusFirst, focusLast } = services_navManager.createNavManager(); const navManagerConfig$ = tansu.computed(() => ({ keys: { ArrowUp: focusPrevious, ArrowDown: focusNext, Home: focusFirst, End: focusLast }, selector: navSelector$() })); const traverseTree = (node, level, parent) => { var _a; const copyNode = { ...node, ariaLabel: node.ariaLabel ?? node.label, level, children: [], isExpanded: ((_a = node.children) == null ? void 0 : _a.length) ? node.isExpanded ?? false : void 0 }; treeMap.set(copyNode, { parent }); if (node.children) { copyNode.children = node.children.map((child) => traverseTree(child, level + 1, copyNode)); } return copyNode; }; const normalizedNodes$ = tansu.computed(() => { treeMap.clear(); return nodes$().map((node) => traverseTree(node, 0, void 0)); }); const _lastFocusedTreeItem$ = tansu.writable(normalizedNodes$().find((node) => node.isExpanded !== void 0)); const getTreeItemInfo = (item) => { const treeItem = treeMap.get(item); if (!treeItem) { console.error(`Node ${item.label} doesn't exist in the map`); } return treeItem; }; const treeItemElementDirective = utils_directive.browserDirective( (toggleItem, args) => { let treeItemInfo; const destroy = () => { if (treeItemInfo && treeItemInfo.htmlElement === toggleItem) { treeItemInfo.htmlElement = void 0; } treeItemInfo = void 0; }; const update = (args2) => { destroy(); treeItemInfo = getTreeItemInfo(args2.item); if (treeItemInfo) { if (treeItemInfo.htmlElement) { console.warn(`The tree item directive should be used once per element`); } treeItemInfo.htmlElement = toggleItem; } }; update(args); return { update, destroy }; } ); const focusElementIfExists = (itemToFocus) => { var _a; if (itemToFocus) { const mapItemHtml = (_a = getTreeItemInfo(itemToFocus)) == null ? void 0 : _a.htmlElement; if (mapItemHtml) { const index = elementsInDomOrder$().indexOf(mapItemHtml); focusIndex(index, 0); } } }; const itemToggleAttributesDirective = utils_directive.createAttributesDirective((treeItemContext$) => ({ events: { focus: () => { const { item } = treeItemContext$(); _lastFocusedTreeItem$.set(item); }, click: () => { const { item } = treeItemContext$(); toggleExpanded(item); }, keydown: (event) => { var _a, _b; const { key } = event; const { item } = treeItemContext$(); const isExpanded = item.isExpanded; refreshElements(); switch (key) { case "Enter": case " ": toggleExpanded(item); break; case "ArrowLeft": if (isExpanded) { toggleExpanded(item); } else { focusElementIfExists((_a = getTreeItemInfo(item)) == null ? void 0 : _a.parent); } break; case "ArrowRight": if (!isExpanded) { toggleExpanded(item); } else { focusElementIfExists((_b = item.children) == null ? void 0 : _b[0]); } break; default: return; } event.preventDefault(); event.stopPropagation(); } }, attributes: { "aria-label": tansu.computed(() => { const { item } = treeItemContext$(); return ariaLabelToggleFn$()(item.ariaLabel); }), tabindex: tansu.computed(() => { const { item } = treeItemContext$(); return item === _lastFocusedTreeItem$() ? "0" : "-1"; }), type: tansu.readable("button") }, classNames: { "au-tree-expand-icon": utils_stores.true$, "au-tree-expand-icon-expanded": tansu.computed(() => { _toggleChange$(); const { item } = treeItemContext$(); return item.isExpanded ?? false; }) } })); const toggleExpanded = (node) => { const treeItemInfo = getTreeItemInfo(node); if (treeItemInfo === void 0 || node.isExpanded === void 0) { return; } node.isExpanded = !node.isExpanded; _toggleChange$.set({}); onExpandToggle$()(node); }; const widget = { ...utils_stores.stateStores({ normalizedNodes$, expandedMap$, ...stateProps }), patch, api: {}, directives: { navigationDirective: utils_directive.bindDirective(navDirective, navManagerConfig$), itemToggleDirective: utils_directive.mergeDirectives(treeItemElementDirective, itemToggleAttributesDirective), itemAttributesDirective: utils_directive.createAttributesDirective((treeItemContext$) => ({ attributes: { role: tansu.readable("treeitem"), "aria-selected": tansu.readable("false"), // TODO: adapt aria-selected to the actual selected state "aria-expanded": tansu.computed(() => { var _a; const { item } = treeItemContext$(); _toggleChange$(); return (_a = item.isExpanded) == null ? void 0 : _a.toString(); }) } })) } }; return widget; }); exports.createTree = createTree; exports.getTreeDefaultConfig = getTreeDefaultConfig;