@nexterajs/ui 0.0.2 → 0.1.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 (59) hide show
  1. package/README.md +279 -34
  2. package/fesm2022/nexterajs-ui-charts.mjs +1096 -0
  3. package/fesm2022/nexterajs-ui-charts.mjs.map +1 -0
  4. package/fesm2022/nexterajs-ui-core.mjs +652 -12
  5. package/fesm2022/nexterajs-ui-core.mjs.map +1 -1
  6. package/fesm2022/nexterajs-ui-display.mjs +1142 -0
  7. package/fesm2022/nexterajs-ui-display.mjs.map +1 -0
  8. package/fesm2022/nexterajs-ui-dropdowns.mjs +321 -33
  9. package/fesm2022/nexterajs-ui-dropdowns.mjs.map +1 -1
  10. package/fesm2022/nexterajs-ui-file-uploader.mjs +572 -10
  11. package/fesm2022/nexterajs-ui-file-uploader.mjs.map +1 -1
  12. package/fesm2022/nexterajs-ui-forms.mjs +2027 -136
  13. package/fesm2022/nexterajs-ui-forms.mjs.map +1 -1
  14. package/fesm2022/nexterajs-ui-grid.mjs +1222 -188
  15. package/fesm2022/nexterajs-ui-grid.mjs.map +1 -1
  16. package/fesm2022/nexterajs-ui-intl.mjs +103 -11
  17. package/fesm2022/nexterajs-ui-intl.mjs.map +1 -1
  18. package/fesm2022/nexterajs-ui-navigation.mjs +1054 -0
  19. package/fesm2022/nexterajs-ui-navigation.mjs.map +1 -0
  20. package/fesm2022/nexterajs-ui-notify.mjs +46 -44
  21. package/fesm2022/nexterajs-ui-notify.mjs.map +1 -1
  22. package/fesm2022/nexterajs-ui-overlay.mjs +1671 -0
  23. package/fesm2022/nexterajs-ui-overlay.mjs.map +1 -0
  24. package/fesm2022/nexterajs-ui-pivot.mjs +51 -26
  25. package/fesm2022/nexterajs-ui-pivot.mjs.map +1 -1
  26. package/fesm2022/nexterajs-ui-shell.mjs +542 -0
  27. package/fesm2022/nexterajs-ui-shell.mjs.map +1 -0
  28. package/fesm2022/nexterajs-ui-tree-view.mjs +5 -2
  29. package/fesm2022/nexterajs-ui-tree-view.mjs.map +1 -1
  30. package/fesm2022/nexterajs-ui-validation.mjs +42 -11
  31. package/fesm2022/nexterajs-ui-validation.mjs.map +1 -1
  32. package/fesm2022/nexterajs-ui.mjs +5 -0
  33. package/fesm2022/nexterajs-ui.mjs.map +1 -1
  34. package/package.json +33 -2
  35. package/styles/_dropdown.scss +5 -5
  36. package/styles/_editor.scss +52 -17
  37. package/styles/_mixins.scss +1 -1
  38. package/styles/_scrollbar.scss +5 -5
  39. package/styles/_status.scss +21 -0
  40. package/styles/_toast.scss +14 -14
  41. package/styles/_tokens.scss +420 -3
  42. package/styles/_widget.scss +14 -3
  43. package/styles/index.scss +6 -4
  44. package/types/nexterajs-ui-charts.d.ts +446 -0
  45. package/types/nexterajs-ui-core.d.ts +387 -22
  46. package/types/nexterajs-ui-display.d.ts +720 -0
  47. package/types/nexterajs-ui-dropdowns.d.ts +191 -48
  48. package/types/nexterajs-ui-file-uploader.d.ts +236 -7
  49. package/types/nexterajs-ui-forms.d.ts +675 -30
  50. package/types/nexterajs-ui-grid.d.ts +448 -93
  51. package/types/nexterajs-ui-intl.d.ts +39 -4
  52. package/types/nexterajs-ui-navigation.d.ts +531 -0
  53. package/types/nexterajs-ui-notify.d.ts +4 -2
  54. package/types/nexterajs-ui-overlay.d.ts +703 -0
  55. package/types/nexterajs-ui-pivot.d.ts +56 -48
  56. package/types/nexterajs-ui-shell.d.ts +294 -0
  57. package/types/nexterajs-ui-tree-view.d.ts +6 -3
  58. package/types/nexterajs-ui-validation.d.ts +20 -11
  59. package/types/nexterajs-ui.d.ts +5 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nexterajs-ui-shell.mjs","sources":["../../../projects/nextera-ui/shell/src/lib/nxe-shell.types.ts","../../../projects/nextera-ui/shell/src/lib/sidenav/nxe-sidenav.component.ts","../../../projects/nextera-ui/shell/src/lib/sidenav/nxe-sidenav.component.html","../../../projects/nextera-ui/shell/src/lib/shell/nxe-shell.component.ts","../../../projects/nextera-ui/shell/src/lib/shell/nxe-shell.component.html","../../../projects/nextera-ui/shell/src/lib/topbar/nxe-topbar.component.ts","../../../projects/nextera-ui/shell/src/lib/topbar/nxe-topbar.component.html","../../../projects/nextera-ui/shell/src/public-api.ts","../../../projects/nextera-ui/shell/src/nexterajs-ui-shell.ts"],"sourcesContent":["import { Directive, InjectionToken, Signal } from '@angular/core';\nimport type { Params } from '@angular/router';\nimport type { NxeStatusFamily } from '@nexterajs/ui/display';\n\n/**\n * One entry of the side navigation.\n *\n * - A **link** has `route` (router) or `href` (plain anchor).\n * - A **parent** has `children`: it is a disclosure button, not a link. Its children appear under it\n * when it is open, and it opens by itself when one of them is the current page.\n * - A **group** has `heading: true` and `children`: a non-interactive heading, with its items in a\n * labelled list under it. Groups sit at the top level; items inside them nest as deep as needed.\n * - An entry with none of those is a plain button; `itemClick` reports it.\n */\nexport interface NxeNavItem {\n /** Unique within the tree. Keys the open state and the DOM ids. */\n readonly id: string;\n /** What the entry says. Translate it. */\n readonly label: string;\n /**\n * The icon, named for `<i class=\"nxe-icon nxe-icon-NAME\">`, or, when the app supplies an\n * `ng-template[nxeSidenavIcon]`, any key that template maps to an icon. An entry without one\n * shows the first letter of its label so a collapsed rail is still usable.\n */\n readonly icon?: string;\n /** Router link: a URL (`'/buyer/inbox'`) or commands (`['/buyer/price-lists', id]`). */\n readonly route?: string | unknown[];\n /** Query parameters for `route`. When set they take part in the active match. */\n readonly queryParams?: Params;\n /** A plain `href`, for a link that leaves the app. Never shown as the current page. */\n readonly href?: string;\n /** A count, or a signal of one (read in the template, so it updates without a new tree). */\n readonly badge?: number | Signal<number | null | undefined>;\n /** Colour of the badge; `danger` by default. */\n readonly badgeFamily?: NxeStatusFamily;\n /** Spoken name of the badge (\"5 items need action\"); defaults to the number. */\n readonly badgeLabel?: string;\n /** Children of a parent entry, or the items of a group. */\n readonly children?: readonly NxeNavItem[];\n /** Make this a group heading; its `children` are the group's items. */\n readonly heading?: boolean;\n /** Not interactive, not focusable, announced as disabled. */\n readonly disabled?: boolean;\n /**\n * Match the URL exactly. Without it an entry is the current page for its URL and everything\n * below it (`/buyer/purchase-orders` for `/buyer/purchase-orders/42`). Give the home entry\n * (`/buyer`) `exact: true`, or it is current everywhere.\n */\n readonly exact?: boolean;\n}\n\n/** `docked`: beside the content, expanded or a rail. `drawer`: off-canvas, over the content. */\nexport type NxeShellMode = 'docked' | 'drawer';\n\n/**\n * What `nxe-topbar` and `nxe-sidenav` need from the `nxe-shell` around them, through\n * {@link NXE_SHELL}. Both also work without a shell; they then do not toggle anything.\n */\nexport interface NxeShellState {\n readonly mode: Signal<NxeShellMode>;\n /** The sidenav is a narrow icon rail (docked and collapsed). */\n readonly rail: Signal<boolean>;\n /** Docked: the user's collapsed choice. */\n readonly collapsed: Signal<boolean>;\n /** The drawer is on screen (drawer mode and open). */\n readonly drawerShown: Signal<boolean>;\n /** DOM id of the projected `nxe-sidenav`, for `aria-controls`. */\n readonly sidenavId: Signal<string | null>;\n setCollapsed(collapsed: boolean): void;\n /** Hamburger: open or close the drawer, or collapse or expand the dock. */\n toggleNavigation(): void;\n closeDrawer(): void;\n}\n\n/** Provided by `nxe-shell` to the topbar and sidenav projected into it. */\nexport const NXE_SHELL = new InjectionToken<NxeShellState>('NXE_SHELL');\n\n/**\n * `<ng-template nxeSidenavIcon let-item>` inside `nxe-sidenav` draws the icon of every entry that\n * has an `icon`, given the entry. Use it to map `icon` keys to your own SVGs:\n *\n * ```html\n * <nxe-sidenav [items]=\"items\">\n * <ng-template nxeSidenavIcon let-item>\n * <svg viewBox=\"0 0 24 24\"><path [attr.d]=\"paths[item.icon]\" /></svg>\n * </ng-template>\n * </nxe-sidenav>\n * ```\n */\n@Directive({ selector: 'ng-template[nxeSidenavIcon]' })\nexport class NxeSidenavIconDirective {}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n ElementRef,\n inject,\n input,\n linkedSignal,\n model,\n output,\n TemplateRef,\n viewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, PRIMARY_OUTLET, Router, RouterLink, UrlTree } from '@angular/router';\nimport { NxeMessageKey, NxeMessageService } from '@nexterajs/ui/core';\nimport { NxeBadgeAnchorDirective, NxeBadgeComponent } from '@nexterajs/ui/display';\nimport { NxeButtonComponent } from '@nexterajs/ui/forms';\nimport { filter, map } from 'rxjs';\nimport { NXE_SHELL, NxeNavItem, NxeSidenavIconDirective } from '../nxe-shell.types';\n\n/** An item with the place it holds in the tree. */\ninterface NavNode {\n readonly item: NxeNavItem;\n /** Nearest ancestor that is an item (a group heading is not one); null at the top. */\n readonly parentId: string | null;\n /** What the router would match for this entry; null for a parent, group or plain button. */\n readonly tree: UrlTree | null;\n}\n\nlet nextSidenavId = 0;\n\n/**\n * `nxe-sidenav` is the application's primary navigation, drawn from a tree of {@link NxeNavItem}.\n *\n * ```html\n * <nxe-sidenav [items]=\"items\" (itemClick)=\"track($event)\">\n * <img nxeSidenavHeader src=\"logo.svg\" alt=\"Nextera\" />\n * </nxe-sidenav>\n * ```\n *\n * **Structure and ARIA.** A `<nav>` landmark holding nested lists, in the pattern the ARIA Authoring\n * Practices call a *disclosure navigation menu*: a link is `<a aria-current=\"page\">` when it is the\n * current page; a parent is a `<button aria-expanded aria-controls>` that shows or hides the list\n * under it; a group heading labels its list (`aria-labelledby`). It is deliberately not\n * `role=\"tree\"`: a tree makes one node focusable and needs the full tree keyboard contract, where\n * every link here is an ordinary tab stop and the arrow keys are a shortcut on top of Tab.\n *\n * **Keyboard.** Tab and Shift+Tab visit every link and parent. On a link or parent, Arrow Down/Up\n * move to the next/previous visible entry, Home/End to the first/last. Arrow Right opens a closed\n * parent, or moves into an open one's first child; Arrow Left closes an open parent, or moves from\n * a child to its parent (Right and Left swap in a right-to-left document). Enter and Space activate.\n *\n * **Current page.** From the router: an entry is current when its URL matches (a prefix, or the\n * whole URL with `exact`), and when several do, the most specific one alone is `aria-current`\n * (`/payments/aging` over `/payments`). The parents of the current page open themselves on\n * navigation; what the user opened or closed by hand holds until the next navigation into it.\n *\n * **Collapsed rail.** With the sidenav collapsed (through `nxe-shell` or the `collapsed` model) only\n * the icons show, each with its label as a native `title` and as visually-hidden text, and a badge\n * pins to the icon. There is no flyout for children (that waits for the menu): activating a parent\n * in the rail expands the rail with that parent open.\n *\n * Inside an `nxe-shell` the shell owns the collapsed state and the off-canvas drawer; on its own the\n * sidenav is a plain docked column.\n */\n@Component({\n selector: 'nxe-sidenav',\n imports: [NgTemplateOutlet, RouterLink, NxeBadgeComponent, NxeBadgeAnchorDirective, NxeButtonComponent],\n templateUrl: './nxe-sidenav.component.html',\n styleUrl: './nxe-sidenav.component.scss',\n host: {\n class: 'nxe-sidenav',\n '[id]': 'id',\n '[class.nxe-sidenav-rail]': 'rail()',\n '[class.nxe-sidenav-drawer]': 'drawer()',\n '[class.nxe-sidenav-open]': 'drawerOpen()',\n '[attr.inert]': \"drawer() && !drawerOpen() ? '' : null\",\n '[attr.role]': \"drawerOpen() ? 'dialog' : null\",\n '[attr.aria-modal]': \"drawerOpen() ? 'true' : null\",\n '[attr.aria-label]': 'drawerOpen() ? navLabel() : null',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class NxeSidenavComponent {\n private readonly router = inject(Router);\n private readonly messages = inject(NxeMessageService);\n private readonly shell = inject(NXE_SHELL, { optional: true });\n /** The sidenav's own element; `nxe-shell` traps focus in it while the drawer is open. */\n readonly element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly navElement = viewChild.required<ElementRef<HTMLElement>>('nav');\n\n /** DOM id, which the topbar's toggle button names in `aria-controls`. */\n readonly id = `nxe-sidenav-${nextSidenavId++}`;\n\n /** The navigation tree. */\n readonly items = input<readonly NxeNavItem[]>([]);\n /** Accessible name of the `<nav>` landmark; \"Main navigation\" in the active language by default. */\n readonly label = input<string>('');\n /**\n * Collapsed to the icon rail. Inside an `nxe-shell` the shell's `collapsed` wins and this is\n * left alone; the sidenav writes the user's choice through the shell instead.\n */\n readonly collapsed = model(false);\n /** Show the collapse/expand button at the foot of a docked sidenav. Default true. */\n readonly collapsible = input(true, { transform: booleanAttribute });\n /** An entry was activated: a link followed, a plain button pressed. Parents do not emit. */\n readonly itemClick = output<NxeNavItem>();\n\n /** Draws the icon of every entry that has an `icon`. */\n protected readonly iconTemplate = contentChild(NxeSidenavIconDirective, { read: TemplateRef });\n\n protected readonly navLabel = computed(() => this.label() || this.messages.t('shell.mainNavigation'));\n\n protected readonly rail = computed(() => (this.shell ? this.shell.rail() : this.collapsed()));\n protected readonly drawer = computed(() => !!this.shell && this.shell.mode() === 'drawer');\n protected readonly drawerOpen = computed(() => !!this.shell && this.shell.drawerShown());\n\n /** Every entry by id, with its place in the tree and the URL it points at. */\n private readonly nodes = computed(() => {\n const nodes = new Map<string, NavNode>();\n const walk = (items: readonly NxeNavItem[], parentId: string | null) => {\n for (const item of items) {\n const heading = !!item.heading;\n nodes.set(item.id, {\n item,\n parentId,\n tree: !heading && !item.disabled && !item.children?.length ? this.treeOf(item) : null,\n });\n walk(item.children ?? [], heading ? parentId : item.id);\n }\n };\n walk(this.items(), null);\n return nodes;\n });\n\n /** The router's URL, re-read at every navigation so what is current is a signal. */\n private readonly url = toSignal(\n this.router.events.pipe(\n filter((event) => event instanceof NavigationEnd),\n map(() => this.router.url),\n ),\n { initialValue: this.router.url },\n );\n\n /** Id of the one entry that is the current page. */\n private readonly currentId = computed<string | null>(() => {\n this.url();\n let best: string | null = null;\n let bestScore = -1;\n for (const [id, node] of this.nodes()) {\n if (!node.tree) {\n continue;\n }\n const exact = !!node.item.exact;\n const active = this.router.isActive(node.tree, {\n paths: exact ? 'exact' : 'subset',\n queryParams: exact ? 'exact' : node.item.queryParams ? 'subset' : 'ignored',\n fragment: 'ignored',\n matrixParams: 'ignored',\n });\n if (!active) {\n continue;\n }\n const score =\n (node.tree.root.children[PRIMARY_OUTLET]?.segments.length ?? 0) * 100 +\n Object.keys(node.item.queryParams ?? {}).length;\n if (score > bestScore) {\n best = id;\n bestScore = score;\n }\n }\n return best;\n });\n\n /** Ids of the parents above the current page. They open by themselves. */\n private readonly currentAncestors = computed(\n () => {\n const ancestors = new Set<string>();\n const nodes = this.nodes();\n const current = this.currentId();\n let id = current ? (nodes.get(current)?.parentId ?? null) : null;\n while (id) {\n ancestors.add(id);\n id = nodes.get(id)?.parentId ?? null;\n }\n return ancestors as ReadonlySet<string>;\n },\n { equal: (a, b) => a.size === b.size && [...a].every((id) => b.has(id)) },\n );\n\n /**\n * What the user opened or closed by hand. Navigating to a new page forgets the choices about\n * its ancestors, so they open for it, and keeps the rest.\n */\n private readonly manual = linkedSignal<\n { url: string; ancestors: ReadonlySet<string> },\n ReadonlyMap<string, boolean>\n >({\n source: () => ({ url: this.url(), ancestors: this.currentAncestors() }),\n computation: ({ ancestors }, previous) => {\n const next = new Map(previous?.value);\n ancestors.forEach((id) => next.delete(id));\n return next;\n },\n });\n\n protected t(key: NxeMessageKey): string {\n return this.messages.t(key);\n }\n\n protected isCurrent(id: string): boolean {\n return this.currentId() === id;\n }\n\n /** The current page is somewhere under this parent. */\n protected holdsCurrent(id: string): boolean {\n return this.currentAncestors().has(id);\n }\n\n protected isOpen(id: string): boolean {\n return this.manual().get(id) ?? this.currentAncestors().has(id);\n }\n\n protected hasChildren(item: NxeNavItem): boolean {\n return !item.heading && !!item.children?.length;\n }\n\n protected badgeValue(item: NxeNavItem): number | null | undefined {\n const badge = item.badge;\n return typeof badge === 'function' ? badge() : badge;\n }\n\n protected badgeName(item: NxeNavItem): string {\n const value = this.badgeValue(item);\n return item.badgeLabel && value ? `${value} ${item.badgeLabel}` : '';\n }\n\n protected initial(item: NxeNavItem): string {\n return item.label.trim().charAt(0).toUpperCase();\n }\n\n /** Toggle a parent; in the rail, expand the rail with the parent open instead. */\n protected onParent(item: NxeNavItem): void {\n if (this.rail()) {\n this.setCollapsed(false);\n this.setOpen(item.id, true);\n } else {\n this.setOpen(item.id, !this.isOpen(item.id));\n }\n }\n\n protected onActivate(item: NxeNavItem): void {\n this.itemClick.emit(item);\n }\n\n protected toggleCollapsed(): void {\n this.setCollapsed(!this.rail());\n }\n\n protected closeDrawer(): void {\n this.shell?.closeDrawer();\n }\n\n protected onKeydown(event: KeyboardEvent, item: NxeNavItem): void {\n if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {\n return;\n }\n const current = event.currentTarget as HTMLElement;\n const links = this.focusables();\n const at = links.indexOf(current);\n const rtl = getComputedStyle(current).direction === 'rtl';\n const key =\n event.key === 'ArrowRight' ? (rtl ? 'ArrowLeft' : 'ArrowRight') : event.key === 'ArrowLeft' ? (rtl ? 'ArrowRight' : 'ArrowLeft') : event.key;\n const parent = this.hasChildren(item) && !this.rail();\n\n let target: HTMLElement | undefined;\n switch (key) {\n case 'ArrowDown':\n target = links[at + 1];\n break;\n case 'ArrowUp':\n target = links[at - 1];\n break;\n case 'Home':\n target = links[0];\n break;\n case 'End':\n target = links[links.length - 1];\n break;\n case 'ArrowRight':\n if (!parent) {\n return;\n }\n if (!this.isOpen(item.id)) {\n this.setOpen(item.id, true);\n } else {\n target = links[at + 1];\n }\n break;\n case 'ArrowLeft': {\n if (parent && this.isOpen(item.id)) {\n this.setOpen(item.id, false);\n break;\n }\n const parentId = this.nodes().get(item.id)?.parentId;\n target = parentId ? links.find((link) => link.dataset['nxeNav'] === parentId) : undefined;\n if (!target) {\n return;\n }\n break;\n }\n default:\n return;\n }\n event.preventDefault();\n target?.focus();\n }\n\n /** Links and parent buttons in document order; the ones inside a closed parent are not rendered. */\n private focusables(): HTMLElement[] {\n return Array.from(this.navElement().nativeElement.querySelectorAll<HTMLElement>('a.nxe-nav-link, button.nxe-nav-link'));\n }\n\n private setOpen(id: string, open: boolean): void {\n this.manual.update((current) => new Map(current).set(id, open));\n }\n\n private setCollapsed(collapsed: boolean): void {\n if (this.shell) {\n this.shell.setCollapsed(collapsed);\n } else {\n this.collapsed.set(collapsed);\n }\n }\n\n private treeOf(item: NxeNavItem): UrlTree | null {\n if (item.route === undefined) {\n return null;\n }\n if (typeof item.route === 'string') {\n const tree = this.router.parseUrl(item.route);\n tree.queryParams = { ...tree.queryParams, ...item.queryParams };\n return tree;\n }\n return this.router.createUrlTree(item.route, { queryParams: item.queryParams });\n }\n}\n","<div class=\"nxe-sidenav-header\">\n <ng-content select=\"[nxeSidenavHeader]\" />\n @if (drawer()) {\n <nxe-button class=\"nxe-sidenav-close\" stylingMode=\"text\" size=\"sm\" [attr.aria-label]=\"t('shell.closeNavigation')\" (onClick)=\"closeDrawer()\">\n <svg nxeButtonIcon viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M3.7 2.3 8 6.6l4.3-4.3 1.4 1.4L9.4 8l4.3 4.3-1.4 1.4L8 9.4l-4.3 4.3-1.4-1.4L6.6 8 2.3 3.7z\" />\n </svg>\n </nxe-button>\n }\n</div>\n\n<nav #nav class=\"nxe-sidenav-nav\" [attr.aria-label]=\"navLabel()\">\n <ul class=\"nxe-nav-list nxe-nav-root\">\n @for (item of items(); track item.id) {\n <ng-container *ngTemplateOutlet=\"node; context: { $implicit: item, depth: 0 }\" />\n }\n </ul>\n</nav>\n\n<div class=\"nxe-sidenav-footer\">\n <ng-content select=\"[nxeSidenavFooter]\" />\n @if (!drawer() && collapsible()) {\n <nxe-button\n class=\"nxe-sidenav-collapse\"\n stylingMode=\"text\"\n size=\"sm\"\n [attr.aria-label]=\"rail() ? t('shell.expandSidebar') : t('shell.collapseSidebar')\"\n [attr.title]=\"rail() ? t('shell.expandSidebar') : t('shell.collapseSidebar')\"\n (onClick)=\"toggleCollapsed()\">\n <svg nxeButtonIcon class=\"nxe-sidenav-collapse-icon\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M9.7 3.3 5 8l4.7 4.7 1.1-1.1L7.2 8l3.6-3.6zM3 3h1.5v10H3z\" />\n </svg>\n </nxe-button>\n }\n</div>\n\n<!-- One entry's icon, label and badge; shared by every kind of entry. -->\n<ng-template #body let-item>\n <span class=\"nxe-nav-lead\" nxeBadgeAnchor>\n <span class=\"nxe-nav-icon\" aria-hidden=\"true\">\n @if (item.icon && iconTemplate(); as icon) {\n <ng-container *ngTemplateOutlet=\"icon; context: { $implicit: item }\" />\n } @else if (item.icon) {\n <i class=\"nxe-icon nxe-icon-{{ item.icon }}\"></i>\n } @else {\n <span class=\"nxe-nav-glyph\">{{ initial(item) }}</span>\n }\n </span>\n @if (rail()) {\n <nxe-badge overlay [value]=\"badgeValue(item)\" [family]=\"item.badgeFamily ?? 'danger'\" [label]=\"badgeName(item)\" />\n }\n </span>\n <span class=\"nxe-nav-label\">{{ item.label }}</span>\n @if (!rail()) {\n <nxe-badge class=\"nxe-nav-badge\" [value]=\"badgeValue(item)\" [family]=\"item.badgeFamily ?? 'danger'\" [label]=\"badgeName(item)\" />\n }\n</ng-template>\n\n<ng-template #node let-item let-depth=\"depth\">\n @if (item.heading) {\n <li class=\"nxe-nav-group\">\n <div class=\"nxe-nav-heading\" [id]=\"id + '-' + item.id\" [class.nxe-nav-heading-rail]=\"rail()\">{{ item.label }}</div>\n <ul class=\"nxe-nav-list\" [attr.aria-labelledby]=\"id + '-' + item.id\">\n @for (child of item.children; track child.id) {\n <ng-container *ngTemplateOutlet=\"node; context: { $implicit: child, depth: depth }\" />\n }\n </ul>\n </li>\n } @else {\n <li\n class=\"nxe-nav-item\"\n [class.nxe-nav-current]=\"isCurrent(item.id)\"\n [class.nxe-nav-holds-current]=\"holdsCurrent(item.id)\"\n [style.--nxe-nav-depth]=\"depth\">\n @if (item.disabled) {\n <span class=\"nxe-nav-link nxe-nav-link-disabled\" aria-disabled=\"true\" [attr.title]=\"rail() ? item.label : null\">\n <ng-container *ngTemplateOutlet=\"body; context: { $implicit: item }\" />\n </span>\n } @else if (hasChildren(item)) {\n <button\n type=\"button\"\n class=\"nxe-nav-link nxe-nav-parent\"\n [attr.data-nxe-nav]=\"item.id\"\n [attr.aria-expanded]=\"!rail() && isOpen(item.id)\"\n [attr.aria-controls]=\"!rail() && isOpen(item.id) ? id + '-' + item.id + '-list' : null\"\n [attr.title]=\"rail() ? item.label : null\"\n (click)=\"onParent(item)\"\n (keydown)=\"onKeydown($event, item)\">\n <ng-container *ngTemplateOutlet=\"body; context: { $implicit: item }\" />\n @if (!rail()) {\n <svg class=\"nxe-nav-chevron\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"m4.2 5.8 3.8 3.8 3.8-3.8 1 1L8 11.6 3.2 6.8z\" />\n </svg>\n }\n </button>\n @if (!rail() && isOpen(item.id)) {\n <ul class=\"nxe-nav-list nxe-nav-sub\" [id]=\"id + '-' + item.id + '-list'\">\n @for (child of item.children; track child.id) {\n <ng-container *ngTemplateOutlet=\"node; context: { $implicit: child, depth: depth + 1 }\" />\n }\n </ul>\n }\n } @else if (item.route !== undefined) {\n <a\n class=\"nxe-nav-link\"\n [attr.data-nxe-nav]=\"item.id\"\n [routerLink]=\"item.route\"\n [queryParams]=\"item.queryParams\"\n [attr.aria-current]=\"isCurrent(item.id) ? 'page' : null\"\n [attr.title]=\"rail() ? item.label : null\"\n (click)=\"onActivate(item)\"\n (keydown)=\"onKeydown($event, item)\">\n <ng-container *ngTemplateOutlet=\"body; context: { $implicit: item }\" />\n </a>\n } @else if (item.href !== undefined) {\n <a\n class=\"nxe-nav-link\"\n [attr.data-nxe-nav]=\"item.id\"\n [attr.href]=\"item.href\"\n [attr.title]=\"rail() ? item.label : null\"\n (click)=\"onActivate(item)\"\n (keydown)=\"onKeydown($event, item)\">\n <ng-container *ngTemplateOutlet=\"body; context: { $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"nxe-nav-link\"\n [attr.data-nxe-nav]=\"item.id\"\n [attr.title]=\"rail() ? item.label : null\"\n (click)=\"onActivate(item)\"\n (keydown)=\"onKeydown($event, item)\">\n <ng-container *ngTemplateOutlet=\"body; context: { $implicit: item }\" />\n </button>\n }\n </li>\n }\n</ng-template>\n","import {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n DestroyRef,\n ElementRef,\n forwardRef,\n inject,\n Injector,\n input,\n model,\n numberAttribute,\n signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\nimport { FocusTrap, FocusTrapFactory } from '@angular/cdk/a11y';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { NxeMessageService } from '@nexterajs/ui/core';\nimport { filter } from 'rxjs';\nimport { NxeSidenavComponent } from '../sidenav/nxe-sidenav.component';\nimport { NXE_SHELL, NxeShellMode, NxeShellState } from '../nxe-shell.types';\n\nlet nextShellId = 0;\n\n/**\n * `nxe-shell` is the page frame of an application: an optional banner, the topbar, the sidenav and\n * the main content.\n *\n * ```html\n * <nxe-shell [(collapsed)]=\"collapsed\">\n * <sb-impersonation-banner nxeShellBanner />\n * <nxe-topbar>...</nxe-topbar>\n * <nxe-sidenav [items]=\"items\" />\n * <router-outlet />\n * </nxe-shell>\n * ```\n *\n * **Slots.** `[nxeShellBanner]` (full width above the topbar, for a banner that must stay on screen),\n * an `nxe-topbar`, an `nxe-sidenav`, and everything else, which goes in the `<main>` landmark. A\n * \"Skip to content\" link, the first tab stop, jumps to that `<main>`.\n *\n * **Responsive.** The shell measures its own width. At or above `breakpoint` (960px by default, the\n * design system's `md`) the sidenav is docked beside the content, expanded or, when `collapsed`, an\n * icon rail. Below it the sidenav is an off-canvas drawer over the content with a scrim: the\n * topbar's toggle button opens it, focus is trapped inside while it is open, Esc and a click on the\n * scrim close it, a navigation closes it, and focus returns to the control that opened it.\n * Widening the shell past the breakpoint closes the drawer.\n *\n * The shell is as tall as `--nxe-shell-height` (default `100dvh`): give it the height of the space\n * it fills, and the content scrolls inside `<main>`.\n */\n@Component({\n selector: 'nxe-shell',\n templateUrl: './nxe-shell.component.html',\n styleUrl: './nxe-shell.component.scss',\n providers: [{ provide: NXE_SHELL, useExisting: forwardRef(() => NxeShellComponent) }],\n host: {\n class: 'nxe-shell',\n '[class.nxe-shell-docked]': \"mode() === 'docked'\",\n '[class.nxe-shell-drawer]': \"mode() === 'drawer'\",\n '[class.nxe-shell-rail]': 'rail()',\n '[class.nxe-shell-drawer-open]': 'drawerShown()',\n '(keydown.escape)': 'onEscape($event)',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class NxeShellComponent implements NxeShellState {\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly messages = inject(NxeMessageService);\n private readonly router = inject(Router);\n private readonly injector = inject(Injector);\n private readonly focusTrapFactory = inject(FocusTrapFactory);\n\n /** Docked sidenav shows only icons. Two-way: `[(collapsed)]`. */\n readonly collapsed = model(false);\n /** The off-canvas drawer is open (only meaningful below the breakpoint). Two-way: `[(drawerOpen)]`. */\n readonly drawerOpen = model(false);\n /** Shell width, in CSS px, from which the sidenav is docked rather than a drawer. */\n readonly breakpoint = input(960, { transform: (v: unknown) => numberAttribute(v, 960) });\n\n /** Skip-link target id; unique per shell. */\n protected readonly mainId = `nxe-shell-main-${nextShellId++}`;\n\n private readonly sidenav = contentChild(NxeSidenavComponent);\n\n /** Own width, kept by a ResizeObserver; the window's until the first measurement. */\n private readonly width = signal(typeof window === 'undefined' ? Number.POSITIVE_INFINITY : window.innerWidth);\n\n readonly mode = computed<NxeShellMode>(() => (this.width() >= this.breakpoint() ? 'docked' : 'drawer'));\n readonly rail = computed(() => this.mode() === 'docked' && this.collapsed());\n readonly drawerShown = computed(() => this.mode() === 'drawer' && this.drawerOpen());\n readonly sidenavId = computed(() => this.sidenav()?.id ?? null);\n\n private trap: FocusTrap | null = null;\n private returnFocusTo: HTMLElement | null = null;\n\n constructor() {\n const destroyRef = inject(DestroyRef);\n afterNextRender(() => {\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n const observer = new ResizeObserver(([entry]) => this.width.set(entry.contentRect.width));\n observer.observe(this.host);\n destroyRef.onDestroy(() => observer.disconnect());\n });\n\n // Widening the shell past the breakpoint is the end of the drawer; do not reopen on narrowing.\n toObservable(this.mode)\n .pipe(takeUntilDestroyed())\n .subscribe((mode) => {\n if (mode === 'docked') {\n this.drawerOpen.set(false);\n }\n });\n\n // Going to another page closes the drawer.\n this.router.events\n .pipe(\n filter((event) => event instanceof NavigationEnd),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.drawerOpen.set(false));\n\n // The trap and the focus return follow the drawer however it was opened or closed.\n toObservable(this.drawerShown)\n .pipe(takeUntilDestroyed())\n .subscribe((shown) => (shown ? this.onDrawerShown() : this.onDrawerHidden()));\n\n destroyRef.onDestroy(() => this.trap?.destroy());\n }\n\n setCollapsed(collapsed: boolean): void {\n this.collapsed.set(collapsed);\n }\n\n /** The hamburger: the drawer below the breakpoint, the dock's collapsed state above it. */\n toggleNavigation(): void {\n if (this.mode() === 'drawer') {\n if (!this.drawerOpen()) {\n this.returnFocusTo = document.activeElement as HTMLElement | null;\n }\n this.drawerOpen.update((open) => !open);\n } else {\n this.collapsed.update((collapsed) => !collapsed);\n }\n }\n\n closeDrawer(): void {\n this.drawerOpen.set(false);\n }\n\n protected t(key: 'shell.skipToContent'): string {\n return this.messages.t(key);\n }\n\n protected skipToContent(event: Event): void {\n event.preventDefault();\n const main = this.host.querySelector<HTMLElement>(`#${this.mainId}`);\n main?.focus();\n main?.scrollIntoView?.({ block: 'start' });\n }\n\n protected onEscape(event: Event): void {\n if (this.drawerShown() && !event.defaultPrevented) {\n event.preventDefault();\n this.closeDrawer();\n }\n }\n\n private onDrawerShown(): void {\n this.returnFocusTo ??= document.activeElement as HTMLElement | null;\n const element = this.sidenav()?.element;\n if (!element) {\n return;\n }\n this.trap?.destroy();\n this.trap = this.focusTrapFactory.create(element);\n // The drawer is still `inert` until this change detection ends; focus after the render.\n afterNextRender(\n () => {\n const current = element.querySelector<HTMLElement>('[aria-current=\"page\"]');\n if (current) {\n current.focus();\n } else {\n this.trap?.focusInitialElement();\n }\n },\n { injector: this.injector },\n );\n }\n\n private onDrawerHidden(): void {\n this.trap?.destroy();\n this.trap = null;\n const target = this.returnFocusTo;\n this.returnFocusTo = null;\n if (target?.isConnected) {\n target.focus();\n }\n }\n}\n","<a class=\"nxe-shell-skip\" [attr.href]=\"'#' + mainId\" (click)=\"skipToContent($event)\">{{ t('shell.skipToContent') }}</a>\n<div class=\"nxe-shell-banner\"><ng-content select=\"[nxeShellBanner]\" /></div>\n<div class=\"nxe-shell-topbar\"><ng-content select=\"nxe-topbar\" /></div>\n<div class=\"nxe-shell-body\">\n <ng-content select=\"nxe-sidenav\" />\n <main class=\"nxe-shell-main\" [id]=\"mainId\" tabindex=\"-1\"><ng-content /></main>\n @if (drawerShown()) {\n <div class=\"nxe-shell-scrim\" aria-hidden=\"true\" (click)=\"closeDrawer()\"></div>\n }\n</div>\n","import { booleanAttribute, ChangeDetectionStrategy, Component, computed, inject, input, output, ViewEncapsulation } from '@angular/core';\nimport { NxeMessageService } from '@nexterajs/ui/core';\nimport { NxeButtonComponent } from '@nexterajs/ui/forms';\nimport { NXE_SHELL } from '../nxe-shell.types';\n\n/**\n * `nxe-topbar` is the bar across the top of an `nxe-shell`: a start, a centre and an end slot.\n *\n * ```html\n * <nxe-topbar>\n * <img nxeTopbarStart src=\"logo.svg\" alt=\"Nextera\" />\n * <sb-search nxeTopbarCenter />\n * <ng-container nxeTopbarEnd>\n * <sb-company-switcher />\n * <sb-notifications />\n * </ng-container>\n * </nxe-topbar>\n * ```\n *\n * The start slot is preceded by a hamburger button: an icon-only `nxe-button` named \"Toggle\n * navigation\" with `aria-controls` the sidenav and `aria-expanded` the sidenav's state. Inside an\n * `nxe-shell` it opens and closes the drawer below the breakpoint and collapses and expands the dock\n * above it. `menuToggle` also fires, so a topbar used without a shell can drive its own sidenav.\n * `[nxeTopbarEnd]` elements sit at the inline end of the bar, where a company switcher, language\n * toggle, notification bell and user menu go.\n */\n@Component({\n selector: 'nxe-topbar',\n imports: [NxeButtonComponent],\n templateUrl: './nxe-topbar.component.html',\n styleUrl: './nxe-topbar.component.scss',\n host: { class: 'nxe-topbar' },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class NxeTopbarComponent {\n private readonly messages = inject(NxeMessageService);\n private readonly shell = inject(NXE_SHELL, { optional: true });\n\n /** Show the hamburger button. Default true. */\n readonly menuButton = input(true, { transform: booleanAttribute });\n /** The hamburger was pressed. */\n readonly menuToggle = output<void>();\n\n protected readonly label = computed(() => this.messages.t('shell.toggleNavigation'));\n protected readonly controls = computed(() => this.shell?.sidenavId() ?? null);\n /** Drawer: open. Dock: not collapsed. Without a shell the button says nothing about it. */\n protected readonly expanded = computed(() => {\n if (!this.shell) {\n return null;\n }\n return this.shell.mode() === 'drawer' ? this.shell.drawerShown() : !this.shell.rail();\n });\n\n protected toggle(): void {\n this.shell?.toggleNavigation();\n this.menuToggle.emit();\n }\n}\n","<div class=\"nxe-topbar-start\">\n @if (menuButton()) {\n <nxe-button\n class=\"nxe-topbar-menu\"\n stylingMode=\"text\"\n [attr.aria-label]=\"label()\"\n [attr.aria-controls]=\"controls()\"\n [attr.aria-expanded]=\"expanded()\"\n (onClick)=\"toggle()\">\n <svg nxeButtonIcon viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M2 3.5h12V5H2zm0 3.75h12v1.5H2zM2 11h12v1.5H2z\" />\n </svg>\n </nxe-button>\n }\n <ng-content select=\"[nxeTopbarStart]\" />\n</div>\n<div class=\"nxe-topbar-center\"><ng-content select=\"[nxeTopbarCenter]\" /></div>\n<div class=\"nxe-topbar-end\"><ng-content select=\"[nxeTopbarEnd]\" /></div>\n","// @nexterajs/ui/shell — public API.\nexport * from './lib/nxe-shell.types';\nexport * from './lib/shell/nxe-shell.component';\nexport * from './lib/sidenav/nxe-sidenav.component';\nexport * from './lib/topbar/nxe-topbar.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, ElementRef, input, output, signal, viewChild, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { NxeMessageService } from '@nexterajs/ui/core';
3
4
 
4
5
  /**
5
6
  * `nxe-tree-view` — drop-in replacement for DevExtreme `dx-tree-view`.
@@ -13,6 +14,8 @@ import { inject, ElementRef, input, output, signal, viewChild, computed, ViewEnc
13
14
  */
14
15
  class NxeTreeViewComponent {
15
16
  elementRef = inject(ElementRef);
17
+ /** The library's runtime-switchable messages. */
18
+ messages = inject(NxeMessageService);
16
19
  dataSource = input([], /* @ts-ignore */
17
20
  ...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
18
21
  /** Accepted for parity; this component always assembles a tree from the flat list. */
@@ -310,7 +313,7 @@ class NxeTreeViewComponent {
310
313
  }
311
314
  }
312
315
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
313
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeTreeViewComponent, isStandalone: true, selector: "nxe-tree-view", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: false, transformFunction: null }, dataStructure: { classPropertyName: "dataStructure", publicName: "dataStructure", isSignal: true, isRequired: false, transformFunction: null }, keyExpr: { classPropertyName: "keyExpr", publicName: "keyExpr", isSignal: true, isRequired: false, transformFunction: null }, displayExpr: { classPropertyName: "displayExpr", publicName: "displayExpr", isSignal: true, isRequired: false, transformFunction: null }, parentIdExpr: { classPropertyName: "parentIdExpr", publicName: "parentIdExpr", isSignal: true, isRequired: false, transformFunction: null }, rootValue: { classPropertyName: "rootValue", publicName: "rootValue", isSignal: true, isRequired: false, transformFunction: null }, expandedExpr: { classPropertyName: "expandedExpr", publicName: "expandedExpr", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selectByClick: { classPropertyName: "selectByClick", publicName: "selectByClick", isSignal: true, isRequired: false, transformFunction: null }, searchEnabled: { classPropertyName: "searchEnabled", publicName: "searchEnabled", isSignal: true, isRequired: false, transformFunction: null }, searchMode: { classPropertyName: "searchMode", publicName: "searchMode", isSignal: true, isRequired: false, transformFunction: null }, useNativeScrolling: { classPropertyName: "useNativeScrolling", publicName: "useNativeScrolling", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onItemClick: "onItemClick" }, host: { properties: { "class.nxe-treeview-with-search": "searchEnabled()", "style.width": "widthStyle()" }, classAttribute: "nxe-widget nxe-tree-view" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (searchEnabled()) {\r\n <!-- dx builds this as a dxTextBox in mode 'search' (ui.search_box_mixin.js), which forces\r\n showClearButton: a magnifier in a 34px slot inside the left border, and a clear button in\r\n a 34px area inside the right border that only exists while there is text to clear. Both\r\n glyphs are drawn here from primitives; the DXIcons font is not used. -->\r\n <div class=\"nxe-treeview-search\">\r\n <!-- The magnifier both search boxes share (dx draws them with one rule,\r\n .dx-searchbox .dx-icon-search): a r4.75 ring and a 45deg handle, 1.9px strokes. Keep\r\n it identical to the header filter's (nxe-header-filter-panel.component.html). -->\r\n <svg class=\"nxe-treeview-search-icon\" viewBox=\"0 0 34 34\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"15\" cy=\"15\" r=\"4.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n <path d=\"M18.5 18.5 23.5 23.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n </svg>\r\n <input\r\n #searchInput\r\n class=\"nxe-treeview-search-input\"\r\n type=\"text\"\r\n placeholder=\"Search\"\r\n aria-label=\"Search\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($event)\" />\r\n @if (searchText()) {\r\n <button\r\n class=\"nxe-treeview-search-clear\"\r\n type=\"button\"\r\n tabindex=\"-1\"\r\n aria-label=\"Clear\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"clearSearch()\">\r\n <!-- The editors' clear glyph: a 16px disc with a 2px x cut out, two 2px bars\r\n each running corner to corner of the 8px square centred on the disc, drawn as\r\n a plus and turned 45deg. -->\r\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" focusable=\"false\">\r\n <path\r\n fill=\"currentColor\"\r\n fill-rule=\"evenodd\"\r\n transform=\"rotate(45 9 9)\"\r\n d=\"M9 1a8 8 0 1 1 0 16A8 8 0 1 1 9 1ZM8 3.343h2V8h4.657v2H10v4.657H8V10H3.343V8H8Z\" />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n}\r\n\r\n<ul\r\n class=\"nxe-treeview-node-container nxe-treeview-node-container-opened\"\r\n role=\"tree\"\r\n [attr.aria-multiselectable]=\"selectionMode() === 'multiple' ? true : null\"\r\n (keydown)=\"onKeydown($event)\">\r\n @for (row of visibleRows(); track row.node.key; let i = $index) {\r\n @let node = row.node;\r\n <li\r\n class=\"nxe-treeview-node nxe-treeview-item-without-checkbox\"\r\n role=\"treeitem\"\r\n [attr.aria-level]=\"node.level + 1\"\r\n [attr.aria-posinset]=\"row.posInSet\"\r\n [attr.aria-setsize]=\"row.setSize\"\r\n [attr.aria-expanded]=\"node.hasChildren ? isExpanded(node) : null\"\r\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : node.key === selectedKey()\"\r\n [attr.tabindex]=\"isTabStop(node, i) ? 0 : -1\"\r\n (focusin)=\"onNodeFocus(node)\"\r\n (focusout)=\"onNodeBlur(node)\"\r\n [class.nxe-treeview-node-is-leaf]=\"!node.hasChildren\"\r\n [class.nxe-state-selected]=\"selectionMode() !== 'none' && node.key === selectedKey()\"\r\n [class.nxe-state-focused]=\"isFocused(node)\"\r\n [style.padding-left.px]=\"node.level * 15\">\r\n <div class=\"nxe-treeview-item\" (click)=\"onNodeClick(node, $event)\">\r\n @if (node.hasChildren) {\r\n <span\r\n class=\"nxe-treeview-toggle-item-visibility\"\r\n [class.nxe-treeview-toggle-item-visibility-opened]=\"isExpanded(node)\"\r\n (click)=\"toggle(node, $event)\">\r\n </span>\r\n } @else {\r\n <span class=\"nxe-treeview-expander-icon-stub\"></span>\r\n }\r\n <div class=\"nxe-treeview-item-content\">\r\n <span>{{ node.text }}</span>\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n</ul>\r\n", styles: ["nxe-tree-view{display:flex;flex-direction:column;height:100%}nxe-tree-view.nxe-treeview-border-visible{border:1px solid var(--nxe-ui-border, #ddd)}nxe-tree-view.nxe-treeview-border-visible>.nxe-treeview-node-container{padding:1px 1px 1px 7px}nxe-tree-view .nxe-treeview-search{position:relative;display:flex;flex:0 0 auto;box-sizing:border-box;margin:0 0 5px;padding:0;border:1px solid var(--nxe-ui-border, #ddd);border-radius:var(--nxe-ui-radius, 4px);background:var(--nxe-ui-surface, #fff)}nxe-tree-view .nxe-treeview-search:hover{border-color:#337ab766}nxe-tree-view .nxe-treeview-search:focus-within{border-color:var(--nxe-ui-accent, #337ab7)}nxe-tree-view .nxe-treeview-search-input{-webkit-appearance:none;appearance:none;flex:1 1 auto;min-width:0;width:100%;box-sizing:border-box;min-height:34px;margin:0;padding:7px 9px 8px;padding-inline-start:34px;border:0;border-radius:var(--nxe-ui-radius, 4px);outline:0;background:transparent;color:var(--nxe-ui-text, #333);font-size:1em}nxe-tree-view .nxe-treeview-search-input::placeholder{color:var(--nxe-ui-text-muted, #999);opacity:1}nxe-tree-view .nxe-treeview-search-icon{position:absolute;top:50%;left:0;display:block;width:34px;height:34px;margin-top:-17px;color:var(--nxe-ui-text-muted, #999);pointer-events:none}nxe-tree-view .nxe-treeview-search-clear{position:relative;display:flex;align-items:center;justify-content:center;flex:0 0 34px;width:34px;min-width:34px;margin:0;padding:0;border:0;background:transparent;color:var(--nxe-ui-text-muted, #999);cursor:pointer}nxe-tree-view .nxe-treeview-search-clear svg{display:block;width:18px;height:18px}nxe-tree-view .nxe-treeview-node-container{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto;list-style:none;margin:0;padding:0}nxe-tree-view .nxe-treeview-node{list-style:none;-webkit-user-select:none;user-select:none}nxe-tree-view .nxe-treeview-node:focus{outline:0}nxe-tree-view .nxe-treeview-item{display:flex;align-items:center;gap:8px;box-sizing:border-box;margin:0;padding:5px 10px;cursor:pointer}nxe-tree-view .nxe-treeview-item-content{flex:1 1 auto;min-width:0}nxe-tree-view .nxe-treeview-item-content span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nxe-tree-view .nxe-treeview-expander-icon-stub{display:flex;flex:0 0 22px;width:22px;height:22px}nxe-tree-view .nxe-treeview-toggle-item-visibility{display:flex;align-items:center;justify-content:center;flex:0 0 22px;width:22px;height:22px;color:var(--nxe-ui-text, #333);cursor:pointer}nxe-tree-view .nxe-treeview-toggle-item-visibility:before{content:\"\";width:0;height:0;border-style:solid;border-width:4px 0 4px 4px;border-color:transparent transparent transparent currentColor}nxe-tree-view .nxe-treeview-toggle-item-visibility.nxe-treeview-toggle-item-visibility-opened:before{content:\"\";border-width:4px 4px 0;border-color:currentColor transparent transparent}nxe-tree-view .nxe-treeview-node.nxe-state-focused>.nxe-treeview-item .nxe-treeview-toggle-item-visibility{color:inherit}:where(nxe-tree-view) .nxe-treeview-item{color:var(--nxe-ui-text, #333)}:where(nxe-tree-view .nxe-treeview-item):hover{background-color:var(--nxe-ui-hover-bg, rgba(0, 0, 0, .04))}:where(nxe-tree-view .nxe-treeview-node.nxe-state-selected)>.nxe-treeview-item{background-color:var(--nxe-ui-selected-bg, rgba(0, 0, 0, .1));color:var(--nxe-ui-text, #333);border-radius:0}:where(nxe-tree-view .nxe-treeview-node.nxe-state-focused)>.nxe-treeview-item{background-color:var(--nxe-ui-accent, #337ab7);color:var(--nxe-ui-accent-contrast, #fff);border-radius:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
316
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeTreeViewComponent, isStandalone: true, selector: "nxe-tree-view", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: false, transformFunction: null }, dataStructure: { classPropertyName: "dataStructure", publicName: "dataStructure", isSignal: true, isRequired: false, transformFunction: null }, keyExpr: { classPropertyName: "keyExpr", publicName: "keyExpr", isSignal: true, isRequired: false, transformFunction: null }, displayExpr: { classPropertyName: "displayExpr", publicName: "displayExpr", isSignal: true, isRequired: false, transformFunction: null }, parentIdExpr: { classPropertyName: "parentIdExpr", publicName: "parentIdExpr", isSignal: true, isRequired: false, transformFunction: null }, rootValue: { classPropertyName: "rootValue", publicName: "rootValue", isSignal: true, isRequired: false, transformFunction: null }, expandedExpr: { classPropertyName: "expandedExpr", publicName: "expandedExpr", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selectByClick: { classPropertyName: "selectByClick", publicName: "selectByClick", isSignal: true, isRequired: false, transformFunction: null }, searchEnabled: { classPropertyName: "searchEnabled", publicName: "searchEnabled", isSignal: true, isRequired: false, transformFunction: null }, searchMode: { classPropertyName: "searchMode", publicName: "searchMode", isSignal: true, isRequired: false, transformFunction: null }, useNativeScrolling: { classPropertyName: "useNativeScrolling", publicName: "useNativeScrolling", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onItemClick: "onItemClick" }, host: { properties: { "class.nxe-treeview-with-search": "searchEnabled()", "style.width": "widthStyle()" }, classAttribute: "nxe-widget nxe-tree-view" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (searchEnabled()) {\r\n <!-- dx builds this as a dxTextBox in mode 'search' (ui.search_box_mixin.js), which forces\r\n showClearButton: a magnifier in a 34px slot inside the left border, and a clear button in\r\n a 34px area inside the right border that only exists while there is text to clear. Both\r\n glyphs are drawn here from primitives; the DXIcons font is not used. -->\r\n <div class=\"nxe-treeview-search\">\r\n <!-- The magnifier both search boxes share (dx draws them with one rule,\r\n .dx-searchbox .dx-icon-search): a r4.75 ring and a 45deg handle, 1.9px strokes. Keep\r\n it identical to the header filter's (nxe-header-filter-panel.component.html). -->\r\n <svg class=\"nxe-treeview-search-icon\" viewBox=\"0 0 34 34\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"15\" cy=\"15\" r=\"4.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n <path d=\"M18.5 18.5 23.5 23.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n </svg>\r\n <input\r\n #searchInput\r\n class=\"nxe-treeview-search-input\"\r\n type=\"text\"\r\n [attr.placeholder]=\"messages.t('common.search')\"\r\n [attr.aria-label]=\"messages.t('common.search')\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($event)\" />\r\n @if (searchText()) {\r\n <button\r\n class=\"nxe-treeview-search-clear\"\r\n type=\"button\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"messages.t('common.clear')\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"clearSearch()\">\r\n <!-- The editors' clear glyph: a 16px disc with a 2px x cut out, two 2px bars\r\n each running corner to corner of the 8px square centred on the disc, drawn as\r\n a plus and turned 45deg. -->\r\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" focusable=\"false\">\r\n <path\r\n fill=\"currentColor\"\r\n fill-rule=\"evenodd\"\r\n transform=\"rotate(45 9 9)\"\r\n d=\"M9 1a8 8 0 1 1 0 16A8 8 0 1 1 9 1ZM8 3.343h2V8h4.657v2H10v4.657H8V10H3.343V8H8Z\" />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n}\r\n\r\n<ul\r\n class=\"nxe-treeview-node-container nxe-treeview-node-container-opened\"\r\n role=\"tree\"\r\n [attr.aria-multiselectable]=\"selectionMode() === 'multiple' ? true : null\"\r\n (keydown)=\"onKeydown($event)\">\r\n @for (row of visibleRows(); track row.node.key; let i = $index) {\r\n @let node = row.node;\r\n <li\r\n class=\"nxe-treeview-node nxe-treeview-item-without-checkbox\"\r\n role=\"treeitem\"\r\n [attr.aria-level]=\"node.level + 1\"\r\n [attr.aria-posinset]=\"row.posInSet\"\r\n [attr.aria-setsize]=\"row.setSize\"\r\n [attr.aria-expanded]=\"node.hasChildren ? isExpanded(node) : null\"\r\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : node.key === selectedKey()\"\r\n [attr.tabindex]=\"isTabStop(node, i) ? 0 : -1\"\r\n (focusin)=\"onNodeFocus(node)\"\r\n (focusout)=\"onNodeBlur(node)\"\r\n [class.nxe-treeview-node-is-leaf]=\"!node.hasChildren\"\r\n [class.nxe-state-selected]=\"selectionMode() !== 'none' && node.key === selectedKey()\"\r\n [class.nxe-state-focused]=\"isFocused(node)\"\r\n [style.padding-left.px]=\"node.level * 15\">\r\n <div class=\"nxe-treeview-item\" (click)=\"onNodeClick(node, $event)\">\r\n @if (node.hasChildren) {\r\n <span\r\n class=\"nxe-treeview-toggle-item-visibility\"\r\n [class.nxe-treeview-toggle-item-visibility-opened]=\"isExpanded(node)\"\r\n (click)=\"toggle(node, $event)\">\r\n </span>\r\n } @else {\r\n <span class=\"nxe-treeview-expander-icon-stub\"></span>\r\n }\r\n <div class=\"nxe-treeview-item-content\">\r\n <span>{{ node.text }}</span>\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n</ul>\r\n", styles: ["nxe-tree-view{display:flex;flex-direction:column;height:100%}nxe-tree-view.nxe-treeview-border-visible{border:1px solid var(--nxe-ui-border, #ddd)}nxe-tree-view.nxe-treeview-border-visible>.nxe-treeview-node-container{padding:1px 1px 1px 7px}nxe-tree-view .nxe-treeview-search{position:relative;display:flex;flex:0 0 auto;box-sizing:border-box;margin:0 0 5px;padding:0;border:1px solid var(--nxe-ui-border, #ddd);border-radius:var(--nxe-ui-radius, 4px);background:var(--nxe-ui-surface, #fff)}nxe-tree-view .nxe-treeview-search:hover{border-color:var(--nxe-ui-accent-faded, rgba(51, 122, 183, .4))}nxe-tree-view .nxe-treeview-search:focus-within{border-color:var(--nxe-ui-accent, #337ab7)}nxe-tree-view .nxe-treeview-search-input{-webkit-appearance:none;appearance:none;flex:1 1 auto;min-width:0;width:100%;box-sizing:border-box;min-height:34px;margin:0;padding:7px 9px 8px;padding-inline-start:34px;border:0;border-radius:var(--nxe-ui-radius, 4px);outline:0;background:transparent;color:var(--nxe-ui-text, #333);font-size:1em}nxe-tree-view .nxe-treeview-search-input::placeholder{color:var(--nxe-ui-text-muted, #999);opacity:1}nxe-tree-view .nxe-treeview-search-icon{position:absolute;top:50%;left:0;display:block;width:34px;height:34px;margin-top:-17px;color:var(--nxe-ui-text-muted, #999);pointer-events:none}nxe-tree-view .nxe-treeview-search-clear{position:relative;display:flex;align-items:center;justify-content:center;flex:0 0 34px;width:34px;min-width:34px;margin:0;padding:0;border:0;background:transparent;color:var(--nxe-ui-text-muted, #999);cursor:pointer}nxe-tree-view .nxe-treeview-search-clear svg{display:block;width:18px;height:18px}nxe-tree-view .nxe-treeview-node-container{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto;list-style:none;margin:0;padding:0}nxe-tree-view .nxe-treeview-node{list-style:none;-webkit-user-select:none;user-select:none}nxe-tree-view .nxe-treeview-node:focus{outline:0}nxe-tree-view .nxe-treeview-item{display:flex;align-items:center;gap:8px;box-sizing:border-box;margin:0;padding:5px 10px;cursor:pointer}nxe-tree-view .nxe-treeview-item-content{flex:1 1 auto;min-width:0}nxe-tree-view .nxe-treeview-item-content span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nxe-tree-view .nxe-treeview-expander-icon-stub{display:flex;flex:0 0 22px;width:22px;height:22px}nxe-tree-view .nxe-treeview-toggle-item-visibility{display:flex;align-items:center;justify-content:center;flex:0 0 22px;width:22px;height:22px;color:var(--nxe-ui-text, #333);cursor:pointer}nxe-tree-view .nxe-treeview-toggle-item-visibility:before{content:\"\";width:0;height:0;border-style:solid;border-width:4px 0 4px 4px;border-color:transparent transparent transparent currentColor}nxe-tree-view .nxe-treeview-toggle-item-visibility.nxe-treeview-toggle-item-visibility-opened:before{content:\"\";border-width:4px 4px 0;border-color:currentColor transparent transparent}nxe-tree-view .nxe-treeview-node.nxe-state-focused>.nxe-treeview-item .nxe-treeview-toggle-item-visibility{color:inherit}:where(nxe-tree-view) .nxe-treeview-item{color:var(--nxe-ui-text, #333)}:where(nxe-tree-view .nxe-treeview-item):hover{background-color:var(--nxe-ui-hover-bg, rgba(0, 0, 0, .04))}:where(nxe-tree-view .nxe-treeview-node.nxe-state-selected)>.nxe-treeview-item{background-color:var(--nxe-ui-selected-bg, rgba(0, 0, 0, .1));color:var(--nxe-ui-text, #333);border-radius:0}:where(nxe-tree-view .nxe-treeview-node.nxe-state-focused)>.nxe-treeview-item{background-color:var(--nxe-ui-accent, #337ab7);color:var(--nxe-ui-accent-contrast, #fff);border-radius:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
314
317
  }
315
318
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeTreeViewComponent, decorators: [{
316
319
  type: Component,
@@ -318,7 +321,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
318
321
  class: 'nxe-widget nxe-tree-view',
319
322
  '[class.nxe-treeview-with-search]': 'searchEnabled()',
320
323
  '[style.width]': 'widthStyle()',
321
- }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "@if (searchEnabled()) {\r\n <!-- dx builds this as a dxTextBox in mode 'search' (ui.search_box_mixin.js), which forces\r\n showClearButton: a magnifier in a 34px slot inside the left border, and a clear button in\r\n a 34px area inside the right border that only exists while there is text to clear. Both\r\n glyphs are drawn here from primitives; the DXIcons font is not used. -->\r\n <div class=\"nxe-treeview-search\">\r\n <!-- The magnifier both search boxes share (dx draws them with one rule,\r\n .dx-searchbox .dx-icon-search): a r4.75 ring and a 45deg handle, 1.9px strokes. Keep\r\n it identical to the header filter's (nxe-header-filter-panel.component.html). -->\r\n <svg class=\"nxe-treeview-search-icon\" viewBox=\"0 0 34 34\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"15\" cy=\"15\" r=\"4.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n <path d=\"M18.5 18.5 23.5 23.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n </svg>\r\n <input\r\n #searchInput\r\n class=\"nxe-treeview-search-input\"\r\n type=\"text\"\r\n placeholder=\"Search\"\r\n aria-label=\"Search\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($event)\" />\r\n @if (searchText()) {\r\n <button\r\n class=\"nxe-treeview-search-clear\"\r\n type=\"button\"\r\n tabindex=\"-1\"\r\n aria-label=\"Clear\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"clearSearch()\">\r\n <!-- The editors' clear glyph: a 16px disc with a 2px x cut out, two 2px bars\r\n each running corner to corner of the 8px square centred on the disc, drawn as\r\n a plus and turned 45deg. -->\r\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" focusable=\"false\">\r\n <path\r\n fill=\"currentColor\"\r\n fill-rule=\"evenodd\"\r\n transform=\"rotate(45 9 9)\"\r\n d=\"M9 1a8 8 0 1 1 0 16A8 8 0 1 1 9 1ZM8 3.343h2V8h4.657v2H10v4.657H8V10H3.343V8H8Z\" />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n}\r\n\r\n<ul\r\n class=\"nxe-treeview-node-container nxe-treeview-node-container-opened\"\r\n role=\"tree\"\r\n [attr.aria-multiselectable]=\"selectionMode() === 'multiple' ? true : null\"\r\n (keydown)=\"onKeydown($event)\">\r\n @for (row of visibleRows(); track row.node.key; let i = $index) {\r\n @let node = row.node;\r\n <li\r\n class=\"nxe-treeview-node nxe-treeview-item-without-checkbox\"\r\n role=\"treeitem\"\r\n [attr.aria-level]=\"node.level + 1\"\r\n [attr.aria-posinset]=\"row.posInSet\"\r\n [attr.aria-setsize]=\"row.setSize\"\r\n [attr.aria-expanded]=\"node.hasChildren ? isExpanded(node) : null\"\r\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : node.key === selectedKey()\"\r\n [attr.tabindex]=\"isTabStop(node, i) ? 0 : -1\"\r\n (focusin)=\"onNodeFocus(node)\"\r\n (focusout)=\"onNodeBlur(node)\"\r\n [class.nxe-treeview-node-is-leaf]=\"!node.hasChildren\"\r\n [class.nxe-state-selected]=\"selectionMode() !== 'none' && node.key === selectedKey()\"\r\n [class.nxe-state-focused]=\"isFocused(node)\"\r\n [style.padding-left.px]=\"node.level * 15\">\r\n <div class=\"nxe-treeview-item\" (click)=\"onNodeClick(node, $event)\">\r\n @if (node.hasChildren) {\r\n <span\r\n class=\"nxe-treeview-toggle-item-visibility\"\r\n [class.nxe-treeview-toggle-item-visibility-opened]=\"isExpanded(node)\"\r\n (click)=\"toggle(node, $event)\">\r\n </span>\r\n } @else {\r\n <span class=\"nxe-treeview-expander-icon-stub\"></span>\r\n }\r\n <div class=\"nxe-treeview-item-content\">\r\n <span>{{ node.text }}</span>\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n</ul>\r\n", styles: ["nxe-tree-view{display:flex;flex-direction:column;height:100%}nxe-tree-view.nxe-treeview-border-visible{border:1px solid var(--nxe-ui-border, #ddd)}nxe-tree-view.nxe-treeview-border-visible>.nxe-treeview-node-container{padding:1px 1px 1px 7px}nxe-tree-view .nxe-treeview-search{position:relative;display:flex;flex:0 0 auto;box-sizing:border-box;margin:0 0 5px;padding:0;border:1px solid var(--nxe-ui-border, #ddd);border-radius:var(--nxe-ui-radius, 4px);background:var(--nxe-ui-surface, #fff)}nxe-tree-view .nxe-treeview-search:hover{border-color:#337ab766}nxe-tree-view .nxe-treeview-search:focus-within{border-color:var(--nxe-ui-accent, #337ab7)}nxe-tree-view .nxe-treeview-search-input{-webkit-appearance:none;appearance:none;flex:1 1 auto;min-width:0;width:100%;box-sizing:border-box;min-height:34px;margin:0;padding:7px 9px 8px;padding-inline-start:34px;border:0;border-radius:var(--nxe-ui-radius, 4px);outline:0;background:transparent;color:var(--nxe-ui-text, #333);font-size:1em}nxe-tree-view .nxe-treeview-search-input::placeholder{color:var(--nxe-ui-text-muted, #999);opacity:1}nxe-tree-view .nxe-treeview-search-icon{position:absolute;top:50%;left:0;display:block;width:34px;height:34px;margin-top:-17px;color:var(--nxe-ui-text-muted, #999);pointer-events:none}nxe-tree-view .nxe-treeview-search-clear{position:relative;display:flex;align-items:center;justify-content:center;flex:0 0 34px;width:34px;min-width:34px;margin:0;padding:0;border:0;background:transparent;color:var(--nxe-ui-text-muted, #999);cursor:pointer}nxe-tree-view .nxe-treeview-search-clear svg{display:block;width:18px;height:18px}nxe-tree-view .nxe-treeview-node-container{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto;list-style:none;margin:0;padding:0}nxe-tree-view .nxe-treeview-node{list-style:none;-webkit-user-select:none;user-select:none}nxe-tree-view .nxe-treeview-node:focus{outline:0}nxe-tree-view .nxe-treeview-item{display:flex;align-items:center;gap:8px;box-sizing:border-box;margin:0;padding:5px 10px;cursor:pointer}nxe-tree-view .nxe-treeview-item-content{flex:1 1 auto;min-width:0}nxe-tree-view .nxe-treeview-item-content span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nxe-tree-view .nxe-treeview-expander-icon-stub{display:flex;flex:0 0 22px;width:22px;height:22px}nxe-tree-view .nxe-treeview-toggle-item-visibility{display:flex;align-items:center;justify-content:center;flex:0 0 22px;width:22px;height:22px;color:var(--nxe-ui-text, #333);cursor:pointer}nxe-tree-view .nxe-treeview-toggle-item-visibility:before{content:\"\";width:0;height:0;border-style:solid;border-width:4px 0 4px 4px;border-color:transparent transparent transparent currentColor}nxe-tree-view .nxe-treeview-toggle-item-visibility.nxe-treeview-toggle-item-visibility-opened:before{content:\"\";border-width:4px 4px 0;border-color:currentColor transparent transparent}nxe-tree-view .nxe-treeview-node.nxe-state-focused>.nxe-treeview-item .nxe-treeview-toggle-item-visibility{color:inherit}:where(nxe-tree-view) .nxe-treeview-item{color:var(--nxe-ui-text, #333)}:where(nxe-tree-view .nxe-treeview-item):hover{background-color:var(--nxe-ui-hover-bg, rgba(0, 0, 0, .04))}:where(nxe-tree-view .nxe-treeview-node.nxe-state-selected)>.nxe-treeview-item{background-color:var(--nxe-ui-selected-bg, rgba(0, 0, 0, .1));color:var(--nxe-ui-text, #333);border-radius:0}:where(nxe-tree-view .nxe-treeview-node.nxe-state-focused)>.nxe-treeview-item{background-color:var(--nxe-ui-accent, #337ab7);color:var(--nxe-ui-accent-contrast, #fff);border-radius:0}\n"] }]
324
+ }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "@if (searchEnabled()) {\r\n <!-- dx builds this as a dxTextBox in mode 'search' (ui.search_box_mixin.js), which forces\r\n showClearButton: a magnifier in a 34px slot inside the left border, and a clear button in\r\n a 34px area inside the right border that only exists while there is text to clear. Both\r\n glyphs are drawn here from primitives; the DXIcons font is not used. -->\r\n <div class=\"nxe-treeview-search\">\r\n <!-- The magnifier both search boxes share (dx draws them with one rule,\r\n .dx-searchbox .dx-icon-search): a r4.75 ring and a 45deg handle, 1.9px strokes. Keep\r\n it identical to the header filter's (nxe-header-filter-panel.component.html). -->\r\n <svg class=\"nxe-treeview-search-icon\" viewBox=\"0 0 34 34\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"15\" cy=\"15\" r=\"4.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n <path d=\"M18.5 18.5 23.5 23.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n </svg>\r\n <input\r\n #searchInput\r\n class=\"nxe-treeview-search-input\"\r\n type=\"text\"\r\n [attr.placeholder]=\"messages.t('common.search')\"\r\n [attr.aria-label]=\"messages.t('common.search')\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($event)\" />\r\n @if (searchText()) {\r\n <button\r\n class=\"nxe-treeview-search-clear\"\r\n type=\"button\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"messages.t('common.clear')\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"clearSearch()\">\r\n <!-- The editors' clear glyph: a 16px disc with a 2px x cut out, two 2px bars\r\n each running corner to corner of the 8px square centred on the disc, drawn as\r\n a plus and turned 45deg. -->\r\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" focusable=\"false\">\r\n <path\r\n fill=\"currentColor\"\r\n fill-rule=\"evenodd\"\r\n transform=\"rotate(45 9 9)\"\r\n d=\"M9 1a8 8 0 1 1 0 16A8 8 0 1 1 9 1ZM8 3.343h2V8h4.657v2H10v4.657H8V10H3.343V8H8Z\" />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n}\r\n\r\n<ul\r\n class=\"nxe-treeview-node-container nxe-treeview-node-container-opened\"\r\n role=\"tree\"\r\n [attr.aria-multiselectable]=\"selectionMode() === 'multiple' ? true : null\"\r\n (keydown)=\"onKeydown($event)\">\r\n @for (row of visibleRows(); track row.node.key; let i = $index) {\r\n @let node = row.node;\r\n <li\r\n class=\"nxe-treeview-node nxe-treeview-item-without-checkbox\"\r\n role=\"treeitem\"\r\n [attr.aria-level]=\"node.level + 1\"\r\n [attr.aria-posinset]=\"row.posInSet\"\r\n [attr.aria-setsize]=\"row.setSize\"\r\n [attr.aria-expanded]=\"node.hasChildren ? isExpanded(node) : null\"\r\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : node.key === selectedKey()\"\r\n [attr.tabindex]=\"isTabStop(node, i) ? 0 : -1\"\r\n (focusin)=\"onNodeFocus(node)\"\r\n (focusout)=\"onNodeBlur(node)\"\r\n [class.nxe-treeview-node-is-leaf]=\"!node.hasChildren\"\r\n [class.nxe-state-selected]=\"selectionMode() !== 'none' && node.key === selectedKey()\"\r\n [class.nxe-state-focused]=\"isFocused(node)\"\r\n [style.padding-left.px]=\"node.level * 15\">\r\n <div class=\"nxe-treeview-item\" (click)=\"onNodeClick(node, $event)\">\r\n @if (node.hasChildren) {\r\n <span\r\n class=\"nxe-treeview-toggle-item-visibility\"\r\n [class.nxe-treeview-toggle-item-visibility-opened]=\"isExpanded(node)\"\r\n (click)=\"toggle(node, $event)\">\r\n </span>\r\n } @else {\r\n <span class=\"nxe-treeview-expander-icon-stub\"></span>\r\n }\r\n <div class=\"nxe-treeview-item-content\">\r\n <span>{{ node.text }}</span>\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n</ul>\r\n", styles: ["nxe-tree-view{display:flex;flex-direction:column;height:100%}nxe-tree-view.nxe-treeview-border-visible{border:1px solid var(--nxe-ui-border, #ddd)}nxe-tree-view.nxe-treeview-border-visible>.nxe-treeview-node-container{padding:1px 1px 1px 7px}nxe-tree-view .nxe-treeview-search{position:relative;display:flex;flex:0 0 auto;box-sizing:border-box;margin:0 0 5px;padding:0;border:1px solid var(--nxe-ui-border, #ddd);border-radius:var(--nxe-ui-radius, 4px);background:var(--nxe-ui-surface, #fff)}nxe-tree-view .nxe-treeview-search:hover{border-color:var(--nxe-ui-accent-faded, rgba(51, 122, 183, .4))}nxe-tree-view .nxe-treeview-search:focus-within{border-color:var(--nxe-ui-accent, #337ab7)}nxe-tree-view .nxe-treeview-search-input{-webkit-appearance:none;appearance:none;flex:1 1 auto;min-width:0;width:100%;box-sizing:border-box;min-height:34px;margin:0;padding:7px 9px 8px;padding-inline-start:34px;border:0;border-radius:var(--nxe-ui-radius, 4px);outline:0;background:transparent;color:var(--nxe-ui-text, #333);font-size:1em}nxe-tree-view .nxe-treeview-search-input::placeholder{color:var(--nxe-ui-text-muted, #999);opacity:1}nxe-tree-view .nxe-treeview-search-icon{position:absolute;top:50%;left:0;display:block;width:34px;height:34px;margin-top:-17px;color:var(--nxe-ui-text-muted, #999);pointer-events:none}nxe-tree-view .nxe-treeview-search-clear{position:relative;display:flex;align-items:center;justify-content:center;flex:0 0 34px;width:34px;min-width:34px;margin:0;padding:0;border:0;background:transparent;color:var(--nxe-ui-text-muted, #999);cursor:pointer}nxe-tree-view .nxe-treeview-search-clear svg{display:block;width:18px;height:18px}nxe-tree-view .nxe-treeview-node-container{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto;list-style:none;margin:0;padding:0}nxe-tree-view .nxe-treeview-node{list-style:none;-webkit-user-select:none;user-select:none}nxe-tree-view .nxe-treeview-node:focus{outline:0}nxe-tree-view .nxe-treeview-item{display:flex;align-items:center;gap:8px;box-sizing:border-box;margin:0;padding:5px 10px;cursor:pointer}nxe-tree-view .nxe-treeview-item-content{flex:1 1 auto;min-width:0}nxe-tree-view .nxe-treeview-item-content span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nxe-tree-view .nxe-treeview-expander-icon-stub{display:flex;flex:0 0 22px;width:22px;height:22px}nxe-tree-view .nxe-treeview-toggle-item-visibility{display:flex;align-items:center;justify-content:center;flex:0 0 22px;width:22px;height:22px;color:var(--nxe-ui-text, #333);cursor:pointer}nxe-tree-view .nxe-treeview-toggle-item-visibility:before{content:\"\";width:0;height:0;border-style:solid;border-width:4px 0 4px 4px;border-color:transparent transparent transparent currentColor}nxe-tree-view .nxe-treeview-toggle-item-visibility.nxe-treeview-toggle-item-visibility-opened:before{content:\"\";border-width:4px 4px 0;border-color:currentColor transparent transparent}nxe-tree-view .nxe-treeview-node.nxe-state-focused>.nxe-treeview-item .nxe-treeview-toggle-item-visibility{color:inherit}:where(nxe-tree-view) .nxe-treeview-item{color:var(--nxe-ui-text, #333)}:where(nxe-tree-view .nxe-treeview-item):hover{background-color:var(--nxe-ui-hover-bg, rgba(0, 0, 0, .04))}:where(nxe-tree-view .nxe-treeview-node.nxe-state-selected)>.nxe-treeview-item{background-color:var(--nxe-ui-selected-bg, rgba(0, 0, 0, .1));color:var(--nxe-ui-text, #333);border-radius:0}:where(nxe-tree-view .nxe-treeview-node.nxe-state-focused)>.nxe-treeview-item{background-color:var(--nxe-ui-accent, #337ab7);color:var(--nxe-ui-accent-contrast, #fff);border-radius:0}\n"] }]
322
325
  }], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: false }] }], dataStructure: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataStructure", required: false }] }], keyExpr: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyExpr", required: false }] }], displayExpr: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayExpr", required: false }] }], parentIdExpr: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentIdExpr", required: false }] }], rootValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootValue", required: false }] }], expandedExpr: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedExpr", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selectByClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectByClick", required: false }] }], searchEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchEnabled", required: false }] }], searchMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchMode", required: false }] }], useNativeScrolling: [{ type: i0.Input, args: [{ isSignal: true, alias: "useNativeScrolling", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], onItemClick: [{ type: i0.Output, args: ["onItemClick"] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
323
326
 
324
327
  // @nexterajs/ui/tree-view — public API.
@@ -1 +1 @@
1
- {"version":3,"file":"nexterajs-ui-tree-view.mjs","sources":["../../../projects/nextera-ui/tree-view/src/lib/tree-view/nxe-tree-view.component.ts","../../../projects/nextera-ui/tree-view/src/lib/tree-view/nxe-tree-view.component.html","../../../projects/nextera-ui/tree-view/src/public-api.ts","../../../projects/nextera-ui/tree-view/src/nexterajs-ui-tree-view.ts"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n ElementRef,\r\n inject,\r\n input,\r\n output,\r\n signal,\r\n viewChild,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\n\r\n/** A node in the assembled tree (children built from the flat `parentIdExpr` list). */\r\nexport interface NxeTreeNode {\r\n key: unknown;\r\n text: string;\r\n /** The original data row (consumers read `itemData` off the click event = this). */\r\n data: Record<string, unknown>;\r\n children: NxeTreeNode[];\r\n hasChildren: boolean;\r\n level: number;\r\n}\r\n\r\n/** A visible node with the sibling position a flattened tree has to state explicitly. */\r\nexport interface NxeTreeRow {\r\n node: NxeTreeNode;\r\n posInSet: number;\r\n setSize: number;\r\n}\r\n\r\n/** DevExtreme-shaped `onItemClick` payload (consumers read `event.itemData.<field>`). */\r\nexport interface NxeTreeItemClickEvent {\r\n itemData: Record<string, unknown>;\r\n node: NxeTreeNode;\r\n event: Event;\r\n component: NxeTreeViewComponent;\r\n element: HTMLElement;\r\n}\r\n\r\n/**\r\n * `nxe-tree-view` — drop-in replacement for DevExtreme `dx-tree-view`.\r\n *\r\n * The single usage (settings sidebar) feeds a FLAT list (`dataStructure=\"plain\"`) and lets the\r\n * widget assemble the hierarchy via `keyExpr`/`parentIdExpr`/`rootValue`; so this builds the tree\r\n * internally. Single selection, `selectByClick`, optional search box, and a programmatic\r\n * `expandAll()` (called via `@ViewChild(NxeTreeViewComponent).instance.expandAll()` after async\r\n * load). `(onItemClick)` emits `{ itemData: <full original row>, ... }` so the navigation handler\r\n * keeps reading `event.itemData.url/urlParams/objectName`.\r\n */\r\n@Component({\r\n selector: 'nxe-tree-view',\r\n standalone: true,\r\n templateUrl: './nxe-tree-view.component.html',\r\n styleUrl: './nxe-tree-view.component.scss',\r\n host: {\r\n class: 'nxe-widget nxe-tree-view',\r\n '[class.nxe-treeview-with-search]': 'searchEnabled()',\r\n '[style.width]': 'widthStyle()',\r\n },\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class NxeTreeViewComponent {\r\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\r\n\r\n readonly dataSource = input<readonly unknown[] | null | undefined>([]);\r\n /** Accepted for parity; this component always assembles a tree from the flat list. */\r\n readonly dataStructure = input<'plain' | 'tree'>('plain');\r\n readonly keyExpr = input<string>('id');\r\n readonly displayExpr = input<string>('text');\r\n readonly parentIdExpr = input<string>('parentId');\r\n readonly rootValue = input<unknown>(0);\r\n /** Accepted for parity (no-op: rows carry no `expanded` field; use `expandAll()`). */\r\n readonly expandedExpr = input<string>('expanded');\r\n readonly selectionMode = input<'single' | 'multiple' | 'none'>('single');\r\n readonly selectByClick = input(false);\r\n readonly searchEnabled = input(false);\r\n readonly searchMode = input<'contains' | 'startswith' | 'equals'>('contains');\r\n /** Accepted for parity (native scrolling is the default here anyway). */\r\n readonly useNativeScrolling = input(true);\r\n readonly width = input<number | string | undefined>(undefined);\r\n\r\n readonly onItemClick = output<NxeTreeItemClickEvent>();\r\n\r\n readonly expanded = signal<ReadonlySet<unknown>>(new Set());\r\n readonly selectedKey = signal<unknown>(null);\r\n readonly searchText = signal('');\r\n\r\n private readonly searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput');\r\n\r\n /** DevExtreme `.instance` facade — `expandAll()` is the only method used. */\r\n get instance(): this {\r\n return this;\r\n }\r\n\r\n protected readonly widthStyle = computed(() => {\r\n const w = this.width();\r\n return typeof w === 'number' ? `${w}px` : w;\r\n });\r\n\r\n private readonly tree = computed<NxeTreeNode[]>(() => {\r\n const rows = (this.dataSource() ?? []) as Record<string, unknown>[];\r\n const keyF = this.keyExpr();\r\n const parentF = this.parentIdExpr();\r\n const dispF = this.displayExpr();\r\n const root = this.rootValue();\r\n\r\n const byKey = new Map<unknown, NxeTreeNode>();\r\n for (const r of rows) {\r\n byKey.set(r[keyF], {\r\n key: r[keyF],\r\n text: String(r[dispF] ?? ''),\r\n data: r,\r\n children: [],\r\n hasChildren: false,\r\n level: 0,\r\n });\r\n }\r\n const roots: NxeTreeNode[] = [];\r\n for (const r of rows) {\r\n const node = byKey.get(r[keyF])!;\r\n const parentKey = r[parentF];\r\n const parent = parentKey !== root ? byKey.get(parentKey) : undefined;\r\n if (parent) {\r\n parent.children.push(node);\r\n parent.hasChildren = true;\r\n } else {\r\n roots.push(node);\r\n }\r\n }\r\n const assignLevels = (nodes: NxeTreeNode[], level: number): void => {\r\n for (const n of nodes) {\r\n n.level = level;\r\n assignLevels(n.children, level + 1);\r\n }\r\n };\r\n assignLevels(roots, 0);\r\n return roots;\r\n });\r\n\r\n /**\r\n * The visible nodes, each with its position among its siblings.\r\n *\r\n * The tree renders as one flat `<ul>` with indentation rather than nested lists, so a screen\r\n * reader cannot infer depth or sibling counts from the DOM. `aria-level`, `aria-setsize` and\r\n * `aria-posinset` are what carry that, which makes them required for a flattened tree rather\r\n * than optional extras. They are computed during the walk, where the sibling array is already\r\n * in hand — reconstructing them from the flat list afterwards is possible but much harder to\r\n * read for no gain.\r\n */\r\n protected readonly visibleRows = computed<NxeTreeRow[]>(() => {\r\n const out: NxeTreeRow[] = [];\r\n const query = this.searchText().trim().toLowerCase();\r\n\r\n if (query) {\r\n const match = (text: string): boolean => {\r\n const t = text.toLowerCase();\r\n switch (this.searchMode()) {\r\n case 'startswith':\r\n return t.startsWith(query);\r\n case 'equals':\r\n return t === query;\r\n default:\r\n return t.includes(query);\r\n }\r\n };\r\n const matches: NxeTreeNode[] = [];\r\n const walkAll = (nodes: NxeTreeNode[]): void => {\r\n for (const n of nodes) {\r\n if (match(n.text)) {\r\n matches.push(n);\r\n }\r\n walkAll(n.children);\r\n }\r\n };\r\n walkAll(this.tree());\r\n // Search flattens the hierarchy, so the results ARE the set: reporting each match\r\n // with its original sibling count would describe a structure that is not on screen.\r\n return matches.map((node, i) => ({ node, posInSet: i + 1, setSize: matches.length }));\r\n }\r\n\r\n const expanded = this.expanded();\r\n const walk = (nodes: NxeTreeNode[]): void => {\r\n nodes.forEach((n, i) => {\r\n out.push({ node: n, posInSet: i + 1, setSize: nodes.length });\r\n if (n.hasChildren && expanded.has(n.key)) {\r\n walk(n.children);\r\n }\r\n });\r\n };\r\n walk(this.tree());\r\n return out;\r\n });\r\n\r\n /** Flattened list of currently-visible nodes (respects expand state + search). */\r\n protected readonly visibleNodes = computed<NxeTreeNode[]>(() =>\r\n this.visibleRows().map((row) => row.node),\r\n );\r\n\r\n expandAll(): void {\r\n const keys = new Set<unknown>();\r\n const walk = (nodes: NxeTreeNode[]): void => {\r\n for (const n of nodes) {\r\n if (n.hasChildren) {\r\n keys.add(n.key);\r\n }\r\n walk(n.children);\r\n }\r\n };\r\n walk(this.tree());\r\n this.expanded.set(keys);\r\n }\r\n\r\n collapseAll(): void {\r\n this.expanded.set(new Set());\r\n }\r\n\r\n protected isExpanded(node: NxeTreeNode): boolean {\r\n return this.expanded().has(node.key);\r\n }\r\n\r\n protected toggle(node: NxeTreeNode, event: Event): void {\r\n event.stopPropagation();\r\n this.expanded.update((set) => {\r\n const next = new Set(set);\r\n next.has(node.key) ? next.delete(node.key) : next.add(node.key);\r\n return next;\r\n });\r\n }\r\n\r\n protected onNodeClick(node: NxeTreeNode, event: Event): void {\r\n if (this.selectByClick() && this.selectionMode() !== 'none') {\r\n this.selectedKey.set(node.key);\r\n }\r\n this.onItemClick.emit({\r\n itemData: node.data,\r\n node,\r\n event,\r\n component: this,\r\n element: event.currentTarget as HTMLElement,\r\n });\r\n }\r\n\r\n protected onSearch(event: Event): void {\r\n this.searchText.set((event.target as HTMLInputElement).value);\r\n }\r\n\r\n /**\r\n * The search box's clear button. dx empties the value and leaves the caret in the box (its\r\n * clear button swallows the mouse-down so the input never loses focus); refocusing here covers\r\n * the case where focus had already left the input.\r\n */\r\n protected clearSearch(): void {\r\n this.searchText.set('');\r\n this.searchInput()?.nativeElement.focus();\r\n }\r\n\r\n /**\r\n * The node holding the tree's single tab stop.\r\n *\r\n * A tree is one stop in the tab order, not one per node, so a roving tabindex is part of the\r\n * role rather than an extra: `role=\"tree\"` on a list nothing can focus would announce a\r\n * structure a keyboard user cannot reach.\r\n */\r\n protected readonly focusedKey = signal<unknown>(null);\r\n\r\n protected isTabStop(node: NxeTreeNode, index: number): boolean {\r\n const focused = this.focusedKey();\r\n return focused === null ? index === 0 : focused === node.key;\r\n }\r\n\r\n /**\r\n * The node that holds DOM focus right now, or `null` while focus is anywhere else.\r\n *\r\n * Deliberately not `focusedKey`: that is the roving tab stop and has to outlive a blur, so Tab\r\n * comes back to the same node. dx drops `dx-state-focused` the moment focus leaves the node,\r\n * and the focused look goes with it — the row colour, and the toggle, which follows the row's\r\n * colour only while focused (`color: unset`, dx.light.css). Keyed off focusin/focusout so the\r\n * class means exactly what it says.\r\n */\r\n private readonly focusedNode = signal<NxeTreeNode | null>(null);\r\n\r\n protected isFocused(node: NxeTreeNode): boolean {\r\n const focused = this.focusedNode();\r\n return focused !== null && focused.key === node.key;\r\n }\r\n\r\n protected onNodeFocus(node: NxeTreeNode): void {\r\n this.focusedKey.set(node.key);\r\n this.focusedNode.set(node);\r\n }\r\n\r\n protected onNodeBlur(node: NxeTreeNode): void {\r\n // focusout on the old node fires before focusin on the new one, so this only ever clears\r\n // the node that is actually losing focus.\r\n if (this.isFocused(node)) {\r\n this.focusedNode.set(null);\r\n }\r\n }\r\n\r\n /** Arrow-key navigation, per the WAI-ARIA tree pattern. */\r\n protected onKeydown(event: KeyboardEvent): void {\r\n const rows = this.visibleRows();\r\n if (!rows.length) {\r\n return;\r\n }\r\n const current = Math.max(\r\n 0,\r\n rows.findIndex((r) => r.node.key === this.focusedKey()),\r\n );\r\n const node = rows[current].node;\r\n const expanded = this.isExpanded(node);\r\n\r\n const focusAt = (index: number): void => {\r\n const target = rows[Math.max(0, Math.min(index, rows.length - 1))];\r\n this.focusedKey.set(target.node.key);\r\n const items = (this.elementRef.nativeElement as HTMLElement).querySelectorAll<HTMLElement>(\r\n '[role=\"treeitem\"]',\r\n );\r\n items[rows.indexOf(target)]?.focus();\r\n };\r\n\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n event.preventDefault();\r\n focusAt(current + 1);\r\n break;\r\n case 'ArrowUp':\r\n event.preventDefault();\r\n focusAt(current - 1);\r\n break;\r\n case 'ArrowRight':\r\n event.preventDefault();\r\n if (node.hasChildren && !expanded) {\r\n this.toggle(node, event);\r\n } else if (node.hasChildren) {\r\n focusAt(current + 1);\r\n }\r\n break;\r\n case 'ArrowLeft':\r\n event.preventDefault();\r\n if (node.hasChildren && expanded) {\r\n this.toggle(node, event);\r\n } else {\r\n // to the parent: the nearest earlier row one level up\r\n for (let i = current - 1; i >= 0; i--) {\r\n if (rows[i].node.level < node.level) {\r\n focusAt(i);\r\n break;\r\n }\r\n }\r\n }\r\n break;\r\n case 'Home':\r\n event.preventDefault();\r\n focusAt(0);\r\n break;\r\n case 'End':\r\n event.preventDefault();\r\n focusAt(rows.length - 1);\r\n break;\r\n case 'Enter':\r\n case ' ':\r\n event.preventDefault();\r\n this.onNodeClick(node, event);\r\n break;\r\n default:\r\n }\r\n }\r\n}\r\n","@if (searchEnabled()) {\r\n <!-- dx builds this as a dxTextBox in mode 'search' (ui.search_box_mixin.js), which forces\r\n showClearButton: a magnifier in a 34px slot inside the left border, and a clear button in\r\n a 34px area inside the right border that only exists while there is text to clear. Both\r\n glyphs are drawn here from primitives; the DXIcons font is not used. -->\r\n <div class=\"nxe-treeview-search\">\r\n <!-- The magnifier both search boxes share (dx draws them with one rule,\r\n .dx-searchbox .dx-icon-search): a r4.75 ring and a 45deg handle, 1.9px strokes. Keep\r\n it identical to the header filter's (nxe-header-filter-panel.component.html). -->\r\n <svg class=\"nxe-treeview-search-icon\" viewBox=\"0 0 34 34\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"15\" cy=\"15\" r=\"4.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n <path d=\"M18.5 18.5 23.5 23.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n </svg>\r\n <input\r\n #searchInput\r\n class=\"nxe-treeview-search-input\"\r\n type=\"text\"\r\n placeholder=\"Search\"\r\n aria-label=\"Search\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($event)\" />\r\n @if (searchText()) {\r\n <button\r\n class=\"nxe-treeview-search-clear\"\r\n type=\"button\"\r\n tabindex=\"-1\"\r\n aria-label=\"Clear\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"clearSearch()\">\r\n <!-- The editors' clear glyph: a 16px disc with a 2px x cut out, two 2px bars\r\n each running corner to corner of the 8px square centred on the disc, drawn as\r\n a plus and turned 45deg. -->\r\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" focusable=\"false\">\r\n <path\r\n fill=\"currentColor\"\r\n fill-rule=\"evenodd\"\r\n transform=\"rotate(45 9 9)\"\r\n d=\"M9 1a8 8 0 1 1 0 16A8 8 0 1 1 9 1ZM8 3.343h2V8h4.657v2H10v4.657H8V10H3.343V8H8Z\" />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n}\r\n\r\n<ul\r\n class=\"nxe-treeview-node-container nxe-treeview-node-container-opened\"\r\n role=\"tree\"\r\n [attr.aria-multiselectable]=\"selectionMode() === 'multiple' ? true : null\"\r\n (keydown)=\"onKeydown($event)\">\r\n @for (row of visibleRows(); track row.node.key; let i = $index) {\r\n @let node = row.node;\r\n <li\r\n class=\"nxe-treeview-node nxe-treeview-item-without-checkbox\"\r\n role=\"treeitem\"\r\n [attr.aria-level]=\"node.level + 1\"\r\n [attr.aria-posinset]=\"row.posInSet\"\r\n [attr.aria-setsize]=\"row.setSize\"\r\n [attr.aria-expanded]=\"node.hasChildren ? isExpanded(node) : null\"\r\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : node.key === selectedKey()\"\r\n [attr.tabindex]=\"isTabStop(node, i) ? 0 : -1\"\r\n (focusin)=\"onNodeFocus(node)\"\r\n (focusout)=\"onNodeBlur(node)\"\r\n [class.nxe-treeview-node-is-leaf]=\"!node.hasChildren\"\r\n [class.nxe-state-selected]=\"selectionMode() !== 'none' && node.key === selectedKey()\"\r\n [class.nxe-state-focused]=\"isFocused(node)\"\r\n [style.padding-left.px]=\"node.level * 15\">\r\n <div class=\"nxe-treeview-item\" (click)=\"onNodeClick(node, $event)\">\r\n @if (node.hasChildren) {\r\n <span\r\n class=\"nxe-treeview-toggle-item-visibility\"\r\n [class.nxe-treeview-toggle-item-visibility-opened]=\"isExpanded(node)\"\r\n (click)=\"toggle(node, $event)\">\r\n </span>\r\n } @else {\r\n <span class=\"nxe-treeview-expander-icon-stub\"></span>\r\n }\r\n <div class=\"nxe-treeview-item-content\">\r\n <span>{{ node.text }}</span>\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n</ul>\r\n","// @nexterajs/ui/tree-view — public API.\r\nexport * from './lib/tree-view/nxe-tree-view.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nexterajs-ui-tree-view.mjs","sources":["../../../projects/nextera-ui/tree-view/src/lib/tree-view/nxe-tree-view.component.ts","../../../projects/nextera-ui/tree-view/src/lib/tree-view/nxe-tree-view.component.html","../../../projects/nextera-ui/tree-view/src/public-api.ts","../../../projects/nextera-ui/tree-view/src/nexterajs-ui-tree-view.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n inject,\n input,\n output,\n signal,\n viewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { NxeMessageService } from '@nexterajs/ui/core';\n\n/** A node in the assembled tree (children built from the flat `parentIdExpr` list). */\nexport interface NxeTreeNode {\n key: unknown;\n text: string;\n /** The original data row (consumers read `itemData` off the click event = this). */\n data: Record<string, unknown>;\n children: NxeTreeNode[];\n hasChildren: boolean;\n level: number;\n}\n\n/** A visible node with the sibling position a flattened tree has to state explicitly. */\nexport interface NxeTreeRow {\n node: NxeTreeNode;\n posInSet: number;\n setSize: number;\n}\n\n/** DevExtreme-shaped `onItemClick` payload (consumers read `event.itemData.<field>`). */\nexport interface NxeTreeItemClickEvent {\n itemData: Record<string, unknown>;\n node: NxeTreeNode;\n event: Event;\n component: NxeTreeViewComponent;\n element: HTMLElement;\n}\n\n/**\n * `nxe-tree-view` — drop-in replacement for DevExtreme `dx-tree-view`.\n *\n * The single usage (settings sidebar) feeds a FLAT list (`dataStructure=\"plain\"`) and lets the\n * widget assemble the hierarchy via `keyExpr`/`parentIdExpr`/`rootValue`; so this builds the tree\n * internally. Single selection, `selectByClick`, optional search box, and a programmatic\n * `expandAll()` (called via `@ViewChild(NxeTreeViewComponent).instance.expandAll()` after async\n * load). `(onItemClick)` emits `{ itemData: <full original row>, ... }` so the navigation handler\n * keeps reading `event.itemData.url/urlParams/objectName`.\n */\n@Component({\n selector: 'nxe-tree-view',\n standalone: true,\n templateUrl: './nxe-tree-view.component.html',\n styleUrl: './nxe-tree-view.component.scss',\n host: {\n class: 'nxe-widget nxe-tree-view',\n '[class.nxe-treeview-with-search]': 'searchEnabled()',\n '[style.width]': 'widthStyle()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class NxeTreeViewComponent {\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n /** The library's runtime-switchable messages. */\n protected readonly messages = inject(NxeMessageService);\n\n readonly dataSource = input<readonly unknown[] | null | undefined>([]);\n /** Accepted for parity; this component always assembles a tree from the flat list. */\n readonly dataStructure = input<'plain' | 'tree'>('plain');\n readonly keyExpr = input<string>('id');\n readonly displayExpr = input<string>('text');\n readonly parentIdExpr = input<string>('parentId');\n readonly rootValue = input<unknown>(0);\n /** Accepted for parity (no-op: rows carry no `expanded` field; use `expandAll()`). */\n readonly expandedExpr = input<string>('expanded');\n readonly selectionMode = input<'single' | 'multiple' | 'none'>('single');\n readonly selectByClick = input(false);\n readonly searchEnabled = input(false);\n readonly searchMode = input<'contains' | 'startswith' | 'equals'>('contains');\n /** Accepted for parity (native scrolling is the default here anyway). */\n readonly useNativeScrolling = input(true);\n readonly width = input<number | string | undefined>(undefined);\n\n readonly onItemClick = output<NxeTreeItemClickEvent>();\n\n readonly expanded = signal<ReadonlySet<unknown>>(new Set());\n readonly selectedKey = signal<unknown>(null);\n readonly searchText = signal('');\n\n private readonly searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput');\n\n /** DevExtreme `.instance` facade — `expandAll()` is the only method used. */\n get instance(): this {\n return this;\n }\n\n protected readonly widthStyle = computed(() => {\n const w = this.width();\n return typeof w === 'number' ? `${w}px` : w;\n });\n\n private readonly tree = computed<NxeTreeNode[]>(() => {\n const rows = (this.dataSource() ?? []) as Record<string, unknown>[];\n const keyF = this.keyExpr();\n const parentF = this.parentIdExpr();\n const dispF = this.displayExpr();\n const root = this.rootValue();\n\n const byKey = new Map<unknown, NxeTreeNode>();\n for (const r of rows) {\n byKey.set(r[keyF], {\n key: r[keyF],\n text: String(r[dispF] ?? ''),\n data: r,\n children: [],\n hasChildren: false,\n level: 0,\n });\n }\n const roots: NxeTreeNode[] = [];\n for (const r of rows) {\n const node = byKey.get(r[keyF])!;\n const parentKey = r[parentF];\n const parent = parentKey !== root ? byKey.get(parentKey) : undefined;\n if (parent) {\n parent.children.push(node);\n parent.hasChildren = true;\n } else {\n roots.push(node);\n }\n }\n const assignLevels = (nodes: NxeTreeNode[], level: number): void => {\n for (const n of nodes) {\n n.level = level;\n assignLevels(n.children, level + 1);\n }\n };\n assignLevels(roots, 0);\n return roots;\n });\n\n /**\n * The visible nodes, each with its position among its siblings.\n *\n * The tree renders as one flat `<ul>` with indentation rather than nested lists, so a screen\n * reader cannot infer depth or sibling counts from the DOM. `aria-level`, `aria-setsize` and\n * `aria-posinset` are what carry that, which makes them required for a flattened tree rather\n * than optional extras. They are computed during the walk, where the sibling array is already\n * in hand — reconstructing them from the flat list afterwards is possible but much harder to\n * read for no gain.\n */\n protected readonly visibleRows = computed<NxeTreeRow[]>(() => {\n const out: NxeTreeRow[] = [];\n const query = this.searchText().trim().toLowerCase();\n\n if (query) {\n const match = (text: string): boolean => {\n const t = text.toLowerCase();\n switch (this.searchMode()) {\n case 'startswith':\n return t.startsWith(query);\n case 'equals':\n return t === query;\n default:\n return t.includes(query);\n }\n };\n const matches: NxeTreeNode[] = [];\n const walkAll = (nodes: NxeTreeNode[]): void => {\n for (const n of nodes) {\n if (match(n.text)) {\n matches.push(n);\n }\n walkAll(n.children);\n }\n };\n walkAll(this.tree());\n // Search flattens the hierarchy, so the results ARE the set: reporting each match\n // with its original sibling count would describe a structure that is not on screen.\n return matches.map((node, i) => ({ node, posInSet: i + 1, setSize: matches.length }));\n }\n\n const expanded = this.expanded();\n const walk = (nodes: NxeTreeNode[]): void => {\n nodes.forEach((n, i) => {\n out.push({ node: n, posInSet: i + 1, setSize: nodes.length });\n if (n.hasChildren && expanded.has(n.key)) {\n walk(n.children);\n }\n });\n };\n walk(this.tree());\n return out;\n });\n\n /** Flattened list of currently-visible nodes (respects expand state + search). */\n protected readonly visibleNodes = computed<NxeTreeNode[]>(() =>\n this.visibleRows().map((row) => row.node),\n );\n\n expandAll(): void {\n const keys = new Set<unknown>();\n const walk = (nodes: NxeTreeNode[]): void => {\n for (const n of nodes) {\n if (n.hasChildren) {\n keys.add(n.key);\n }\n walk(n.children);\n }\n };\n walk(this.tree());\n this.expanded.set(keys);\n }\n\n collapseAll(): void {\n this.expanded.set(new Set());\n }\n\n protected isExpanded(node: NxeTreeNode): boolean {\n return this.expanded().has(node.key);\n }\n\n protected toggle(node: NxeTreeNode, event: Event): void {\n event.stopPropagation();\n this.expanded.update((set) => {\n const next = new Set(set);\n next.has(node.key) ? next.delete(node.key) : next.add(node.key);\n return next;\n });\n }\n\n protected onNodeClick(node: NxeTreeNode, event: Event): void {\n if (this.selectByClick() && this.selectionMode() !== 'none') {\n this.selectedKey.set(node.key);\n }\n this.onItemClick.emit({\n itemData: node.data,\n node,\n event,\n component: this,\n element: event.currentTarget as HTMLElement,\n });\n }\n\n protected onSearch(event: Event): void {\n this.searchText.set((event.target as HTMLInputElement).value);\n }\n\n /**\n * The search box's clear button. dx empties the value and leaves the caret in the box (its\n * clear button swallows the mouse-down so the input never loses focus); refocusing here covers\n * the case where focus had already left the input.\n */\n protected clearSearch(): void {\n this.searchText.set('');\n this.searchInput()?.nativeElement.focus();\n }\n\n /**\n * The node holding the tree's single tab stop.\n *\n * A tree is one stop in the tab order, not one per node, so a roving tabindex is part of the\n * role rather than an extra: `role=\"tree\"` on a list nothing can focus would announce a\n * structure a keyboard user cannot reach.\n */\n protected readonly focusedKey = signal<unknown>(null);\n\n protected isTabStop(node: NxeTreeNode, index: number): boolean {\n const focused = this.focusedKey();\n return focused === null ? index === 0 : focused === node.key;\n }\n\n /**\n * The node that holds DOM focus right now, or `null` while focus is anywhere else.\n *\n * Deliberately not `focusedKey`: that is the roving tab stop and has to outlive a blur, so Tab\n * comes back to the same node. dx drops `dx-state-focused` the moment focus leaves the node,\n * and the focused look goes with it — the row colour, and the toggle, which follows the row's\n * colour only while focused (`color: unset`, dx.light.css). Keyed off focusin/focusout so the\n * class means exactly what it says.\n */\n private readonly focusedNode = signal<NxeTreeNode | null>(null);\n\n protected isFocused(node: NxeTreeNode): boolean {\n const focused = this.focusedNode();\n return focused !== null && focused.key === node.key;\n }\n\n protected onNodeFocus(node: NxeTreeNode): void {\n this.focusedKey.set(node.key);\n this.focusedNode.set(node);\n }\n\n protected onNodeBlur(node: NxeTreeNode): void {\n // focusout on the old node fires before focusin on the new one, so this only ever clears\n // the node that is actually losing focus.\n if (this.isFocused(node)) {\n this.focusedNode.set(null);\n }\n }\n\n /** Arrow-key navigation, per the WAI-ARIA tree pattern. */\n protected onKeydown(event: KeyboardEvent): void {\n const rows = this.visibleRows();\n if (!rows.length) {\n return;\n }\n const current = Math.max(\n 0,\n rows.findIndex((r) => r.node.key === this.focusedKey()),\n );\n const node = rows[current].node;\n const expanded = this.isExpanded(node);\n\n const focusAt = (index: number): void => {\n const target = rows[Math.max(0, Math.min(index, rows.length - 1))];\n this.focusedKey.set(target.node.key);\n const items = (this.elementRef.nativeElement as HTMLElement).querySelectorAll<HTMLElement>(\n '[role=\"treeitem\"]',\n );\n items[rows.indexOf(target)]?.focus();\n };\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n focusAt(current + 1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n focusAt(current - 1);\n break;\n case 'ArrowRight':\n event.preventDefault();\n if (node.hasChildren && !expanded) {\n this.toggle(node, event);\n } else if (node.hasChildren) {\n focusAt(current + 1);\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n if (node.hasChildren && expanded) {\n this.toggle(node, event);\n } else {\n // to the parent: the nearest earlier row one level up\n for (let i = current - 1; i >= 0; i--) {\n if (rows[i].node.level < node.level) {\n focusAt(i);\n break;\n }\n }\n }\n break;\n case 'Home':\n event.preventDefault();\n focusAt(0);\n break;\n case 'End':\n event.preventDefault();\n focusAt(rows.length - 1);\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this.onNodeClick(node, event);\n break;\n default:\n }\n }\n}\n","@if (searchEnabled()) {\r\n <!-- dx builds this as a dxTextBox in mode 'search' (ui.search_box_mixin.js), which forces\r\n showClearButton: a magnifier in a 34px slot inside the left border, and a clear button in\r\n a 34px area inside the right border that only exists while there is text to clear. Both\r\n glyphs are drawn here from primitives; the DXIcons font is not used. -->\r\n <div class=\"nxe-treeview-search\">\r\n <!-- The magnifier both search boxes share (dx draws them with one rule,\r\n .dx-searchbox .dx-icon-search): a r4.75 ring and a 45deg handle, 1.9px strokes. Keep\r\n it identical to the header filter's (nxe-header-filter-panel.component.html). -->\r\n <svg class=\"nxe-treeview-search-icon\" viewBox=\"0 0 34 34\" aria-hidden=\"true\" focusable=\"false\">\r\n <circle cx=\"15\" cy=\"15\" r=\"4.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n <path d=\"M18.5 18.5 23.5 23.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" />\r\n </svg>\r\n <input\r\n #searchInput\r\n class=\"nxe-treeview-search-input\"\r\n type=\"text\"\r\n [attr.placeholder]=\"messages.t('common.search')\"\r\n [attr.aria-label]=\"messages.t('common.search')\"\r\n [value]=\"searchText()\"\r\n (input)=\"onSearch($event)\" />\r\n @if (searchText()) {\r\n <button\r\n class=\"nxe-treeview-search-clear\"\r\n type=\"button\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"messages.t('common.clear')\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"clearSearch()\">\r\n <!-- The editors' clear glyph: a 16px disc with a 2px x cut out, two 2px bars\r\n each running corner to corner of the 8px square centred on the disc, drawn as\r\n a plus and turned 45deg. -->\r\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" focusable=\"false\">\r\n <path\r\n fill=\"currentColor\"\r\n fill-rule=\"evenodd\"\r\n transform=\"rotate(45 9 9)\"\r\n d=\"M9 1a8 8 0 1 1 0 16A8 8 0 1 1 9 1ZM8 3.343h2V8h4.657v2H10v4.657H8V10H3.343V8H8Z\" />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n}\r\n\r\n<ul\r\n class=\"nxe-treeview-node-container nxe-treeview-node-container-opened\"\r\n role=\"tree\"\r\n [attr.aria-multiselectable]=\"selectionMode() === 'multiple' ? true : null\"\r\n (keydown)=\"onKeydown($event)\">\r\n @for (row of visibleRows(); track row.node.key; let i = $index) {\r\n @let node = row.node;\r\n <li\r\n class=\"nxe-treeview-node nxe-treeview-item-without-checkbox\"\r\n role=\"treeitem\"\r\n [attr.aria-level]=\"node.level + 1\"\r\n [attr.aria-posinset]=\"row.posInSet\"\r\n [attr.aria-setsize]=\"row.setSize\"\r\n [attr.aria-expanded]=\"node.hasChildren ? isExpanded(node) : null\"\r\n [attr.aria-selected]=\"selectionMode() === 'none' ? null : node.key === selectedKey()\"\r\n [attr.tabindex]=\"isTabStop(node, i) ? 0 : -1\"\r\n (focusin)=\"onNodeFocus(node)\"\r\n (focusout)=\"onNodeBlur(node)\"\r\n [class.nxe-treeview-node-is-leaf]=\"!node.hasChildren\"\r\n [class.nxe-state-selected]=\"selectionMode() !== 'none' && node.key === selectedKey()\"\r\n [class.nxe-state-focused]=\"isFocused(node)\"\r\n [style.padding-left.px]=\"node.level * 15\">\r\n <div class=\"nxe-treeview-item\" (click)=\"onNodeClick(node, $event)\">\r\n @if (node.hasChildren) {\r\n <span\r\n class=\"nxe-treeview-toggle-item-visibility\"\r\n [class.nxe-treeview-toggle-item-visibility-opened]=\"isExpanded(node)\"\r\n (click)=\"toggle(node, $event)\">\r\n </span>\r\n } @else {\r\n <span class=\"nxe-treeview-expander-icon-stub\"></span>\r\n }\r\n <div class=\"nxe-treeview-item-content\">\r\n <span>{{ node.text }}</span>\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n</ul>\r\n","// @nexterajs/ui/tree-view — public API.\r\nexport * from './lib/tree-view/nxe-tree-view.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, contentChildren, output, signal, Injector, DestroyRef } from '@angular/core';
2
+ import { inject, input, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, contentChildren, output, signal, Injector, DestroyRef } from '@angular/core';
3
+ import { NxeMessageService, NXE_VALIDATION_TARGET, NxeValidationGroupRegistry, NXE_VALIDATION_GROUP } from '@nexterajs/ui/core';
3
4
  import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
5
  import { distinctUntilChanged, startWith, pairwise, skip } from 'rxjs';
5
- import { NXE_VALIDATION_TARGET, NxeValidationGroupRegistry, NXE_VALIDATION_GROUP } from '@nexterajs/ui/core';
6
6
 
7
7
  /**
8
- * The message shown when a rule fails and the caller gave none.
8
+ * The English message shown when a rule fails and the caller gave none. The components resolve it
9
+ * through `NxeMessageService` (`validation.rule.*`), which starts as these same strings and
10
+ * follows the active language; this table stays for code that reads it directly.
9
11
  *
10
12
  * These are DevExtreme's own defaults, verbatim from its `validation-*` message keys. Without
11
13
  * them a rule with no `message` blocks its group and displays nothing at all, because every
@@ -27,9 +29,14 @@ function nxeIsEmptyValue(value) {
27
29
  value === false ||
28
30
  (Array.isArray(value) && value.length === 0));
29
31
  }
30
- /** The message for a rule with none of its own. */
31
- function nxeDefaultMessage(type) {
32
- return NXE_DEFAULT_VALIDATION_MESSAGES[type] ?? '';
32
+ /**
33
+ * The message for a rule with none of its own: the active language's when a message service is
34
+ * given, English otherwise.
35
+ */
36
+ function nxeDefaultMessage(type, messages) {
37
+ return messages && type in NXE_DEFAULT_VALIDATION_MESSAGES
38
+ ? messages.t(`validation.rule.${type}`)
39
+ : (NXE_DEFAULT_VALIDATION_MESSAGES[type] ?? '');
33
40
  }
34
41
  /** Whether `value` satisfies `spec`. The single evaluator behind both rule forms. */
35
42
  function nxeEvaluateRule(spec, value, rule) {
@@ -94,6 +101,7 @@ function nxeEvaluateRule(spec, value, rule) {
94
101
  * `type` input covers 100% of usage.
95
102
  */
96
103
  class NxeValidationRuleComponent {
104
+ messages = inject(NxeMessageService);
97
105
  type = input('required', /* @ts-ignore */
98
106
  ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
99
107
  message = input('', /* @ts-ignore */
@@ -119,7 +127,7 @@ class NxeValidationRuleComponent {
119
127
  * The message to show when this rule fails: the caller's, or DevExtreme's default for the
120
128
  * rule type. Whitespace counts as absent — a message of spaces is still an empty bubble.
121
129
  */
122
- resolvedMessage = computed(() => this.message().trim() || NXE_DEFAULT_VALIDATION_MESSAGES[this.type()] || '', /* @ts-ignore */
130
+ resolvedMessage = computed(() => this.message().trim() || nxeDefaultMessage(this.type(), this.messages), /* @ts-ignore */
123
131
  ...(ngDevMode ? [{ debugName: "resolvedMessage" }] : /* istanbul ignore next */ []));
124
132
  /** This rule as plain data, so both rule forms evaluate through one function. */
125
133
  toSpec() {
@@ -167,6 +175,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
167
175
  class NxeValidatorComponent {
168
176
  target = inject(NXE_VALIDATION_TARGET, { optional: true });
169
177
  registry = inject(NxeValidationGroupRegistry);
178
+ messages = inject(NxeMessageService);
170
179
  groupScope = inject(NXE_VALIDATION_GROUP, { optional: true });
171
180
  rules = contentChildren(NxeValidationRuleComponent, /* @ts-ignore */
172
181
  ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
@@ -212,7 +221,7 @@ class NxeValidatorComponent {
212
221
  ...(this.validationRules() ?? []).map((spec) => ({
213
222
  type: spec.type,
214
223
  evaluate: (value) => nxeEvaluateRule(spec, value),
215
- message: () => spec.message?.trim() || nxeDefaultMessage(spec.type),
224
+ message: () => spec.message?.trim() || nxeDefaultMessage(spec.type, this.messages),
216
225
  })),
217
226
  ], /* @ts-ignore */
218
227
  ...(ngDevMode ? [{ debugName: "allRules" }] : /* istanbul ignore next */ []));
@@ -228,7 +237,9 @@ class NxeValidatorComponent {
228
237
  if (previous !== undefined) {
229
238
  this.registry.unregister(previous, this.member);
230
239
  }
231
- this.registry.register(group, this.member);
240
+ if (group !== undefined) {
241
+ this.registry.register(group, this.member);
242
+ }
232
243
  });
233
244
  // dx re-validates a field that has already been validated as its value changes, so an
234
245
  // error clears while the user types rather than surviving until the next submit.
@@ -241,6 +252,12 @@ class NxeValidatorComponent {
241
252
  this.validate();
242
253
  }
243
254
  });
255
+ // A bubble that is already showing is a string the editor holds, so it would keep the old
256
+ // language after a switch: rewrite it when the bundle changes.
257
+ /* NOT an effect: one named dependency, the active bundle. */
258
+ toObservable(this.messages.messages, { injector })
259
+ .pipe(skip(1), takeUntilDestroyed())
260
+ .subscribe(() => this.refreshMessages());
244
261
  // Tell the editor whether it is required, so it can render aria-required. The rule is
245
262
  // where the requirement is stated; the editor should not have to be told twice.
246
263
  /* ⚠ NOT an effect - UI-48: one named dependency, the resolved rule list. */
@@ -256,6 +273,20 @@ class NxeValidatorComponent {
256
273
  this.target?.setValidationError(null);
257
274
  });
258
275
  }
276
+ /** Re-word the broken rules after a language switch, without validating again or emitting. */
277
+ refreshMessages() {
278
+ if (!this.validated() || this.isValid()) {
279
+ return;
280
+ }
281
+ const value = this.target?.getValue();
282
+ const broken = this.allRules()
283
+ .filter((rule) => !rule.evaluate(value))
284
+ .map((rule) => ({ type: rule.type, message: rule.message() }));
285
+ if (broken.length) {
286
+ this.brokenRules.set(broken);
287
+ this.target?.setValidationError(broken[0].message);
288
+ }
289
+ }
259
290
  /**
260
291
  * Run every rule against the editor's current value.
261
292
  *
@@ -337,8 +368,8 @@ class NxeValidators {
337
368
  return null;
338
369
  }
339
370
  // Delete the mustMatch error to reset the error on the matching control
340
- if (matchingControl.hasError('mustMatch')) {
341
- delete matchingControl.errors.mustMatch;
371
+ if (matchingControl.errors?.['mustMatch']) {
372
+ delete matchingControl.errors['mustMatch'];
342
373
  matchingControl.updateValueAndValidity();
343
374
  }
344
375
  // Don't validate empty values on the matching control