@lifecorner/angular-ui-kit 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +2 -1
  2. package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs +4 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs.map +1 -1
  4. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs +16 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs.map +1 -1
  6. package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs +4 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs.map +1 -1
  8. package/fesm2022/lifecorner-angular-ui-kit-button.mjs +4 -4
  9. package/fesm2022/lifecorner-angular-ui-kit-button.mjs.map +1 -1
  10. package/fesm2022/lifecorner-angular-ui-kit-callout.mjs +16 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-callout.mjs.map +1 -1
  12. package/fesm2022/lifecorner-angular-ui-kit-chat.mjs +14 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-chat.mjs.map +1 -1
  14. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs +4 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs.map +1 -1
  16. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +4 -1
  17. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
  18. package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs +4 -1
  19. package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs.map +1 -1
  20. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs +4 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs.map +1 -1
  22. package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs +4 -1
  23. package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs.map +1 -1
  24. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs +14 -1
  25. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs.map +1 -1
  26. package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs +4 -1
  27. package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs.map +1 -1
  28. package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs +4 -0
  29. package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs.map +1 -1
  30. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +4 -1
  31. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
  32. package/fesm2022/lifecorner-angular-ui-kit-footer.mjs +4 -1
  33. package/fesm2022/lifecorner-angular-ui-kit-footer.mjs.map +1 -1
  34. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs +14 -1
  35. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs.map +1 -1
  36. package/fesm2022/lifecorner-angular-ui-kit-header.mjs +18 -1
  37. package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
  38. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs +186 -275
  39. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs.map +1 -1
  40. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs +803 -0
  41. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs.map +1 -0
  42. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs +297 -0
  43. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs.map +1 -0
  44. package/fesm2022/lifecorner-angular-ui-kit-internal-navigation.mjs +38 -0
  45. package/fesm2022/lifecorner-angular-ui-kit-internal-navigation.mjs.map +1 -0
  46. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +4 -1
  47. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
  48. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +4 -1
  49. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
  50. package/fesm2022/lifecorner-angular-ui-kit-modal.mjs +4 -1
  51. package/fesm2022/lifecorner-angular-ui-kit-modal.mjs.map +1 -1
  52. package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs +12 -1
  53. package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs.map +1 -1
  54. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +14 -1
  55. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
  56. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +4 -1
  57. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
  58. package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs +4 -1
  59. package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs.map +1 -1
  60. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs +12 -1
  61. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs.map +1 -1
  62. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs +4 -1
  63. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs.map +1 -1
  64. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs +4 -1
  65. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs.map +1 -1
  66. package/fesm2022/lifecorner-angular-ui-kit-sidenav-placeholder.mjs +262 -0
  67. package/fesm2022/lifecorner-angular-ui-kit-sidenav-placeholder.mjs.map +1 -0
  68. package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs +18 -40
  69. package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs.map +1 -1
  70. package/fesm2022/lifecorner-angular-ui-kit-table.mjs +4 -1
  71. package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
  72. package/fesm2022/lifecorner-angular-ui-kit-toast.mjs +16 -1
  73. package/fesm2022/lifecorner-angular-ui-kit-toast.mjs.map +1 -1
  74. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs +18 -1
  75. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs.map +1 -1
  76. package/fesm2022/lifecorner-angular-ui-kit.mjs +2 -0
  77. package/package.json +17 -1
  78. package/types/lifecorner-angular-ui-kit-icon.d.ts +125 -5
  79. package/types/lifecorner-angular-ui-kit-icons.d.ts +140 -0
  80. package/types/lifecorner-angular-ui-kit-internal-icon-set.d.ts +31 -0
  81. package/types/lifecorner-angular-ui-kit-internal-navigation.d.ts +28 -0
  82. package/types/lifecorner-angular-ui-kit-sidenav-placeholder.d.ts +163 -0
  83. package/types/lifecorner-angular-ui-kit-sidenav.d.ts +4 -2
  84. package/types/lifecorner-angular-ui-kit.d.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-tree-view.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/tree-view/file-icons.ts","../../../../libs/angular-ui-kit/src/lib/tree-view/tree-view.component.ts","../../../../libs/angular-ui-kit/src/lib/tree-view/tree-view.component.html"],"sourcesContent":["/**\n * File-type → icon mapping for the TreeView component.\n *\n * Icon names refer to Tabler Icons (the design system's icon source).\n * Resolution order for a file node:\n * 1. explicit `node.icon`\n * 2. exact file name match (e.g. `package.json`, `Dockerfile`)\n * 3. file extension match (e.g. `.ts`, `.png`)\n * 4. generic file fallback\n */\n\n/** Icon used for collapsed folders. */\nexport const FOLDER_ICON = 'folder';\n/** Icon used for expanded (open) folders. */\nexport const FOLDER_OPEN_ICON = 'folder-open';\n/** Generic fallback icon for files with no specific match. */\nexport const FILE_FALLBACK_ICON = 'file';\n\n/** Map of well-known file names (lower-cased) to icons. */\nconst FILE_NAME_ICONS: Record<string, string> = {\n 'package.json': 'brand-npm',\n 'package-lock.json': 'brand-npm',\n 'yarn.lock': 'brand-yarn',\n 'pnpm-lock.yaml': 'brand-pnpm',\n 'dockerfile': 'brand-docker',\n 'docker-compose.yml': 'brand-docker',\n 'docker-compose.yaml': 'brand-docker',\n '.gitignore': 'brand-git',\n '.gitattributes': 'brand-git',\n '.gitmodules': 'brand-git',\n 'readme.md': 'book',\n 'license': 'license',\n 'license.md': 'license',\n '.env': 'key',\n '.env.local': 'key',\n '.editorconfig': 'settings',\n '.prettierrc': 'settings',\n '.eslintrc': 'settings',\n 'tsconfig.json': 'brand-typescript',\n 'angular.json': 'brand-angular',\n 'nx.json': 'settings',\n 'makefile': 'terminal',\n};\n\n/** Map of file extensions (without dot, lower-cased) to icons. */\nconst FILE_EXT_ICONS: Record<string, string> = {\n // Languages\n ts: 'brand-typescript',\n tsx: 'brand-typescript',\n js: 'brand-javascript',\n jsx: 'brand-javascript',\n mjs: 'brand-javascript',\n cjs: 'brand-javascript',\n html: 'brand-html5',\n htm: 'brand-html5',\n css: 'brand-css3',\n scss: 'brand-sass',\n sass: 'brand-sass',\n less: 'brand-css3',\n py: 'brand-python',\n rb: 'diamond',\n go: 'brand-golang',\n rs: 'brand-rust',\n java: 'coffee',\n kt: 'brand-kotlin',\n swift: 'brand-swift',\n c: 'file-code',\n h: 'file-code',\n cpp: 'brand-cpp',\n cs: 'brand-c-sharp',\n php: 'brand-php',\n dart: 'brand-flutter',\n sh: 'terminal-2',\n bash: 'terminal-2',\n zsh: 'terminal-2',\n sql: 'database',\n // Data / config\n json: 'braces',\n jsonc: 'braces',\n yml: 'file-code',\n yaml: 'file-code',\n toml: 'file-code',\n xml: 'file-code',\n ini: 'settings',\n env: 'key',\n // Docs\n md: 'markdown',\n mdx: 'markdown',\n txt: 'file-text',\n pdf: 'file-type-pdf',\n doc: 'file-text',\n docx: 'file-text',\n csv: 'file-spreadsheet',\n xls: 'file-spreadsheet',\n xlsx: 'file-spreadsheet',\n // Images\n png: 'photo',\n jpg: 'photo',\n jpeg: 'photo',\n gif: 'photo',\n webp: 'photo',\n svg: 'file-vector',\n ico: 'photo',\n bmp: 'photo',\n // Media\n mp3: 'music',\n wav: 'music',\n mp4: 'video',\n mov: 'video',\n webm: 'video',\n // Archives\n zip: 'file-zip',\n tar: 'file-zip',\n gz: 'file-zip',\n rar: 'file-zip',\n '7z': 'file-zip',\n // Fonts\n woff: 'typography',\n woff2: 'typography',\n ttf: 'typography',\n otf: 'typography',\n // Misc\n lock: 'lock',\n};\n\n/**\n * Resolve the icon name for a file based on its name.\n * Performs exact-name then extension lookup, falling back to a generic file icon.\n */\nexport function resolveFileIcon(fileName: string): string {\n const lower = fileName.toLowerCase();\n\n const byName = FILE_NAME_ICONS[lower];\n if (byName) return byName;\n\n const dotIndex = lower.lastIndexOf('.');\n if (dotIndex > 0 && dotIndex < lower.length - 1) {\n const ext = lower.slice(dotIndex + 1);\n const byExt = FILE_EXT_ICONS[ext];\n if (byExt) return byExt;\n }\n\n return FILE_FALLBACK_ICON;\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n computed,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\nimport {\n FILE_FALLBACK_ICON,\n FOLDER_ICON,\n FOLDER_OPEN_ICON,\n resolveFileIcon,\n} from './file-icons';\n\n// ── Public types ─────────────────────────────────────────────────────────────\n\n/**\n * Node type. `'file'` and `'folder'` get the built-in file-tree treatment\n * (extension icons, folder open/close icons). Any other string marks a custom\n * domain type: such nodes render a colored type dot (or an explicit `icon`)\n * instead of file icons.\n */\nexport type TreeNodeType = 'file' | 'folder' | (string & {});\n\nexport type TreeNodeStatus =\n | 'default'\n | 'added'\n | 'modified'\n | 'removed'\n | 'muted'\n | 'success'\n | 'busy';\n\n/**\n * A node in the tree. Any node carrying `children` can expand — this covers\n * file trees (folders) as well as arbitrary domain hierarchies.\n */\nexport interface TreeNode {\n /** Display name, e.g. `app.component.ts` or `src`. */\n name: string;\n /**\n * Node type. Optional — inferred as `folder` when `children` is present,\n * otherwise `file`. May be any string for domain hierarchies.\n */\n type?: TreeNodeType;\n /**\n * Stable identifier. Optional — when omitted, the full path is used,\n * which is stable as long as names are unique among siblings.\n */\n id?: string;\n /** Child nodes. Any node with children can expand / collapse. */\n children?: TreeNode[];\n /**\n * Explicit Tabler icon name. Overrides automatic file-type / folder\n * icon resolution and the custom-type dot.\n */\n icon?: string;\n /**\n * Accent color for the node's icon / type dot. Any CSS color, including\n * `var(--…)` token references.\n */\n color?: string;\n /** Optional badge text shown to the right of the node (e.g. git status, count). */\n badge?: string;\n /**\n * Tone of the badge / node accent. `success` additionally renders a ✓\n * indicator, `busy` a pulsing dot (static under `prefers-reduced-motion`).\n */\n status?: TreeNodeStatus;\n /** Whether this node starts expanded. Ignored for nodes without children. */\n expanded?: boolean;\n /** Disable selection / interaction for this node. */\n disabled?: boolean;\n}\n\n// ── Internal flattened representation ─────────────────────────────────────────\n\ninterface FlatNode {\n id: string;\n name: string;\n type: TreeNodeType;\n depth: number;\n hasChildren: boolean;\n expanded: boolean;\n /** Resolved icon, or `null` for custom-type nodes that render a type dot. */\n icon: string | null;\n color?: string;\n badge?: string;\n status: TreeNodeStatus;\n disabled: boolean;\n /** Whether each ancestor level still has following siblings (for guide lines). */\n ancestorHasSibling: boolean[];\n /** Whether this node is the last among its siblings. */\n isLast: boolean;\n /** 1-based position among its siblings (aria-posinset). */\n posInSet: number;\n /** Number of siblings including itself (aria-setsize). */\n setSize: number;\n /** Id of the parent node, `null` for root nodes. */\n parentId: string | null;\n}\n\nconst BADGE_STATUS_COLOR: Record<TreeNodeStatus, string> = {\n default: 'var(--color-text-tertiary)',\n added: 'var(--color-success-default, #16a34a)',\n modified: 'var(--color-warning-default, #d97706)',\n removed: 'var(--color-error-default, #dc2626)',\n muted: 'var(--color-text-disabled)',\n success: 'var(--color-success-default, #16a34a)',\n busy: 'var(--color-primary-500, #6366f1)',\n};\n\nfunction nodeType(node: TreeNode): TreeNodeType {\n return node.type ?? (node.children ? 'folder' : 'file');\n}\n\nfunction isExpandable(node: TreeNode): boolean {\n return !!node.children?.length;\n}\n\n/**\n * Tree view component for visualizing hierarchies — file / folder trees as\n * well as arbitrary domain object trees with custom node types.\n *\n * Features:\n * - Recursive rendering from a single `nodes` input; any node with children\n * can expand / collapse\n * - Automatic file-type icons by extension and well-known file name,\n * with open / closed folder icons (for `file` / `folder` nodes)\n * - Custom node types: colored type dots or explicit icons via `type`,\n * `color`, `icon`\n * - Expand / collapse, with expand-all / collapse-all helpers\n * - Two-way bound selection and a `nodeClick` event\n * - Indentation guide lines for readability\n * - Optional per-node status badges (added / modified / removed) and status\n * indicators (`success` ✓, `busy` pulse)\n * - Keyboard accessible: roving tabindex (one tab stop), Arrow keys / Home /\n * End move focus, Enter / Space toggle or select, Left / Right collapse /\n * expand or move to parent / first child\n * - Dark / light theme support via design tokens\n *\n * @example\n * ```html\n * <lc-tree-view [nodes]=\"projectTree\" [(selectedId)]=\"selected\" />\n * ```\n */\n@Component({\n selector: 'lc-tree-view',\n standalone: true,\n imports: [IconComponent],\n templateUrl: './tree-view.component.html',\n styleUrls: ['./tree-view.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'lc-tree-view' },\n})\nexport class TreeViewComponent {\n /** The root-level nodes of the tree. */\n readonly nodes = input.required<TreeNode[]>();\n\n /** Id (or path) of the currently selected node (two-way bindable). */\n readonly selectedId = model<string | null>(null);\n\n /** Show indentation guide lines. */\n readonly showGuides = input(true);\n\n /** Show the expand-all / collapse-all toolbar. */\n readonly showToolbar = input(true);\n\n /** Label of the toolbar's expand-all button. */\n readonly expandAllLabel = input('Expand all');\n\n /** Label of the toolbar's collapse-all button. */\n readonly collapseAllLabel = input('Collapse all');\n\n /** Icon size for node icons. */\n readonly iconSize = input<'xs' | 'sm' | 'md'>('sm');\n\n /** Accessible name of a folder's expand toggle. */\n readonly expandAriaLabel = input('Expand');\n\n /** Accessible name of a folder's collapse toggle. */\n readonly collapseAriaLabel = input('Collapse');\n\n /** Emitted when a node is clicked / activated. */\n readonly nodeClick = output<TreeNode>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /** Set of node ids the user has explicitly expanded / collapsed. */\n private readonly expandOverrides = signal<Map<string, boolean>>(new Map());\n\n /** Id of the node that last had keyboard focus (roving tabindex). */\n private readonly focusedId = signal<string | null>(null);\n\n /** Flattened, render-ready list of visible nodes. */\n protected readonly visibleNodes = computed<FlatNode[]>(() => {\n const out: FlatNode[] = [];\n const overrides = this.expandOverrides();\n this.flatten(this.nodes(), 0, '', [], overrides, out, null);\n return out;\n });\n\n /**\n * The single tab stop of the tree: the last focused node if it is still\n * visible, else the selected node, else the first enabled node.\n */\n protected readonly tabStopId = computed<string | null>(() => {\n const nodes = this.visibleNodes();\n const focusable = (id: string | null) =>\n id !== null && nodes.some((n) => n.id === id && !n.disabled);\n const focused = this.focusedId();\n if (focusable(focused)) return focused;\n const selected = this.selectedId();\n if (focusable(selected)) return selected;\n return nodes.find((n) => !n.disabled)?.id ?? null;\n });\n\n protected badgeColor(status: TreeNodeStatus): string {\n return BADGE_STATUS_COLOR[status];\n }\n\n protected onNodeClick(flat: FlatNode): void {\n if (flat.disabled) return;\n if (flat.hasChildren) {\n this.toggle(flat);\n }\n this.selectedId.set(flat.id);\n const original = this.findNode(this.nodes(), '', flat.id);\n if (original) this.nodeClick.emit(original);\n }\n\n protected onToggleClick(flat: FlatNode, event: Event): void {\n event.stopPropagation();\n if (!flat.disabled) this.toggle(flat);\n }\n\n protected onFocus(flat: FlatNode): void {\n this.focusedId.set(flat.id);\n }\n\n protected onKeydown(flat: FlatNode, event: KeyboardEvent): void {\n if (flat.disabled) return;\n const nodes = this.visibleNodes();\n const index = nodes.findIndex((n) => n.id === flat.id);\n\n switch (event.key) {\n case 'Enter':\n case ' ':\n event.preventDefault();\n this.onNodeClick(flat);\n break;\n case 'ArrowRight':\n event.preventDefault();\n if (!flat.hasChildren) return;\n if (!flat.expanded) {\n this.setExpanded(flat.id, true);\n } else {\n // Expanded: move to the first child.\n this.focusIndex(index + 1);\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n if (flat.hasChildren && flat.expanded) {\n this.setExpanded(flat.id, false);\n } else if (flat.parentId !== null) {\n // Leaf or collapsed node: move to the parent.\n this.focusIndex(nodes.findIndex((n) => n.id === flat.parentId));\n }\n break;\n case 'ArrowDown':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, index, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, index, -1));\n break;\n case 'Home':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, -1, 1));\n break;\n case 'End':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, nodes.length, -1));\n break;\n }\n }\n\n /** Expand every folder in the tree. */\n expandAll(): void {\n this.setAll(true);\n }\n\n /** Collapse every folder in the tree. */\n collapseAll(): void {\n this.setAll(false);\n }\n\n protected trackById = (_: number, n: FlatNode): string => n.id;\n\n // ── internals ──────────────────────────────────────────────────────────────\n\n /** Index of the next enabled node from `from` in direction `dir`, or -1. */\n private nextEnabled(nodes: FlatNode[], from: number, dir: 1 | -1): number {\n for (let i = from + dir; i >= 0 && i < nodes.length; i += dir) {\n if (!nodes[i].disabled) return i;\n }\n return -1;\n }\n\n /**\n * Moves keyboard focus to the visible node at `index`. The rows are\n * rendered in `visibleNodes()` order, so the index maps 1:1 to the DOM.\n */\n private focusIndex(index: number): void {\n const nodes = this.visibleNodes();\n if (index < 0 || index >= nodes.length || nodes[index].disabled) return;\n this.focusedId.set(nodes[index].id);\n const items = this.host.nativeElement.querySelectorAll<HTMLElement>('.lc-tree-view__item');\n items[index]?.focus();\n }\n\n private toggle(flat: FlatNode): void {\n this.setExpanded(flat.id, !flat.expanded);\n }\n\n private setExpanded(id: string, value: boolean): void {\n this.expandOverrides.update((map) => {\n const next = new Map(map);\n next.set(id, value);\n return next;\n });\n }\n\n private setAll(value: boolean): void {\n const next = new Map<string, boolean>();\n const walk = (nodes: TreeNode[], parentPath: string): void => {\n for (const node of nodes) {\n const id = node.id ?? `${parentPath}/${node.name}`;\n if (node.children?.length) {\n next.set(id, value);\n walk(node.children, id);\n }\n }\n };\n walk(this.nodes(), '');\n this.expandOverrides.set(next);\n }\n\n private isExpanded(\n id: string,\n node: TreeNode,\n overrides: Map<string, boolean>,\n ): boolean {\n const override = overrides.get(id);\n if (override !== undefined) return override;\n return node.expanded ?? false;\n }\n\n private flatten(\n nodes: TreeNode[],\n depth: number,\n parentPath: string,\n ancestorHasSibling: boolean[],\n overrides: Map<string, boolean>,\n out: FlatNode[],\n parentId: string | null,\n ): void {\n nodes.forEach((node, index) => {\n const id = node.id ?? `${parentPath}/${node.name}`;\n const type = nodeType(node);\n const hasChildren = isExpandable(node);\n const expanded = hasChildren && this.isExpanded(id, node, overrides);\n const isLast = index === nodes.length - 1;\n\n out.push({\n id,\n name: node.name,\n type,\n depth,\n hasChildren,\n expanded,\n icon: this.resolveIcon(node, type, expanded),\n color: node.color,\n badge: node.badge,\n status: node.status ?? 'default',\n disabled: node.disabled ?? false,\n ancestorHasSibling,\n isLast,\n posInSet: index + 1,\n setSize: nodes.length,\n parentId,\n });\n\n if (expanded && node.children) {\n this.flatten(\n node.children,\n depth + 1,\n id,\n [...ancestorHasSibling, !isLast],\n overrides,\n out,\n id,\n );\n }\n });\n }\n\n private resolveIcon(\n node: TreeNode,\n type: TreeNodeType,\n expanded: boolean,\n ): string | null {\n if (node.icon) return node.icon;\n if (type === 'folder') return expanded ? FOLDER_OPEN_ICON : FOLDER_ICON;\n if (type === 'file') {\n return node.name ? resolveFileIcon(node.name) : FILE_FALLBACK_ICON;\n }\n // Custom domain type without explicit icon: render a colored type dot.\n return null;\n }\n\n private findNode(\n nodes: TreeNode[],\n parentPath: string,\n targetId: string,\n ): TreeNode | null {\n for (const node of nodes) {\n const id = node.id ?? `${parentPath}/${node.name}`;\n if (id === targetId) return node;\n if (node.children) {\n const found = this.findNode(node.children, id, targetId);\n if (found) return found;\n }\n }\n return null;\n }\n}\n","@if (showToolbar()) {\n <div class=\"lc-tree-view__toolbar\">\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"expandAll()\" [attr.title]=\"expandAllLabel()\">\n <lc-icon name=\"fold-down\" size=\"xs\" [decorative]=\"true\" />\n <span>{{ expandAllLabel() }}</span>\n </button>\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"collapseAll()\" [attr.title]=\"collapseAllLabel()\">\n <lc-icon name=\"fold-up\" size=\"xs\" [decorative]=\"true\" />\n <span>{{ collapseAllLabel() }}</span>\n </button>\n </div>\n}\n\n<ul class=\"lc-tree-view__list\" role=\"tree\">\n @for (node of visibleNodes(); track trackById($index, node)) {\n <li\n class=\"lc-tree-view__item\"\n [class.lc-tree-view__item--selected]=\"node.id === selectedId()\"\n [class.lc-tree-view__item--disabled]=\"node.disabled\"\n [class.lc-tree-view__item--folder]=\"node.type === 'folder'\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.depth + 1\"\n [attr.aria-posinset]=\"node.posInSet\"\n [attr.aria-setsize]=\"node.setSize\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"node.id === selectedId()\"\n [attr.aria-disabled]=\"node.disabled ? true : null\"\n [attr.aria-busy]=\"node.status === 'busy' ? true : null\"\n [tabindex]=\"node.id === tabStopId() ? 0 : -1\"\n (focus)=\"onFocus(node)\"\n (click)=\"onNodeClick(node)\"\n (keydown)=\"onKeydown(node, $event)\"\n >\n <!-- indentation + guide lines -->\n @for (hasSibling of node.ancestorHasSibling; track $index) {\n <span\n class=\"lc-tree-view__indent\"\n [class.lc-tree-view__indent--guide]=\"showGuides() && hasSibling\"\n ></span>\n }\n\n <!-- expand / collapse chevron -->\n <span class=\"lc-tree-view__chevron\">\n @if (node.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-tree-view__chevron-btn\"\n [class.lc-tree-view__chevron-btn--expanded]=\"node.expanded\"\n (click)=\"onToggleClick(node, $event)\"\n [attr.aria-label]=\"node.expanded ? collapseAriaLabel() : expandAriaLabel()\"\n tabindex=\"-1\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n\n <!-- type / file icon, or type dot for custom node types -->\n <span\n class=\"lc-tree-view__icon\"\n [style.color]=\"\n node.color ?? (node.status === 'default' ? null : badgeColor(node.status))\n \"\n >\n @if (node.icon; as icon) {\n <lc-icon [name]=\"icon\" [size]=\"iconSize()\" [decorative]=\"true\" />\n } @else {\n <span\n class=\"lc-tree-view__type-dot\"\n [style.background-color]=\"node.color ?? null\"\n ></span>\n }\n </span>\n\n <!-- label -->\n <span class=\"lc-tree-view__label\">{{ node.name }}</span>\n\n <!-- badge -->\n @if (node.badge) {\n <span\n class=\"lc-tree-view__badge\"\n [style.color]=\"badgeColor(node.status)\"\n [style.border-color]=\"badgeColor(node.status)\"\n >{{ node.badge }}</span\n >\n }\n\n <!-- status indicator -->\n @if (node.status === 'success') {\n <span class=\"lc-tree-view__status lc-tree-view__status--success\">\n <lc-icon name=\"check\" size=\"xs\" [decorative]=\"true\" />\n </span>\n } @else if (node.status === 'busy') {\n <span class=\"lc-tree-view__status lc-tree-view__status--busy\"></span>\n }\n </li>\n }\n</ul>\n"],"mappings":";;;AAYA,MAAa,cAAc;AAE3B,MAAa,mBAAmB;AAEhC,MAAa,qBAAqB;AAGlC,MAAM,kBAA0C;CAC9C,gBAAgB;CAChB,qBAAqB;CACrB,aAAa;CACb,kBAAkB;CAClB,cAAc;CACd,sBAAsB;CACtB,uBAAuB;CACvB,cAAc;CACd,kBAAkB;CAClB,eAAe;CACf,aAAa;CACb,WAAW;CACX,cAAc;CACd,QAAQ;CACR,cAAc;CACd,iBAAiB;CACjB,eAAe;CACf,aAAa;CACb,iBAAiB;CACjB,gBAAgB;CAChB,WAAW;CACX,YAAY;AACb;AAGD,MAAM,iBAAyC;CAE7C,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CACN,MAAM;CACN,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,IAAI;CACJ,OAAO;CACP,GAAG;CACH,GAAG;CACH,KAAK;CACL,IAAI;CACJ,KAAK;CACL,MAAM;CACN,IAAI;CACJ,MAAM;CACN,KAAK;CACL,KAAK;CAEL,MAAM;CACN,OAAO;CACP,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CAEL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CAEN,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CAEL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CAEN,KAAK;CACL,KAAK;CACL,IAAI;CACJ,KAAK;CACL,MAAM;CAEN,MAAM;CACN,OAAO;CACP,KAAK;CACL,KAAK;CAEL,MAAM;AACP;AAMD,SAAgB,gBAAgB,UAAgB;CAC9C,MAAM,QAAQ,SAAS,YAAW;CAElC,MAAM,SAAS,gBAAgB;CAC/B,IAAI,QAAQ,OAAO;CAEnB,MAAM,WAAW,MAAM,YAAY,GAAG;CACtC,IAAI,WAAW,KAAK,WAAW,MAAM,SAAS,GAAG;EAC/C,MAAM,MAAM,MAAM,MAAM,WAAW,CAAC;EACpC,MAAM,QAAQ,eAAe;EAC7B,IAAI,OAAO,OAAO;CACpB;CAEA,OAAO;AACT;ACpCA,MAAM,qBAAqD;CACzD,SAAS;CACT,OAAO;CACP,UAAU;CACV,SAAS;CACT,OAAO;CACP,SAAS;CACT,MAAM;AACP;AAED,SAAS,SAAS,MAAc;CAC9B,OAAO,KAAK,SAAS,KAAK,WAAW,WAAW;AAClD;AAEA,SAAS,aAAa,MAAc;CAClC,OAAO,CAAC,CAAC,KAAK,UAAU;AAC1B;AAqCA,IAAa,oBAAb,MAAa,kBAAiB;CAEnB,QAAQ,MAAM,SAAsB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGpC,aAAa,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGvC,aAAa,MAAM,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGxB,cAAc,MAAM,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGzB,iBAAiB,MAAM,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAGpC,mBAAmB,MAAM,gBAAe,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;CAGxC,WAAW,MAA0B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAG1C,kBAAkB,MAAM,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,oBAAoB,MAAM,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAGrC,YAAY,OAAM;CAEV,OAAO,OAAgC,UAAU;CAGjD,kBAAkB,uBAA6B,IAAI,IAAG,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAGzD,YAAY,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGrC,eAAe,eAA0B;EAC1D,MAAM,MAAkB,CAAA;EACxB,MAAM,YAAY,KAAK,gBAAe;EACtC,KAAK,QAAQ,KAAK,MAAK,GAAI,GAAG,IAAI,CAAA,GAAI,WAAW,KAAK,IAAI;EAC1D,OAAO;CACT,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAMiB,YAAY,eAA6B;EAC1D,MAAM,QAAQ,KAAK,aAAY;EAC/B,MAAM,aAAa,OACjB,OAAO,QAAQ,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM,CAAC,EAAE,QAAQ;EAC7D,MAAM,UAAU,KAAK,UAAS;EAC9B,IAAI,UAAU,OAAO,GAAG,OAAO;EAC/B,MAAM,WAAW,KAAK,WAAU;EAChC,IAAI,UAAU,QAAQ,GAAG,OAAO;EAChC,OAAO,MAAM,MAAM,MAAM,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM;CAC/C,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAEQ,WAAW,QAAsB;EACzC,OAAO,mBAAmB;CAC5B;CAEU,YAAY,MAAc;EAClC,IAAI,KAAK,UAAU;EACnB,IAAI,KAAK,aACP,KAAK,OAAO,IAAI;EAElB,KAAK,WAAW,IAAI,KAAK,EAAE;EAC3B,MAAM,WAAW,KAAK,SAAS,KAAK,MAAK,GAAI,IAAI,KAAK,EAAE;EACxD,IAAI,UAAU,KAAK,UAAU,KAAK,QAAQ;CAC5C;CAEU,cAAc,MAAgB,OAAY;EAClD,MAAM,gBAAe;EACrB,IAAI,CAAC,KAAK,UAAU,KAAK,OAAO,IAAI;CACtC;CAEU,QAAQ,MAAc;EAC9B,KAAK,UAAU,IAAI,KAAK,EAAE;CAC5B;CAEU,UAAU,MAAgB,OAAoB;EACtD,IAAI,KAAK,UAAU;EACnB,MAAM,QAAQ,KAAK,aAAY;EAC/B,MAAM,QAAQ,MAAM,WAAW,MAAM,EAAE,OAAO,KAAK,EAAE;EAErD,QAAQ,MAAM,KAAd;GACE,KAAK;GACL,KAAK;IACH,MAAM,eAAc;IACpB,KAAK,YAAY,IAAI;IACrB;GACF,KAAK;IACH,MAAM,eAAc;IACpB,IAAI,CAAC,KAAK,aAAa;IACvB,IAAI,CAAC,KAAK,UACR,KAAK,YAAY,KAAK,IAAI,IAAI;SAG9B,KAAK,WAAW,QAAQ,CAAC;IAE3B;GACF,KAAK;IACH,MAAM,eAAc;IACpB,IAAI,KAAK,eAAe,KAAK,UAC3B,KAAK,YAAY,KAAK,IAAI,KAAK;SAC1B,IAAI,KAAK,aAAa,MAE3B,KAAK,WAAW,MAAM,WAAW,MAAM,EAAE,OAAO,KAAK,QAAQ,CAAC;IAEhE;GACF,KAAK;IACH,MAAM,eAAc;IACpB,KAAK,WAAW,KAAK,YAAY,OAAO,OAAO,CAAC,CAAC;IACjD;GACF,KAAK;IACH,MAAM,eAAc;IACpB,KAAK,WAAW,KAAK,YAAY,OAAO,OAAO,EAAE,CAAC;IAClD;GACF,KAAK;IACH,MAAM,eAAc;IACpB,KAAK,WAAW,KAAK,YAAY,OAAO,IAAI,CAAC,CAAC;IAC9C;GACF,KAAK;IACH,MAAM,eAAc;IACpB,KAAK,WAAW,KAAK,YAAY,OAAO,MAAM,QAAQ,EAAE,CAAC;EAE7D;CACF;CAGA,YAAS;EACP,KAAK,OAAO,IAAI;CAClB;CAGA,cAAW;EACT,KAAK,OAAO,KAAK;CACnB;CAEU,aAAa,GAAW,MAAwB,EAAE;CAKpD,YAAY,OAAmB,MAAc,KAAW;EAC9D,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,KAAK,KACxD,IAAI,CAAC,MAAM,EAAE,CAAC,UAAU,OAAO;EAEjC,OAAO;CACT;CAMQ,WAAW,OAAa;EAC9B,MAAM,QAAQ,KAAK,aAAY;EAC/B,IAAI,QAAQ,KAAK,SAAS,MAAM,UAAU,MAAM,MAAM,CAAC,UAAU;EACjE,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,EAAE;EAElC,AADc,KAAK,KAAK,cAAc,iBAA8B,qBACpE,CAAA,CAAM,MAAM,EAAE,MAAK;CACrB;CAEQ,OAAO,MAAc;EAC3B,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,QAAQ;CAC1C;CAEQ,YAAY,IAAY,OAAc;EAC5C,KAAK,gBAAgB,QAAQ,QAAO;GAClC,MAAM,OAAO,IAAI,IAAI,GAAG;GACxB,KAAK,IAAI,IAAI,KAAK;GAClB,OAAO;EACT,CAAC;CACH;CAEQ,OAAO,OAAc;EAC3B,MAAM,uBAAO,IAAI,IAAG;EACpB,MAAM,QAAQ,OAAmB,eAA4B;GAC3D,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,KAAK,KAAK,MAAM,GAAG,WAAU,GAAI,KAAK;IAC5C,IAAI,KAAK,UAAU,QAAQ;KACzB,KAAK,IAAI,IAAI,KAAK;KAClB,KAAK,KAAK,UAAU,EAAE;IACxB;GACF;EACF;EACA,KAAK,KAAK,MAAK,GAAI,EAAE;EACrB,KAAK,gBAAgB,IAAI,IAAI;CAC/B;CAEQ,WACN,IACA,MACA,WAA+B;EAE/B,MAAM,WAAW,UAAU,IAAI,EAAE;EACjC,IAAI,aAAa,QAAW,OAAO;EACnC,OAAO,KAAK,YAAY;CAC1B;CAEQ,QACN,OACA,OACA,YACA,oBACA,WACA,KACA,UAAuB;EAEvB,MAAM,SAAS,MAAM,UAAS;GAC5B,MAAM,KAAK,KAAK,MAAM,GAAG,WAAU,GAAI,KAAK;GAC5C,MAAM,OAAO,SAAS,IAAI;GAC1B,MAAM,cAAc,aAAa,IAAI;GACrC,MAAM,WAAW,eAAe,KAAK,WAAW,IAAI,MAAM,SAAS;GACnE,MAAM,SAAS,UAAU,MAAM,SAAS;GAExC,IAAI,KAAK;IACP;IACA,MAAM,KAAK;IACX;IACA;IACA;IACA;IACA,MAAM,KAAK,YAAY,MAAM,MAAM,QAAQ;IAC3C,OAAO,KAAK;IACZ,OAAO,KAAK;IACZ,QAAQ,KAAK,UAAU;IACvB,UAAU,KAAK,YAAY;IAC3B;IACA;IACA,UAAU,QAAQ;IAClB,SAAS,MAAM;IACf;GACD,CAAA;GAED,IAAI,YAAY,KAAK,UACnB,KAAK,QACH,KAAK,UACL,QAAQ,GACR,IACA,CAAC,GAAG,oBAAoB,CAAC,MAAM,GAC/B,WACA,KACA,EAAE;EAGR,CAAC;CACH;CAEQ,YACN,MACA,MACA,UAAiB;EAEjB,IAAI,KAAK,MAAM,OAAO,KAAK;EAC3B,IAAI,SAAS,UAAU,OAAO,WAAW,mBAAmB;EAC5D,IAAI,SAAS,QACX,OAAO,KAAK,OAAO,gBAAgB,KAAK,IAAI,IAAI;EAGlD,OAAO;CACT;CAEQ,SACN,OACA,YACA,UAAgB;EAEhB,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,KAAK,KAAK,MAAM,GAAG,WAAU,GAAI,KAAK;GAC5C,IAAI,OAAO,UAAU,OAAO;GAC5B,IAAI,KAAK,UAAU;IACjB,MAAM,QAAQ,KAAK,SAAS,KAAK,UAAU,IAAI,QAAQ;IACvD,IAAI,OAAO,OAAO;GACpB;EACF;EACA,OAAO;CACT;CA1RW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAjB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAiB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,kBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,iBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,mBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,YAAA;GAAA,WAAA;EAAA;EAAA,MAAA,EAAA,gBAAA,eAAA;EAAA,UAAA;EAAA,UChK9B;EAkGA,QAAA,CAAA,29GAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MDwDY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAMZ,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAiB,YAAA,CAAA;EAT7B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAc,YACZ;GAAI,SACP,CAAC,aAAa;GAAC,iBAGP,wBAAwB;GAAM,MACzC,EAAE,OAAO,eAAc;GAAE,UAAA;GAAA,QAAA,CAAA,29GAAA;EAAA,CAAA"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-tree-view.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/tree-view/file-icons.ts","../../../../libs/angular-ui-kit/src/lib/tree-view/tree-view.component.ts","../../../../libs/angular-ui-kit/src/lib/tree-view/tree-view.component.html"],"sourcesContent":["/**\n * File-type → icon mapping for the TreeView component.\n *\n * Icon names refer to Tabler Icons (the design system's icon source).\n * Resolution order for a file node:\n * 1. explicit `node.icon`\n * 2. exact file name match (e.g. `package.json`, `Dockerfile`)\n * 3. file extension match (e.g. `.ts`, `.png`)\n * 4. generic file fallback\n */\n\n/** Icon used for collapsed folders. */\nexport const FOLDER_ICON = 'folder';\n/** Icon used for expanded (open) folders. */\nexport const FOLDER_OPEN_ICON = 'folder-open';\n/** Generic fallback icon for files with no specific match. */\nexport const FILE_FALLBACK_ICON = 'file';\n\n/** Map of well-known file names (lower-cased) to icons. */\nconst FILE_NAME_ICONS: Record<string, string> = {\n 'package.json': 'brand-npm',\n 'package-lock.json': 'brand-npm',\n 'yarn.lock': 'brand-yarn',\n 'pnpm-lock.yaml': 'brand-pnpm',\n 'dockerfile': 'brand-docker',\n 'docker-compose.yml': 'brand-docker',\n 'docker-compose.yaml': 'brand-docker',\n '.gitignore': 'brand-git',\n '.gitattributes': 'brand-git',\n '.gitmodules': 'brand-git',\n 'readme.md': 'book',\n 'license': 'license',\n 'license.md': 'license',\n '.env': 'key',\n '.env.local': 'key',\n '.editorconfig': 'settings',\n '.prettierrc': 'settings',\n '.eslintrc': 'settings',\n 'tsconfig.json': 'brand-typescript',\n 'angular.json': 'brand-angular',\n 'nx.json': 'settings',\n 'makefile': 'terminal',\n};\n\n/** Map of file extensions (without dot, lower-cased) to icons. */\nconst FILE_EXT_ICONS: Record<string, string> = {\n // Languages\n ts: 'brand-typescript',\n tsx: 'brand-typescript',\n js: 'brand-javascript',\n jsx: 'brand-javascript',\n mjs: 'brand-javascript',\n cjs: 'brand-javascript',\n html: 'brand-html5',\n htm: 'brand-html5',\n css: 'brand-css3',\n scss: 'brand-sass',\n sass: 'brand-sass',\n less: 'brand-css3',\n py: 'brand-python',\n rb: 'diamond',\n go: 'brand-golang',\n rs: 'brand-rust',\n java: 'coffee',\n kt: 'brand-kotlin',\n swift: 'brand-swift',\n c: 'file-code',\n h: 'file-code',\n cpp: 'brand-cpp',\n cs: 'brand-c-sharp',\n php: 'brand-php',\n dart: 'brand-flutter',\n sh: 'terminal-2',\n bash: 'terminal-2',\n zsh: 'terminal-2',\n sql: 'database',\n // Data / config\n json: 'braces',\n jsonc: 'braces',\n yml: 'file-code',\n yaml: 'file-code',\n toml: 'file-code',\n xml: 'file-code',\n ini: 'settings',\n env: 'key',\n // Docs\n md: 'markdown',\n mdx: 'markdown',\n txt: 'file-text',\n pdf: 'file-type-pdf',\n doc: 'file-text',\n docx: 'file-text',\n csv: 'file-spreadsheet',\n xls: 'file-spreadsheet',\n xlsx: 'file-spreadsheet',\n // Images\n png: 'photo',\n jpg: 'photo',\n jpeg: 'photo',\n gif: 'photo',\n webp: 'photo',\n svg: 'file-vector',\n ico: 'photo',\n bmp: 'photo',\n // Media\n mp3: 'music',\n wav: 'music',\n mp4: 'video',\n mov: 'video',\n webm: 'video',\n // Archives\n zip: 'file-zip',\n tar: 'file-zip',\n gz: 'file-zip',\n rar: 'file-zip',\n '7z': 'file-zip',\n // Fonts\n woff: 'typography',\n woff2: 'typography',\n ttf: 'typography',\n otf: 'typography',\n // Misc\n lock: 'lock',\n};\n\n/**\n * Resolve the icon name for a file based on its name.\n * Performs exact-name then extension lookup, falling back to a generic file icon.\n */\nexport function resolveFileIcon(fileName: string): string {\n const lower = fileName.toLowerCase();\n\n const byName = FILE_NAME_ICONS[lower];\n if (byName) return byName;\n\n const dotIndex = lower.lastIndexOf('.');\n if (dotIndex > 0 && dotIndex < lower.length - 1) {\n const ext = lower.slice(dotIndex + 1);\n const byExt = FILE_EXT_ICONS[ext];\n if (byExt) return byExt;\n }\n\n return FILE_FALLBACK_ICON;\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n computed,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport {\n ICON_CHECK,\n ICON_CHEVRON_RIGHT,\n ICON_FILE,\n ICON_FILE_TEXT,\n ICON_KEY,\n ICON_SETTINGS,\n} from '@lifecorner/angular-ui-kit/icons';\nimport {\n FILE_FALLBACK_ICON,\n FOLDER_ICON,\n FOLDER_OPEN_ICON,\n resolveFileIcon,\n} from './file-icons';\n\n// ── Public types ─────────────────────────────────────────────────────────────\n\n/**\n * Node type. `'file'` and `'folder'` get the built-in file-tree treatment\n * (extension icons, folder open/close icons). Any other string marks a custom\n * domain type: such nodes render a colored type dot (or an explicit `icon`)\n * instead of file icons.\n */\nexport type TreeNodeType = 'file' | 'folder' | (string & {});\n\nexport type TreeNodeStatus =\n | 'default'\n | 'added'\n | 'modified'\n | 'removed'\n | 'muted'\n | 'success'\n | 'busy';\n\n/**\n * A node in the tree. Any node carrying `children` can expand — this covers\n * file trees (folders) as well as arbitrary domain hierarchies.\n */\nexport interface TreeNode {\n /** Display name, e.g. `app.component.ts` or `src`. */\n name: string;\n /**\n * Node type. Optional — inferred as `folder` when `children` is present,\n * otherwise `file`. May be any string for domain hierarchies.\n */\n type?: TreeNodeType;\n /**\n * Stable identifier. Optional — when omitted, the full path is used,\n * which is stable as long as names are unique among siblings.\n */\n id?: string;\n /** Child nodes. Any node with children can expand / collapse. */\n children?: TreeNode[];\n /**\n * Explicit Tabler icon name. Overrides automatic file-type / folder\n * icon resolution and the custom-type dot.\n */\n icon?: string;\n /**\n * Accent color for the node's icon / type dot. Any CSS color, including\n * `var(--…)` token references.\n */\n color?: string;\n /** Optional badge text shown to the right of the node (e.g. git status, count). */\n badge?: string;\n /**\n * Tone of the badge / node accent. `success` additionally renders a ✓\n * indicator, `busy` a pulsing dot (static under `prefers-reduced-motion`).\n */\n status?: TreeNodeStatus;\n /** Whether this node starts expanded. Ignored for nodes without children. */\n expanded?: boolean;\n /** Disable selection / interaction for this node. */\n disabled?: boolean;\n}\n\n// ── Internal flattened representation ─────────────────────────────────────────\n\ninterface FlatNode {\n id: string;\n name: string;\n type: TreeNodeType;\n depth: number;\n hasChildren: boolean;\n expanded: boolean;\n /** Resolved icon, or `null` for custom-type nodes that render a type dot. */\n icon: string | null;\n color?: string;\n badge?: string;\n status: TreeNodeStatus;\n disabled: boolean;\n /** Whether each ancestor level still has following siblings (for guide lines). */\n ancestorHasSibling: boolean[];\n /** Whether this node is the last among its siblings. */\n isLast: boolean;\n /** 1-based position among its siblings (aria-posinset). */\n posInSet: number;\n /** Number of siblings including itself (aria-setsize). */\n setSize: number;\n /** Id of the parent node, `null` for root nodes. */\n parentId: string | null;\n}\n\nconst BADGE_STATUS_COLOR: Record<TreeNodeStatus, string> = {\n default: 'var(--color-text-tertiary)',\n added: 'var(--color-success-default, #16a34a)',\n modified: 'var(--color-warning-default, #d97706)',\n removed: 'var(--color-error-default, #dc2626)',\n muted: 'var(--color-text-disabled)',\n success: 'var(--color-success-default, #16a34a)',\n busy: 'var(--color-primary-500, #6366f1)',\n};\n\nfunction nodeType(node: TreeNode): TreeNodeType {\n return node.type ?? (node.children ? 'folder' : 'file');\n}\n\nfunction isExpandable(node: TreeNode): boolean {\n return !!node.children?.length;\n}\n\n/**\n * Tree view component for visualizing hierarchies — file / folder trees as\n * well as arbitrary domain object trees with custom node types.\n *\n * Features:\n * - Recursive rendering from a single `nodes` input; any node with children\n * can expand / collapse\n * - Automatic file-type icons by extension and well-known file name,\n * with open / closed folder icons (for `file` / `folder` nodes)\n * - Custom node types: colored type dots or explicit icons via `type`,\n * `color`, `icon`\n * - Expand / collapse, with expand-all / collapse-all helpers\n * - Two-way bound selection and a `nodeClick` event\n * - Indentation guide lines for readability\n * - Optional per-node status badges (added / modified / removed) and status\n * indicators (`success` ✓, `busy` pulse)\n * - Keyboard accessible: roving tabindex (one tab stop), Arrow keys / Home /\n * End move focus, Enter / Space toggle or select, Left / Right collapse /\n * expand or move to parent / first child\n * - Dark / light theme support via design tokens\n *\n * @example\n * ```html\n * <lc-tree-view [nodes]=\"projectTree\" [(selectedId)]=\"selected\" />\n * ```\n */\n@Component({\n selector: 'lc-tree-view',\n standalone: true,\n imports: [IconComponent],\n // Its own glyphs and the inline ones of the built-in file-type map (file-icons.ts).\n viewProviders: [ɵprovideKitIcons([ICON_CHEVRON_RIGHT, ICON_CHECK, ICON_FILE, ICON_FILE_TEXT, ICON_KEY, ICON_SETTINGS])],\n templateUrl: './tree-view.component.html',\n styleUrls: ['./tree-view.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'lc-tree-view' },\n})\nexport class TreeViewComponent {\n /** The root-level nodes of the tree. */\n readonly nodes = input.required<TreeNode[]>();\n\n /** Id (or path) of the currently selected node (two-way bindable). */\n readonly selectedId = model<string | null>(null);\n\n /** Show indentation guide lines. */\n readonly showGuides = input(true);\n\n /** Show the expand-all / collapse-all toolbar. */\n readonly showToolbar = input(true);\n\n /** Label of the toolbar's expand-all button. */\n readonly expandAllLabel = input('Expand all');\n\n /** Label of the toolbar's collapse-all button. */\n readonly collapseAllLabel = input('Collapse all');\n\n /** Icon size for node icons. */\n readonly iconSize = input<'xs' | 'sm' | 'md'>('sm');\n\n /** Accessible name of a folder's expand toggle. */\n readonly expandAriaLabel = input('Expand');\n\n /** Accessible name of a folder's collapse toggle. */\n readonly collapseAriaLabel = input('Collapse');\n\n /** Emitted when a node is clicked / activated. */\n readonly nodeClick = output<TreeNode>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /** Set of node ids the user has explicitly expanded / collapsed. */\n private readonly expandOverrides = signal<Map<string, boolean>>(new Map());\n\n /** Id of the node that last had keyboard focus (roving tabindex). */\n private readonly focusedId = signal<string | null>(null);\n\n /** Flattened, render-ready list of visible nodes. */\n protected readonly visibleNodes = computed<FlatNode[]>(() => {\n const out: FlatNode[] = [];\n const overrides = this.expandOverrides();\n this.flatten(this.nodes(), 0, '', [], overrides, out, null);\n return out;\n });\n\n /**\n * The single tab stop of the tree: the last focused node if it is still\n * visible, else the selected node, else the first enabled node.\n */\n protected readonly tabStopId = computed<string | null>(() => {\n const nodes = this.visibleNodes();\n const focusable = (id: string | null) =>\n id !== null && nodes.some((n) => n.id === id && !n.disabled);\n const focused = this.focusedId();\n if (focusable(focused)) return focused;\n const selected = this.selectedId();\n if (focusable(selected)) return selected;\n return nodes.find((n) => !n.disabled)?.id ?? null;\n });\n\n protected badgeColor(status: TreeNodeStatus): string {\n return BADGE_STATUS_COLOR[status];\n }\n\n protected onNodeClick(flat: FlatNode): void {\n if (flat.disabled) return;\n if (flat.hasChildren) {\n this.toggle(flat);\n }\n this.selectedId.set(flat.id);\n const original = this.findNode(this.nodes(), '', flat.id);\n if (original) this.nodeClick.emit(original);\n }\n\n protected onToggleClick(flat: FlatNode, event: Event): void {\n event.stopPropagation();\n if (!flat.disabled) this.toggle(flat);\n }\n\n protected onFocus(flat: FlatNode): void {\n this.focusedId.set(flat.id);\n }\n\n protected onKeydown(flat: FlatNode, event: KeyboardEvent): void {\n if (flat.disabled) return;\n const nodes = this.visibleNodes();\n const index = nodes.findIndex((n) => n.id === flat.id);\n\n switch (event.key) {\n case 'Enter':\n case ' ':\n event.preventDefault();\n this.onNodeClick(flat);\n break;\n case 'ArrowRight':\n event.preventDefault();\n if (!flat.hasChildren) return;\n if (!flat.expanded) {\n this.setExpanded(flat.id, true);\n } else {\n // Expanded: move to the first child.\n this.focusIndex(index + 1);\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n if (flat.hasChildren && flat.expanded) {\n this.setExpanded(flat.id, false);\n } else if (flat.parentId !== null) {\n // Leaf or collapsed node: move to the parent.\n this.focusIndex(nodes.findIndex((n) => n.id === flat.parentId));\n }\n break;\n case 'ArrowDown':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, index, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, index, -1));\n break;\n case 'Home':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, -1, 1));\n break;\n case 'End':\n event.preventDefault();\n this.focusIndex(this.nextEnabled(nodes, nodes.length, -1));\n break;\n }\n }\n\n /** Expand every folder in the tree. */\n expandAll(): void {\n this.setAll(true);\n }\n\n /** Collapse every folder in the tree. */\n collapseAll(): void {\n this.setAll(false);\n }\n\n protected trackById = (_: number, n: FlatNode): string => n.id;\n\n // ── internals ──────────────────────────────────────────────────────────────\n\n /** Index of the next enabled node from `from` in direction `dir`, or -1. */\n private nextEnabled(nodes: FlatNode[], from: number, dir: 1 | -1): number {\n for (let i = from + dir; i >= 0 && i < nodes.length; i += dir) {\n if (!nodes[i].disabled) return i;\n }\n return -1;\n }\n\n /**\n * Moves keyboard focus to the visible node at `index`. The rows are\n * rendered in `visibleNodes()` order, so the index maps 1:1 to the DOM.\n */\n private focusIndex(index: number): void {\n const nodes = this.visibleNodes();\n if (index < 0 || index >= nodes.length || nodes[index].disabled) return;\n this.focusedId.set(nodes[index].id);\n const items = this.host.nativeElement.querySelectorAll<HTMLElement>('.lc-tree-view__item');\n items[index]?.focus();\n }\n\n private toggle(flat: FlatNode): void {\n this.setExpanded(flat.id, !flat.expanded);\n }\n\n private setExpanded(id: string, value: boolean): void {\n this.expandOverrides.update((map) => {\n const next = new Map(map);\n next.set(id, value);\n return next;\n });\n }\n\n private setAll(value: boolean): void {\n const next = new Map<string, boolean>();\n const walk = (nodes: TreeNode[], parentPath: string): void => {\n for (const node of nodes) {\n const id = node.id ?? `${parentPath}/${node.name}`;\n if (node.children?.length) {\n next.set(id, value);\n walk(node.children, id);\n }\n }\n };\n walk(this.nodes(), '');\n this.expandOverrides.set(next);\n }\n\n private isExpanded(\n id: string,\n node: TreeNode,\n overrides: Map<string, boolean>,\n ): boolean {\n const override = overrides.get(id);\n if (override !== undefined) return override;\n return node.expanded ?? false;\n }\n\n private flatten(\n nodes: TreeNode[],\n depth: number,\n parentPath: string,\n ancestorHasSibling: boolean[],\n overrides: Map<string, boolean>,\n out: FlatNode[],\n parentId: string | null,\n ): void {\n nodes.forEach((node, index) => {\n const id = node.id ?? `${parentPath}/${node.name}`;\n const type = nodeType(node);\n const hasChildren = isExpandable(node);\n const expanded = hasChildren && this.isExpanded(id, node, overrides);\n const isLast = index === nodes.length - 1;\n\n out.push({\n id,\n name: node.name,\n type,\n depth,\n hasChildren,\n expanded,\n icon: this.resolveIcon(node, type, expanded),\n color: node.color,\n badge: node.badge,\n status: node.status ?? 'default',\n disabled: node.disabled ?? false,\n ancestorHasSibling,\n isLast,\n posInSet: index + 1,\n setSize: nodes.length,\n parentId,\n });\n\n if (expanded && node.children) {\n this.flatten(\n node.children,\n depth + 1,\n id,\n [...ancestorHasSibling, !isLast],\n overrides,\n out,\n id,\n );\n }\n });\n }\n\n private resolveIcon(\n node: TreeNode,\n type: TreeNodeType,\n expanded: boolean,\n ): string | null {\n if (node.icon) return node.icon;\n if (type === 'folder') return expanded ? FOLDER_OPEN_ICON : FOLDER_ICON;\n if (type === 'file') {\n return node.name ? resolveFileIcon(node.name) : FILE_FALLBACK_ICON;\n }\n // Custom domain type without explicit icon: render a colored type dot.\n return null;\n }\n\n private findNode(\n nodes: TreeNode[],\n parentPath: string,\n targetId: string,\n ): TreeNode | null {\n for (const node of nodes) {\n const id = node.id ?? `${parentPath}/${node.name}`;\n if (id === targetId) return node;\n if (node.children) {\n const found = this.findNode(node.children, id, targetId);\n if (found) return found;\n }\n }\n return null;\n }\n}\n","@if (showToolbar()) {\n <div class=\"lc-tree-view__toolbar\">\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"expandAll()\" [attr.title]=\"expandAllLabel()\">\n <lc-icon name=\"fold-down\" size=\"xs\" [decorative]=\"true\" />\n <span>{{ expandAllLabel() }}</span>\n </button>\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"collapseAll()\" [attr.title]=\"collapseAllLabel()\">\n <lc-icon name=\"fold-up\" size=\"xs\" [decorative]=\"true\" />\n <span>{{ collapseAllLabel() }}</span>\n </button>\n </div>\n}\n\n<ul class=\"lc-tree-view__list\" role=\"tree\">\n @for (node of visibleNodes(); track trackById($index, node)) {\n <li\n class=\"lc-tree-view__item\"\n [class.lc-tree-view__item--selected]=\"node.id === selectedId()\"\n [class.lc-tree-view__item--disabled]=\"node.disabled\"\n [class.lc-tree-view__item--folder]=\"node.type === 'folder'\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.depth + 1\"\n [attr.aria-posinset]=\"node.posInSet\"\n [attr.aria-setsize]=\"node.setSize\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"node.id === selectedId()\"\n [attr.aria-disabled]=\"node.disabled ? true : null\"\n [attr.aria-busy]=\"node.status === 'busy' ? true : null\"\n [tabindex]=\"node.id === tabStopId() ? 0 : -1\"\n (focus)=\"onFocus(node)\"\n (click)=\"onNodeClick(node)\"\n (keydown)=\"onKeydown(node, $event)\"\n >\n <!-- indentation + guide lines -->\n @for (hasSibling of node.ancestorHasSibling; track $index) {\n <span\n class=\"lc-tree-view__indent\"\n [class.lc-tree-view__indent--guide]=\"showGuides() && hasSibling\"\n ></span>\n }\n\n <!-- expand / collapse chevron -->\n <span class=\"lc-tree-view__chevron\">\n @if (node.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-tree-view__chevron-btn\"\n [class.lc-tree-view__chevron-btn--expanded]=\"node.expanded\"\n (click)=\"onToggleClick(node, $event)\"\n [attr.aria-label]=\"node.expanded ? collapseAriaLabel() : expandAriaLabel()\"\n tabindex=\"-1\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n\n <!-- type / file icon, or type dot for custom node types -->\n <span\n class=\"lc-tree-view__icon\"\n [style.color]=\"\n node.color ?? (node.status === 'default' ? null : badgeColor(node.status))\n \"\n >\n @if (node.icon; as icon) {\n <lc-icon [name]=\"icon\" [size]=\"iconSize()\" [decorative]=\"true\" />\n } @else {\n <span\n class=\"lc-tree-view__type-dot\"\n [style.background-color]=\"node.color ?? null\"\n ></span>\n }\n </span>\n\n <!-- label -->\n <span class=\"lc-tree-view__label\">{{ node.name }}</span>\n\n <!-- badge -->\n @if (node.badge) {\n <span\n class=\"lc-tree-view__badge\"\n [style.color]=\"badgeColor(node.status)\"\n [style.border-color]=\"badgeColor(node.status)\"\n >{{ node.badge }}</span\n >\n }\n\n <!-- status indicator -->\n @if (node.status === 'success') {\n <span class=\"lc-tree-view__status lc-tree-view__status--success\">\n <lc-icon name=\"check\" size=\"xs\" [decorative]=\"true\" />\n </span>\n } @else if (node.status === 'busy') {\n <span class=\"lc-tree-view__status lc-tree-view__status--busy\"></span>\n }\n </li>\n }\n</ul>\n"],"mappings":"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"}
@@ -7,6 +7,7 @@ export * from "@lifecorner/angular-ui-kit/card";
7
7
  export * from "@lifecorner/angular-ui-kit/checkbox";
8
8
  export * from "@lifecorner/angular-ui-kit/icon";
9
9
  export * from "@lifecorner/angular-ui-kit/icon-names";
10
+ export * from "@lifecorner/angular-ui-kit/icons";
10
11
  export * from "@lifecorner/angular-ui-kit/logo";
11
12
  export * from "@lifecorner/angular-ui-kit/menu";
12
13
  export * from "@lifecorner/angular-ui-kit/input";
@@ -44,6 +45,7 @@ export * from "@lifecorner/angular-ui-kit/breadcrumbs";
44
45
  export * from "@lifecorner/angular-ui-kit/header";
45
46
  export * from "@lifecorner/angular-ui-kit/pagination";
46
47
  export * from "@lifecorner/angular-ui-kit/sidenav";
48
+ export * from "@lifecorner/angular-ui-kit/sidenav-placeholder";
47
49
  export * from "@lifecorner/angular-ui-kit/tabs";
48
50
  export * from "@lifecorner/angular-ui-kit/bottom-nav";
49
51
  export * from "@lifecorner/angular-ui-kit/app-shell";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifecorner/angular-ui-kit",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "LifeCorner Design System – Angular UI component library",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -247,6 +247,10 @@
247
247
  "types": "./types/lifecorner-angular-ui-kit-icon-names.d.ts",
248
248
  "default": "./fesm2022/lifecorner-angular-ui-kit-icon-names.mjs"
249
249
  },
250
+ "./icons": {
251
+ "types": "./types/lifecorner-angular-ui-kit-icons.d.ts",
252
+ "default": "./fesm2022/lifecorner-angular-ui-kit-icons.mjs"
253
+ },
250
254
  "./input": {
251
255
  "types": "./types/lifecorner-angular-ui-kit-input.d.ts",
252
256
  "default": "./fesm2022/lifecorner-angular-ui-kit-input.mjs"
@@ -255,6 +259,14 @@
255
259
  "types": "./types/lifecorner-angular-ui-kit-internal-chart.d.ts",
256
260
  "default": "./fesm2022/lifecorner-angular-ui-kit-internal-chart.mjs"
257
261
  },
262
+ "./internal/icon-set": {
263
+ "types": "./types/lifecorner-angular-ui-kit-internal-icon-set.d.ts",
264
+ "default": "./fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs"
265
+ },
266
+ "./internal/navigation": {
267
+ "types": "./types/lifecorner-angular-ui-kit-internal-navigation.d.ts",
268
+ "default": "./fesm2022/lifecorner-angular-ui-kit-internal-navigation.mjs"
269
+ },
258
270
  "./internal/overlay": {
259
271
  "types": "./types/lifecorner-angular-ui-kit-internal-overlay.d.ts",
260
272
  "default": "./fesm2022/lifecorner-angular-ui-kit-internal-overlay.mjs"
@@ -387,6 +399,10 @@
387
399
  "types": "./types/lifecorner-angular-ui-kit-sidenav.d.ts",
388
400
  "default": "./fesm2022/lifecorner-angular-ui-kit-sidenav.mjs"
389
401
  },
402
+ "./sidenav-placeholder": {
403
+ "types": "./types/lifecorner-angular-ui-kit-sidenav-placeholder.d.ts",
404
+ "default": "./fesm2022/lifecorner-angular-ui-kit-sidenav-placeholder.mjs"
405
+ },
390
406
  "./skeleton": {
391
407
  "types": "./types/lifecorner-angular-ui-kit-skeleton.d.ts",
392
408
  "default": "./fesm2022/lifecorner-angular-ui-kit-skeleton.mjs"
@@ -1,4 +1,5 @@
1
1
  import * as i0 from "@angular/core";
2
+ import { InjectionToken, Provider } from "@angular/core";
2
3
  export type IconVariant = 'outline' | 'solid';
3
4
  export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
4
5
  /**
@@ -26,6 +27,14 @@ export declare function resetIconCache(): void;
26
27
  * inlined at build time, see {@link isInlineIconName}): no request, they
27
28
  * render offline and in prerendered HTML; looked up by the name as written,
28
29
  * then by its alias target
30
+ * - Tree-shakable glyphs (3.6, AS-17): register the ones an app renders with
31
+ * `provideLcIcons([ICON_HOME, …])` (constants from
32
+ * `@lifecorner/angular-ui-kit/icons`); only those, and the glyphs the kit's
33
+ * own components render, are bundled. A registered glyph renders
34
+ * synchronously. An inline name that nobody registered loads the kit's whole
35
+ * set once, lazily (`unregistered: 'load'`, the default: the 3.5.1 output,
36
+ * prerendered HTML unchanged), or shows the placeholder and warns
37
+ * (`unregistered: 'placeholder'`)
29
38
  * - Dynamic SVG loading from Tabler Icons for everything else
30
39
  * - Fail-loud on unknown names: a console warning (once per name, in dev AND
31
40
  * prod) + a visible placeholder (never a silent empty space). Development
@@ -90,7 +99,13 @@ export declare class IconComponent {
90
99
  * empty — the client takes over on hydration.
91
100
  */
92
101
  private readonly isBrowser;
102
+ /** The registered glyphs above this icon (3.6, AS-17): kit components' own, then the app's. */
103
+ private readonly scope;
104
+ /** Keeps the server (and `whenStable()`) waiting while the kit's set loads. */
105
+ private readonly pendingTasks;
93
106
  private readonly container;
107
+ /** Whether the container was written once; until then a pending icon leaves a prerendered `<svg>` alone. */
108
+ private rendered;
94
109
  /**
95
110
  * Icon name from Tabler Icons library
96
111
  * @example "user", "check", "arrow-right"
@@ -134,7 +149,9 @@ export declare class IconComponent {
134
149
  * Strict mode. When `true` and the name is not a valid icon (see
135
150
  * {@link isValidIconName}), the component **throws** in development mode
136
151
  * instead of only warning — useful as a CI guard to catch typos/unknown
137
- * names. No effect in production builds.
152
+ * names. With `provideLcIcons(…, { unregistered: 'placeholder' })` (3.6), a
153
+ * kit glyph that is not registered also throws in development. No effect in
154
+ * production builds.
138
155
  * @default false
139
156
  */
140
157
  readonly strict: import("@angular/core").InputSignal<boolean>;
@@ -170,6 +187,14 @@ export declare class IconComponent {
170
187
  */
171
188
  readonly colorStyle: import("@angular/core").Signal<string>;
172
189
  constructor();
190
+ /**
191
+ * A 3.5.1 inline name that no scope registered (3.6, AS-17). `'load'`
192
+ * renders it from the kit's lazily loaded set: synchronously once the set
193
+ * is there, otherwise after the chunk, with the server and `whenStable()`
194
+ * waiting through `PendingTasks`. `'placeholder'` never loads the set.
195
+ */
196
+ private resolveUnregistered;
197
+ private showFromSet;
173
198
  /**
174
199
  * A known name whose asset can't be loaded stays quiet in development (as
175
200
  * before: unit tests and offline work have no assets). Production can't
@@ -243,11 +268,12 @@ export declare const ICON_ALIASES: Readonly<Record<string, string>>;
243
268
  export type IconName = string;
244
269
  /**
245
270
  * Whether `name` renders from markup shipped in the kit — no request, so it
246
- * shows offline, at once and in prerendered HTML (3.3, gap G-7). Covers the
271
+ * shows offline and in prerendered HTML (3.3, gap G-7): registered with
272
+ * `provideLcIcons()` or loaded with the kit's set (3.6, AS-17). Covers the
247
273
  * hand-picked inline set, the Tabler icons inlined at build time (mail,
248
274
  * users, settings, logout, key, chevron-right, …) and aliases that resolve
249
- * to either. Use it to lint an app or a static site against icons that would
250
- * be fetched.
275
+ * to either — the same names as in 3.5.1. Use it to lint an app or a static
276
+ * site against icons that would be fetched.
251
277
  *
252
278
  * @example
253
279
  * ```ts
@@ -257,4 +283,98 @@ export type IconName = string;
257
283
  * isInlineIconName('robot', 'solid'); // false (Tabler has no filled robot)
258
284
  * ```
259
285
  */
260
- export declare function isInlineIconName(name: string, variant?: IconVariant): boolean;
286
+ export declare function isInlineIconName(name: string, variant?: IconVariant): boolean;
287
+ /**
288
+ * One glyph shipped by the kit (3.6, AS-17). Import the constants from
289
+ * `@lifecorner/angular-ui-kit/icons` (`ICON_HOME`, `ICON_X_MARK_SOLID`, …) and
290
+ * register them with {@link provideLcIcons}. Markup you write yourself is not
291
+ * supported: it has to pass the same allow-list as fetched icons, or it
292
+ * renders the placeholder.
293
+ */
294
+ interface LcIconDefinition {
295
+ /** The 3.5.1 key of the glyph, e.g. `x-mark` or `mail` (aliases resolve to it). */
296
+ readonly name: string;
297
+ /** The variant the glyph draws. */
298
+ readonly variant: IconVariant;
299
+ /** The SVG markup. */
300
+ readonly svg: string;
301
+ }
302
+ /**
303
+ * What a kit glyph does that nobody registered (see {@link LcIconOptions}):
304
+ * `'load'` loads the kit's whole set once, as a lazy chunk, and renders it;
305
+ * `'placeholder'` shows the placeholder and warns, so the set never loads.
306
+ */
307
+ type LcIconFallback = 'load' | 'placeholder';
308
+ /** App-wide `lc-icon` options. Set them once, in `app.config`. */
309
+ interface LcIconOptions {
310
+ /**
311
+ * What a kit glyph does that nobody registered: `'load'` loads the kit's
312
+ * whole set once, as a lazy chunk, and renders it (the 3.5.1 output, a
313
+ * moment later in a client-only render; prerendered HTML is unchanged);
314
+ * `'placeholder'` shows the placeholder and warns once per name (in
315
+ * production too), so the set never loads. With `'placeholder'`,
316
+ * `<lc-icon [strict]="true">` also throws in development. For
317
+ * `unregistered`, the nearest `provideLcIcons` call that sets it wins.
318
+ * @default 'load'
319
+ */
320
+ readonly unregistered?: LcIconFallback;
321
+ }
322
+ /**
323
+ * The glyphs one injector registered, linked to the scope above it.
324
+ * @internal
325
+ */
326
+ interface IconScope {
327
+ readonly parent: IconScope | null;
328
+ /** Markup by `variant:name`. */
329
+ readonly glyphs: ReadonlyMap<string, string>;
330
+ /** Set by an app's {@link provideLcIcons} call only. */
331
+ readonly unregistered: LcIconFallback | undefined;
332
+ /** `true` for an app's call, `false` for a kit component's own glyphs. */
333
+ readonly byApp: boolean;
334
+ }
335
+ /** @internal */
336
+ declare const LC_ICON_SCOPE: InjectionToken<IconScope>;
337
+ /**
338
+ * Registers kit glyphs for every `lc-icon` below this injector: `app.config`,
339
+ * a route's `providers` or a component's `providers` (3.6, AS-17). Registered
340
+ * glyphs render synchronously, in prerendered HTML too, and only they (plus
341
+ * the kit's internal ones) are bundled. Glyphs add up along the injector tree
342
+ * and across several calls in one injector; for `unregistered`, the nearest
343
+ * call that sets it wins.
344
+ *
345
+ * Without any call, every 3.5.1 name still renders: the kit's set loads once,
346
+ * lazily (see {@link LcIconOptions}).
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * // app.config.ts — provideLcIcons from the icon entry, the ICON_* constants
351
+ * // from the icons entry (@lifecorner/angular-ui-kit/icons).
352
+ * export const appConfig: ApplicationConfig = {
353
+ * providers: [provideLcIcons([ICON_HOME, ICON_MAIL, ICON_X_MARK], { unregistered: 'placeholder' })],
354
+ * };
355
+ * ```
356
+ */
357
+ export declare function provideLcIcons(icons: readonly LcIconDefinition[], options?: LcIconOptions): Provider[];
358
+ /**
359
+ * The glyphs a kit component renders itself (chevrons, close, status icons),
360
+ * for its `viewProviders`. Sets no `unregistered` mode and does not count as
361
+ * the app registering glyphs. Not documented for apps; use
362
+ * {@link provideLcIcons}.
363
+ */
364
+ export declare function ɵprovideKitIcons(icons: readonly LcIconDefinition[]): Provider;
365
+ /**
366
+ * The registered markup for a name and variant, nearest scope first.
367
+ * @internal
368
+ */
369
+ declare function findRegisteredGlyph(scope: IconScope | null, name: string, variant: IconVariant): string | undefined;
370
+ /**
371
+ * The nearest `unregistered` setting, `'load'` when nobody set one.
372
+ * @internal
373
+ */
374
+ declare function unregisteredFallback(scope: IconScope | null): LcIconFallback;
375
+ /**
376
+ * Whether the app registers glyphs anywhere above this scope.
377
+ * @internal
378
+ */
379
+ declare function appRegistersIcons(scope: IconScope | null): boolean;
380
+ export type { LcIconDefinition, LcIconFallback, LcIconOptions };
@@ -0,0 +1,140 @@
1
+ import { LcIconDefinition } from "@lifecorner/angular-ui-kit/icon";
2
+ export declare const ICON_HOME: LcIconDefinition;
3
+ export declare const ICON_HOME_SOLID: LcIconDefinition;
4
+ export declare const ICON_CHART_BAR_SQUARE: LcIconDefinition;
5
+ export declare const ICON_CHART_BAR_SQUARE_SOLID: LcIconDefinition;
6
+ export declare const ICON_LIGHT_BULB: LcIconDefinition;
7
+ export declare const ICON_LIGHT_BULB_SOLID: LcIconDefinition;
8
+ export declare const ICON_BANKNOTES: LcIconDefinition;
9
+ export declare const ICON_BANKNOTES_SOLID: LcIconDefinition;
10
+ export declare const ICON_PLAY_CIRCLE: LcIconDefinition;
11
+ export declare const ICON_PLAY_CIRCLE_SOLID: LcIconDefinition;
12
+ export declare const ICON_X_MARK: LcIconDefinition;
13
+ export declare const ICON_X_MARK_SOLID: LcIconDefinition;
14
+ export declare const ICON_BARS_3: LcIconDefinition;
15
+ export declare const ICON_BARS_3_SOLID: LcIconDefinition;
16
+ export declare const ICON_GLOBE_ALT: LcIconDefinition;
17
+ export declare const ICON_GLOBE_ALT_SOLID: LcIconDefinition;
18
+ export declare const ICON_CALENDAR: LcIconDefinition;
19
+ export declare const ICON_CALENDAR_SOLID: LcIconDefinition;
20
+ export declare const ICON_HASHTAG: LcIconDefinition;
21
+ export declare const ICON_HASHTAG_SOLID: LcIconDefinition;
22
+ export declare const ICON_SUN: LcIconDefinition;
23
+ export declare const ICON_SUN_SOLID: LcIconDefinition;
24
+ export declare const ICON_MOON: LcIconDefinition;
25
+ export declare const ICON_MOON_SOLID: LcIconDefinition;
26
+ export declare const ICON_USER: LcIconDefinition;
27
+ export declare const ICON_USER_SOLID: LcIconDefinition;
28
+ export declare const ICON_CHEVRON_DOWN: LcIconDefinition;
29
+ export declare const ICON_CHEVRON_DOWN_SOLID: LcIconDefinition;
30
+ export declare const ICON_CHEVRON_UP: LcIconDefinition;
31
+ export declare const ICON_CHEVRON_UP_SOLID: LcIconDefinition;
32
+ export declare const ICON_EYE: LcIconDefinition;
33
+ export declare const ICON_EYE_SOLID: LcIconDefinition;
34
+ export declare const ICON_EYE_SLASH: LcIconDefinition;
35
+ export declare const ICON_EYE_SLASH_SOLID: LcIconDefinition;
36
+ export declare const ICON_CHECK_CIRCLE: LcIconDefinition;
37
+ export declare const ICON_CHECK_CIRCLE_SOLID: LcIconDefinition;
38
+ export declare const ICON_EXCLAMATION_TRIANGLE: LcIconDefinition;
39
+ export declare const ICON_EXCLAMATION_TRIANGLE_SOLID: LcIconDefinition;
40
+ export declare const ICON_INFORMATION_CIRCLE: LcIconDefinition;
41
+ export declare const ICON_INFORMATION_CIRCLE_SOLID: LcIconDefinition;
42
+ export declare const ICON_EXCLAMATION_CIRCLE: LcIconDefinition;
43
+ export declare const ICON_EXCLAMATION_CIRCLE_SOLID: LcIconDefinition;
44
+ export declare const ICON_CLOCK: LcIconDefinition;
45
+ export declare const ICON_CLOCK_SOLID: LcIconDefinition;
46
+ export declare const ICON_ARROW_PATH: LcIconDefinition;
47
+ export declare const ICON_ARROW_PATH_SOLID: LcIconDefinition;
48
+ export declare const ICON_BUILDING_LIBRARY: LcIconDefinition;
49
+ export declare const ICON_BUILDING_LIBRARY_SOLID: LcIconDefinition;
50
+ export declare const ICON_ARROW_TRENDING_UP: LcIconDefinition;
51
+ export declare const ICON_ARROW_TRENDING_UP_SOLID: LcIconDefinition;
52
+ export declare const ICON_ARROW_TRENDING_DOWN: LcIconDefinition;
53
+ export declare const ICON_ARROW_TRENDING_DOWN_SOLID: LcIconDefinition;
54
+ export declare const ICON_PERCENT_BADGE: LcIconDefinition;
55
+ export declare const ICON_PERCENT_BADGE_SOLID: LcIconDefinition;
56
+ export declare const ICON_PRESENTATION_CHART_LINE: LcIconDefinition;
57
+ export declare const ICON_PRESENTATION_CHART_LINE_SOLID: LcIconDefinition;
58
+ export declare const ICON_BELL_ALERT: LcIconDefinition;
59
+ export declare const ICON_BELL_ALERT_SOLID: LcIconDefinition;
60
+ export declare const ICON_PENCIL: LcIconDefinition;
61
+ export declare const ICON_PENCIL_SOLID: LcIconDefinition;
62
+ export declare const ICON_PLAY: LcIconDefinition;
63
+ export declare const ICON_PLAY_SOLID: LcIconDefinition;
64
+ export declare const ICON_CHECK: LcIconDefinition;
65
+ export declare const ICON_CHECK_SOLID: LcIconDefinition;
66
+ export declare const ICON_DOCUMENT_DUPLICATE: LcIconDefinition;
67
+ export declare const ICON_DOCUMENT_DUPLICATE_SOLID: LcIconDefinition;
68
+ export declare const ICON_CHART_BAR: LcIconDefinition;
69
+ export declare const ICON_CHART_BAR_SOLID: LcIconDefinition;
70
+ export declare const ICON_BOLT: LcIconDefinition;
71
+ export declare const ICON_BOLT_SOLID: LcIconDefinition;
72
+ export declare const ICON_STAR: LcIconDefinition;
73
+ export declare const ICON_STAR_SOLID: LcIconDefinition;
74
+ export declare const ICON_PLUS: LcIconDefinition;
75
+ export declare const ICON_PLUS_SOLID: LcIconDefinition;
76
+ export declare const ICON_BELL_SLASH: LcIconDefinition;
77
+ export declare const ICON_BELL_SLASH_SOLID: LcIconDefinition;
78
+ export declare const ICON_MAIL: LcIconDefinition;
79
+ export declare const ICON_MAIL_SOLID: LcIconDefinition;
80
+ export declare const ICON_FILE_TEXT: LcIconDefinition;
81
+ export declare const ICON_FILE_TEXT_SOLID: LcIconDefinition;
82
+ export declare const ICON_FILE: LcIconDefinition;
83
+ export declare const ICON_FILE_SOLID: LcIconDefinition;
84
+ export declare const ICON_ARROW_RIGHT: LcIconDefinition;
85
+ export declare const ICON_ARROW_LEFT: LcIconDefinition;
86
+ export declare const ICON_EXTERNAL_LINK: LcIconDefinition;
87
+ export declare const ICON_EXTERNAL_LINK_SOLID: LcIconDefinition;
88
+ export declare const ICON_WORLD: LcIconDefinition;
89
+ export declare const ICON_WORLD_SOLID: LcIconDefinition;
90
+ export declare const ICON_MENU_2: LcIconDefinition;
91
+ export declare const ICON_MENU_2_SOLID: LcIconDefinition;
92
+ export declare const ICON_X: LcIconDefinition;
93
+ export declare const ICON_X_SOLID: LcIconDefinition;
94
+ export declare const ICON_USERS: LcIconDefinition;
95
+ export declare const ICON_USER_PLUS: LcIconDefinition;
96
+ export declare const ICON_SETTINGS: LcIconDefinition;
97
+ export declare const ICON_SETTINGS_SOLID: LcIconDefinition;
98
+ export declare const ICON_LOGOUT: LcIconDefinition;
99
+ export declare const ICON_DOTS: LcIconDefinition;
100
+ export declare const ICON_DOTS_SOLID: LcIconDefinition;
101
+ export declare const ICON_WIFI_OFF: LcIconDefinition;
102
+ export declare const ICON_CHEVRON_RIGHT: LcIconDefinition;
103
+ export declare const ICON_CHEVRON_RIGHT_SOLID: LcIconDefinition;
104
+ export declare const ICON_CHEVRON_LEFT: LcIconDefinition;
105
+ export declare const ICON_LOGIN: LcIconDefinition;
106
+ export declare const ICON_KEY: LcIconDefinition;
107
+ export declare const ICON_KEY_SOLID: LcIconDefinition;
108
+ export declare const ICON_PASSWORD: LcIconDefinition;
109
+ export declare const ICON_SHIELD_CHECK: LcIconDefinition;
110
+ export declare const ICON_SHIELD_CHECK_SOLID: LcIconDefinition;
111
+ export declare const ICON_ROBOT: LcIconDefinition;
112
+ export declare const ICON_PLUG_CONNECTED: LcIconDefinition;
113
+ export declare const ICON_DEVICE_MOBILE: LcIconDefinition;
114
+ export declare const ICON_DEVICE_MOBILE_SOLID: LcIconDefinition;
115
+ export declare const ICON_DEVICE_DESKTOP: LcIconDefinition;
116
+ export declare const ICON_DEVICE_DESKTOP_SOLID: LcIconDefinition;
117
+ export declare const ICON_DEVICE_LAPTOP: LcIconDefinition;
118
+ export declare const ICON_COPY: LcIconDefinition;
119
+ export declare const ICON_COPY_SOLID: LcIconDefinition;
120
+ export declare const ICON_SHARE: LcIconDefinition;
121
+ export declare const ICON_LINK: LcIconDefinition;
122
+ export declare const ICON_LINK_SOLID: LcIconDefinition;
123
+ export declare const ICON_TRASH: LcIconDefinition;
124
+ export declare const ICON_TRASH_SOLID: LcIconDefinition;
125
+ export declare const ICON_INFO_CIRCLE: LcIconDefinition;
126
+ export declare const ICON_INFO_CIRCLE_SOLID: LcIconDefinition;
127
+ export declare const ICON_CIRCLE_CHECK: LcIconDefinition;
128
+ export declare const ICON_CIRCLE_CHECK_SOLID: LcIconDefinition;
129
+ export declare const ICON_ALERT_TRIANGLE: LcIconDefinition;
130
+ export declare const ICON_ALERT_TRIANGLE_SOLID: LcIconDefinition;
131
+ export declare const ICON_ALERT_CIRCLE: LcIconDefinition;
132
+ export declare const ICON_ALERT_CIRCLE_SOLID: LcIconDefinition;
133
+ export declare const ICON_CIRCLE_X: LcIconDefinition;
134
+ export declare const ICON_CIRCLE_X_SOLID: LcIconDefinition;
135
+ /**
136
+ * Every glyph above (133): `provideLcIcons(BUNDLED_ICONS)` restores the
137
+ * synchronous rendering of 3.5.1 for every inline name, e.g. in tests or
138
+ * Storybook. An app that imports it bundles all of them.
139
+ */
140
+ export declare const BUNDLED_ICONS: readonly LcIconDefinition[];
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `lc-icon`'s variant, declared here as well: this entry is loaded lazily by
3
+ * `@lifecorner/angular-ui-kit/icon` and must not import it (an entry cycle).
4
+ * @internal
5
+ */
6
+ type IconSetVariant = 'outline' | 'solid';
7
+ /**
8
+ * Inline SVG map for common icons (the hand-picked set). The source of truth:
9
+ * `scripts/generate-icon-names.mjs` derives the key index of `lc-icon` and the
10
+ * `ICON_*` constants of `@lifecorner/angular-ui-kit/icons` from it.
11
+ *
12
+ * Since 3.6 (AS-17) this map lives in the lazy `internal/icon-set` entry:
13
+ * `lc-icon` loads it only for a 3.5.1 inline name that nobody registered.
14
+ * Keyed by the (alias-resolved) icon name, then variant.
15
+ *
16
+ * Every key is included in {@link ICON_NAMES} as a valid `lc-icon` name.
17
+ *
18
+ * @see ICON_NAMES
19
+ */
20
+ export declare const INLINE_ICON_SVGS: Readonly<Record<string, Record<IconSetVariant, string>>>;
21
+ /**
22
+ * Tabler icons inlined at build time (3.3, G-7), keyed by Tabler name, then
23
+ * variant (`solid` only where Tabler has a filled icon). `lc-icon` uses them
24
+ * after the hand-picked {@link INLINE_ICON_SVGS} and before any request, so
25
+ * they render offline and in prerendered HTML. Since 3.6 (AS-17) they load
26
+ * lazily with the rest of the kit's set, unless an app registers them.
27
+ */
28
+ export declare const INLINE_TABLER_SVGS: Readonly<Record<string, Partial<Record<IconSetVariant, string>>>>;
29
+ /** The names in {@link INLINE_TABLER_SVGS}, in generator order. */
30
+ export declare const INLINE_TABLER_NAMES: readonly string[];
31
+ export type { IconSetVariant };
@@ -0,0 +1,28 @@
1
+ import { Router } from "@angular/router";
2
+ import { NavigationItem } from "@lifecorner/angular-ui-kit/models";
3
+ /**
4
+ * The side navigation's current-item rules (3.5, AS-16), moved out of
5
+ * `lc-sidenav` unchanged (3.6, AS-18) so `lc-sidenav-placeholder` expands the
6
+ * same groups before the sidenav has loaded.
7
+ *
8
+ * @internal
9
+ */
10
+ /** The items sorted by `displayOrder`. */
11
+ export declare function sortNavigationItems(items: readonly NavigationItem[]): NavigationItem[];
12
+ /**
13
+ * Every item rendered as a link, in display order (depth first): simple
14
+ * items, the children of a group, the children of a section, and the
15
+ * children of a section's sub-group.
16
+ */
17
+ export declare function navigationLinks(sortedItems: readonly NavigationItem[]): NavigationItem[];
18
+ /** The router URL's path: primary outlet only, no query, fragment or matrix parameters. */
19
+ export declare function navigationPath(router: Pick<Router, 'parseUrl'>, url: string): string;
20
+ /**
21
+ * The one current link (or none). With `activeRoute` set: the first link
22
+ * whose route equals it. Without: the router's path — the exact match wins,
23
+ * otherwise the longest link route the path continues with a `/` (`/`
24
+ * matches only `/`); ties go to the first link in display order.
25
+ */
26
+ export declare function currentNavigationItem(links: readonly NavigationItem[], activeRoute: string, path: string): NavigationItem | null;
27
+ /** Ids of the collapsible parents (top-level groups, section sub-groups) that contain the current item. */
28
+ export declare function currentItemParents(items: readonly NavigationItem[], current: NavigationItem | null): string[];