ngwr 14.3.1 → 14.4.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.
- package/README.md +1 -1
- package/avatar/styles/_index.scss +4 -2
- package/badge/styles/_index.scss +4 -1
- package/badge/styles/_tag.scss +3 -0
- package/cascader/styles/_rules.scss +3 -0
- package/command-palette/styles/_index.scss +23 -0
- package/context-menu/styles/_index.scss +13 -1
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs +17 -4
- package/fesm2022/ngwr-affix.mjs.map +1 -1
- package/fesm2022/ngwr-alert.mjs +4 -4
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs +3 -3
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs +3 -3
- package/fesm2022/ngwr-badge.mjs +6 -6
- package/fesm2022/ngwr-bar-chart-testing.mjs +18 -2
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs +3 -3
- package/fesm2022/ngwr-border-glow.mjs +3 -3
- package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-button.mjs +6 -6
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs +12 -9
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +35 -7
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +32 -4
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +9 -9
- package/fesm2022/ngwr-carousel.mjs +6 -6
- package/fesm2022/ngwr-cascader-testing.mjs +87 -17
- package/fesm2022/ngwr-cascader-testing.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +63 -4
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox-testing.mjs +13 -2
- package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +42 -7
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +3 -3
- package/fesm2022/ngwr-click-spark.mjs +3 -3
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-color-picker.mjs +6 -6
- package/fesm2022/ngwr-command-palette.mjs +16 -3
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs +3 -3
- package/fesm2022/ngwr-context-menu.mjs +12 -12
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-counter.mjs +6 -6
- package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
- package/fesm2022/ngwr-date-picker.mjs +81 -26
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-date.mjs +3 -3
- package/fesm2022/ngwr-decrypt-text.mjs +3 -3
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-dialog.mjs +15 -15
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-divider.mjs +3 -3
- package/fesm2022/ngwr-donut-chart-testing.mjs +18 -2
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart.mjs +4 -4
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +6 -6
- package/fesm2022/ngwr-drawer.mjs +18 -18
- package/fesm2022/ngwr-dropdown.mjs +32 -14
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-event-calendar.mjs +6 -6
- package/fesm2022/ngwr-falling-text.mjs +3 -3
- package/fesm2022/ngwr-file-upload.mjs +18 -5
- package/fesm2022/ngwr-file-upload.mjs.map +1 -1
- package/fesm2022/ngwr-form.mjs +66 -14
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
- package/fesm2022/ngwr-gauge-testing.mjs +16 -2
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +3 -3
- package/fesm2022/ngwr-glitch-text.mjs +3 -3
- package/fesm2022/ngwr-gradient-text.mjs +3 -3
- package/fesm2022/ngwr-hotkey.mjs +6 -6
- package/fesm2022/ngwr-i18n-ar.mjs +2 -0
- package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-cs.mjs +2 -0
- package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-de.mjs +2 -0
- package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +14 -2
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-es.mjs +2 -0
- package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-fr.mjs +2 -0
- package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-he.mjs +2 -0
- package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-hi.mjs +2 -0
- package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-id.mjs +2 -0
- package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-it.mjs +2 -0
- package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ja.mjs +2 -0
- package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ko.mjs +2 -0
- package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-nl.mjs +2 -0
- package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pl.mjs +2 -0
- package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pt.mjs +2 -0
- package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +2 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-sv.mjs +2 -0
- package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-tr.mjs +2 -0
- package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-uk.mjs +2 -0
- package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-vi.mjs +2 -0
- package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh-TW.mjs +2 -0
- package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh.mjs +2 -0
- package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +9 -9
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-image-cropper.mjs +3 -3
- package/fesm2022/ngwr-input-number.mjs +15 -6
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +38 -6
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +15 -15
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-knob-testing.mjs +11 -3
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +19 -4
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-lightbox.mjs +3 -3
- package/fesm2022/ngwr-line-chart-testing.mjs +18 -2
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +22 -4
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +6 -6
- package/fesm2022/ngwr-loading-bar.mjs +6 -6
- package/fesm2022/ngwr-markdown.mjs +6 -6
- package/fesm2022/ngwr-marquee.mjs +3 -3
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-mention.mjs +6 -6
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meter-group.mjs +3 -3
- package/fesm2022/ngwr-overlay.mjs +6 -6
- package/fesm2022/ngwr-page-header.mjs +3 -3
- package/fesm2022/ngwr-pagination.mjs +33 -6
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +21 -21
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popover.mjs +29 -11
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs +3 -3
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-rating-testing.mjs +12 -3
- package/fesm2022/ngwr-rating-testing.mjs.map +1 -1
- package/fesm2022/ngwr-rating.mjs +19 -4
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-result.mjs +13 -13
- package/fesm2022/ngwr-result.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text.mjs +3 -3
- package/fesm2022/ngwr-schema-form.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-segmented.mjs +3 -3
- package/fesm2022/ngwr-select.mjs +9 -9
- package/fesm2022/ngwr-shiny-text.mjs +6 -6
- package/fesm2022/ngwr-sidebar.mjs +3 -3
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs +18 -5
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +3 -3
- package/fesm2022/ngwr-speed-dial.mjs +3 -3
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-split-text.mjs +3 -3
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-spotlight-card.mjs +6 -6
- package/fesm2022/ngwr-squircle.mjs +28 -9
- package/fesm2022/ngwr-squircle.mjs.map +1 -1
- package/fesm2022/ngwr-star-border.mjs +3 -3
- package/fesm2022/ngwr-statistic.mjs +20 -13
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-stepper.mjs +6 -6
- package/fesm2022/ngwr-storage.mjs +3 -3
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-table.mjs +18 -18
- package/fesm2022/ngwr-tabs-router.mjs +3 -3
- package/fesm2022/ngwr-tabs.mjs +6 -6
- package/fesm2022/ngwr-textarea.mjs +3 -3
- package/fesm2022/ngwr-theme.mjs +3 -3
- package/fesm2022/ngwr-tilt-card.mjs +6 -6
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-toast.mjs +10 -10
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +3 -3
- package/fesm2022/ngwr-tour.mjs +6 -6
- package/fesm2022/ngwr-transfer.mjs +88 -11
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree-testing.mjs +24 -0
- package/fesm2022/ngwr-tree-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +72 -7
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +3 -3
- package/fesm2022/ngwr-typography.mjs +3 -3
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
- package/fesm2022/ngwr-waves.mjs +3 -3
- package/fesm2022/ngwr-window.mjs +12 -12
- package/form/styles/_index.scss +63 -0
- package/i18n/ar.json +4 -2
- package/i18n/cs.json +4 -2
- package/i18n/de.json +4 -2
- package/i18n/en.json +6 -4
- package/i18n/es.json +4 -2
- package/i18n/fr.json +4 -2
- package/i18n/he.json +4 -2
- package/i18n/hi.json +4 -2
- package/i18n/id.json +4 -2
- package/i18n/it.json +4 -2
- package/i18n/ja.json +4 -2
- package/i18n/ko.json +4 -2
- package/i18n/nl.json +4 -2
- package/i18n/pl.json +4 -2
- package/i18n/pt.json +4 -2
- package/i18n/ru.json +4 -2
- package/i18n/sv.json +4 -2
- package/i18n/tr.json +4 -2
- package/i18n/uk.json +4 -2
- package/i18n/vi.json +4 -2
- package/i18n/zh-TW.json +4 -2
- package/i18n/zh.json +4 -2
- package/input-otp/styles/_index.scss +5 -1
- package/line-chart/styles/_index.scss +5 -3
- package/llms-full.txt +1 -1
- package/markdown/styles/_index.scss +4 -1
- package/mention/styles/_index.scss +22 -1
- package/package.json +1 -1
- package/radio/styles/_index.scss +3 -0
- package/result/styles/_index.scss +12 -0
- package/schematics/migrations/v14/index.js +6 -3
- package/schematics/migrations.json +1 -1
- package/schematics/ng-add/index.js +34 -5
- package/segmented/styles/_index.scss +4 -1
- package/select/styles/_index.scss +20 -1
- package/statistic/styles/_index.scss +4 -0
- package/stepper/styles/_index.scss +36 -0
- package/switch/styles/_index.scss +3 -0
- package/table/styles/_index.scss +12 -0
- package/tabs/styles/_index.scss +4 -1
- package/textarea/styles/_index.scss +6 -1
- package/theme/styles/_colors.scss +11 -0
- package/theme/styles/_contrast.scss +136 -0
- package/theme/styles/_index.scss +1 -0
- package/theme/styles/_mixins.scss +15 -8
- package/timeline/styles/_index.scss +28 -1
- package/toast/styles/_index.scss +11 -0
- package/tree/styles/_index.scss +27 -1
- package/types/ngwr-badge.d.ts +1 -1
- package/types/ngwr-bar-chart-testing.d.ts +11 -1
- package/types/ngwr-button.d.ts +1 -1
- package/types/ngwr-calendar-heatmap-testing.d.ts +10 -5
- package/types/ngwr-calendar-heatmap.d.ts +22 -0
- package/types/ngwr-calendar.d.ts +21 -0
- package/types/ngwr-cascader-testing.d.ts +74 -17
- package/types/ngwr-cascader.d.ts +28 -0
- package/types/ngwr-checkbox-testing.d.ts +12 -1
- package/types/ngwr-checkbox.d.ts +10 -0
- package/types/ngwr-clipboard.d.ts +1 -1
- package/types/ngwr-confetti.d.ts +1 -1
- package/types/ngwr-cookie.d.ts +1 -1
- package/types/ngwr-date-adapters-fns.d.ts +1 -1
- package/types/ngwr-date-adapters-luxon.d.ts +1 -1
- package/types/ngwr-date.d.ts +9 -3
- package/types/ngwr-density.d.ts +1 -1
- package/types/ngwr-dialog.d.ts +1 -1
- package/types/ngwr-donut-chart-testing.d.ts +11 -1
- package/types/ngwr-drawer.d.ts +1 -1
- package/types/ngwr-dropdown.d.ts +16 -0
- package/types/ngwr-file-upload.d.ts +11 -0
- package/types/ngwr-form.d.ts +25 -0
- package/types/ngwr-gauge-testing.d.ts +9 -1
- package/types/ngwr-hotkey.d.ts +1 -1
- package/types/ngwr-i18n.d.ts +1 -1
- package/types/ngwr-input-number.d.ts +12 -2
- package/types/ngwr-input-otp.d.ts +12 -0
- package/types/ngwr-knob-testing.d.ts +11 -3
- package/types/ngwr-knob.d.ts +13 -0
- package/types/ngwr-line-chart-testing.d.ts +11 -1
- package/types/ngwr-line-chart.d.ts +18 -0
- package/types/ngwr-loading-bar.d.ts +1 -1
- package/types/ngwr-markdown.d.ts +1 -1
- package/types/ngwr-media.d.ts +1 -1
- package/types/ngwr-meta.d.ts +1 -1
- package/types/ngwr-overlay.d.ts +2 -2
- package/types/ngwr-pagination.d.ts +16 -0
- package/types/ngwr-platform.d.ts +3 -3
- package/types/ngwr-popover.d.ts +16 -0
- package/types/ngwr-rating-testing.d.ts +12 -3
- package/types/ngwr-rating.d.ts +13 -0
- package/types/ngwr-scroll.d.ts +1 -1
- package/types/ngwr-squircle.d.ts +22 -3
- package/types/ngwr-statistic.d.ts +0 -1
- package/types/ngwr-storage.d.ts +1 -1
- package/types/ngwr-theme.d.ts +1 -1
- package/types/ngwr-toast.d.ts +1 -1
- package/types/ngwr-tour.d.ts +1 -1
- package/types/ngwr-transfer.d.ts +52 -0
- package/types/ngwr-tree-testing.d.ts +20 -0
- package/types/ngwr-tree.d.ts +41 -0
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-window.d.ts +1 -1
- package/typography/styles/_index.scss +26 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-transfer.mjs","sources":["../../../projects/lib/transfer/transfer.ts","../../../projects/lib/transfer/transfer.html","../../../projects/lib/transfer/ngwr-transfer.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input, model, output, signal } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { WrButton } from 'ngwr/button';\nimport { WrCheckbox } from 'ngwr/checkbox';\nimport { WR_FORM_FIELD, useFormFieldAria } from 'ngwr/form';\nimport { useI18nFormatter, useI18nText } from 'ngwr/i18n';\nimport { WrInput } from 'ngwr/input';\n\nimport type { WrTransferItem } from './interfaces';\n\n/** A pane's rows plus the header state derived from them. */\ninterface PaneState {\n readonly rows: readonly WrTransferItem[];\n /**\n * The staged values this pane is actually SHOWING — visible and enabled. The\n * staging signals can hold more than that (a filter change hides rows without\n * unstaging them), and every consumer of the staged set has to agree on which\n * one it means: the count, the header checkbox and the move all read this.\n */\n readonly checked: readonly unknown[];\n readonly checkedCount: number;\n readonly enabledCount: number;\n readonly allChecked: boolean;\n readonly someChecked: boolean;\n}\n\n/**\n * Dual-listbox picker: two panes and the buttons that move rows between them.\n *\n * The full item set is `[items]` and the RIGHT pane is the value — `[(value)]`\n * holds the chosen `WrTransferItem['value']`s, everything else stays left. The\n * checked state inside each pane is transient staging, deliberately not part of\n * the value: what a form cares about is which rows ended up on the right.\n *\n * A signal-forms native control (`FormValueControl<readonly unknown[]>`), so\n * `[formField]` binds straight to `value`; `[(ngModel)]` and reactive forms keep\n * working through Angular's bridge, and `[(value)]` is the standalone shape.\n *\n * @example\n * ```html\n * <!-- Standalone -->\n * <wr-transfer [items]=\"permissions\" [(value)]=\"granted\" />\n *\n * <!-- Searchable, with pane titles -->\n * <wr-transfer\n * searchable\n * sourceTitle=\"Available\"\n * targetTitle=\"Granted\"\n * [items]=\"permissions\"\n * [formField]=\"form.granted\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/transfer\n */\n@Component({\n selector: 'wr-transfer',\n templateUrl: './transfer.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n // `role=\"group\"` so the error state below has somewhere legible to live: the\n // control is a composite with no single element carrying its semantics, and\n // `aria-invalid` on a bare `generic` host is announced by nothing.\n role: 'group',\n '[attr.aria-invalid]': 'fieldAria.ariaInvalid()',\n '[attr.aria-describedby]': 'fieldAria.describedBy()',\n },\n // The transfer is the control a `<wr-form-field>` wraps; the checkboxes and\n // the search inputs in its own template are parts, not controls. Without this\n // shield each of them found the field's token and announced the one error as\n // its own — which is why `fieldAria` below reads the field with `skipSelf`.\n providers: [{ provide: WR_FORM_FIELD, useValue: null }],\n imports: [WrButton, WrCheckbox, WrInput],\n})\nexport class WrTransfer implements FormValueControl<readonly unknown[]> {\n /** Every row, in either pane. Membership of the right pane is `value`. */\n readonly items = input<readonly WrTransferItem[]>([]);\n\n /**\n * Values currently in the RIGHT pane. Two-way bindable; bound automatically\n * by `[formField]` / `[(ngModel)]`.\n */\n readonly value = model<readonly unknown[]>([]);\n\n /** Emitted on blur / commit so a bound field marks itself touched. */\n readonly touch = output<void>();\n\n /** Disable the whole control. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Refuse changes to the value while both panes stay focusable and readable.\n * Bound automatically from the field's readonly state when used with\n * `[formField]`.\n *\n * The move buttons go inert and the row checkboxes go read-only; SEARCH keeps\n * working, because filtering a pane changes what is shown and not what is\n * chosen. No `aria-readonly` on the host: role `group` does not support the\n * state, so each row's checkbox mirrors its own instead.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria({ skipSelf: true });\n\n /** Show a filter box above each pane. @default false */\n readonly searchable = input(false, { transform: coerceBooleanProperty });\n\n /** Heading above the left pane. Falls back to `transfer.source`. */\n readonly sourceTitle = input<string | null>(null);\n\n /** Heading above the right pane. Falls back to `transfer.target`. */\n readonly targetTitle = input<string | null>(null);\n\n /** Placeholder in both filter boxes. Falls back to `transfer.search`. */\n readonly searchPlaceholder = input<string | null>(null);\n\n /** Shown in a pane with no rows. Falls back to `transfer.empty`. */\n readonly emptyText = input<string | null>(null);\n\n /**\n * Accessible name of each pane's select-all checkbox, composed with the pane\n * heading. Falls back to `transfer.selectAll`.\n */\n readonly selectAllLabel = input<string | null>(null);\n\n /** Accessible name of the move-right button. Falls back to `transfer.toTarget`. */\n readonly toTargetLabel = input<string | null>(null);\n\n /** Accessible name of the move-left button. Falls back to `transfer.toSource`. */\n readonly toSourceLabel = input<string | null>(null);\n\n protected readonly resolvedSourceTitle = useI18nText(this.sourceTitle, 'transfer.source', 'Available');\n protected readonly resolvedTargetTitle = useI18nText(this.targetTitle, 'transfer.target', 'Selected');\n protected readonly resolvedSearch = useI18nText(this.searchPlaceholder, 'transfer.search', 'Search');\n protected readonly resolvedEmpty = useI18nText(this.emptyText, 'transfer.empty', 'Nothing here');\n protected readonly resolvedSelectAll = useI18nText(this.selectAllLabel, 'transfer.selectAll', 'Select all');\n protected readonly resolvedToTarget = useI18nText(this.toTargetLabel, 'transfer.toTarget', 'Move to selected');\n protected readonly resolvedToSource = useI18nText(this.toSourceLabel, 'transfer.toSource', 'Move to available');\n\n /** `{{checked}} / {{total}}` under each heading. @internal */\n protected readonly countLabel = useI18nFormatter('transfer.count', '{{checked}} / {{total}}');\n\n /** Pane order — the template iterates it so both panes share one block. */\n protected readonly panes = ['source', 'target'] as const;\n\n private readonly sourceQuery = signal('');\n private readonly targetQuery = signal('');\n\n /** Staged rows, per pane. Cleared as soon as a move commits them. */\n private readonly sourceChecked = signal<readonly unknown[]>([]);\n private readonly targetChecked = signal<readonly unknown[]>([]);\n\n // The value as a guaranteed array — a classic-forms binding can write null even\n // though the type is `readonly unknown[]`, so normalise every read. Same hazard\n // `wr-checkbox-group` guards; here an unguarded `.map` threw outright.\n private readonly selected = computed<readonly unknown[]>(() => {\n const v = this.value();\n return Array.isArray(v) ? v : [];\n });\n\n private readonly valueSet = computed(() => new Set(this.selected()));\n\n protected readonly source = computed<PaneState>(() =>\n this.pane(\n this.items().filter(item => !this.valueSet().has(item.value)),\n this.sourceQuery(),\n this.sourceChecked()\n )\n );\n\n protected readonly target = computed<PaneState>(() =>\n this.pane(\n // Right-pane order follows `value`, not `items`: the order a user built is\n // the one they expect to read back.\n this.selected()\n .map(v => this.items().find(item => item.value === v))\n .filter((item): item is WrTransferItem => item !== undefined),\n this.targetQuery(),\n this.targetChecked()\n )\n );\n\n protected readonly canMoveRight = computed(\n () => !this.disabled() && !this.readonly() && this.source().checkedCount > 0\n );\n protected readonly canMoveLeft = computed(\n () => !this.disabled() && !this.readonly() && this.target().checkedCount > 0\n );\n\n protected readonly classes = computed(() => {\n const parts = ['wr-transfer'];\n if (this.disabled()) parts.push('wr-transfer--disabled');\n else if (this.readonly()) parts.push('wr-transfer--readonly');\n if (this.searchable()) parts.push('wr-transfer--searchable');\n return parts.join(' ');\n });\n\n protected isChecked(pane: 'source' | 'target', item: WrTransferItem): boolean {\n return (pane === 'source' ? this.sourceChecked() : this.targetChecked()).includes(item.value);\n }\n\n protected toggle(pane: 'source' | 'target', item: WrTransferItem, checked: boolean): void {\n if (item.disabled || this.readonly()) return;\n const box = pane === 'source' ? this.sourceChecked : this.targetChecked;\n const current = box();\n if (checked) {\n box.set([...current, item.value]);\n return;\n }\n // A `Set` compares with SameValueZero, which is what `WrTransferItem['value']`\n // is documented to use and what `isChecked`'s `includes` already does. A `!==`\n // filter cannot remove `NaN` from its own list, so such a row drew itself\n // unchecked while staying staged.\n const next = new Set(current);\n next.delete(item.value);\n box.set([...next]);\n }\n\n /** Header checkbox — stages or clears every enabled row the filter shows. */\n protected toggleAll(pane: 'source' | 'target', checked: boolean): void {\n if (this.readonly()) return;\n const state = pane === 'source' ? this.source() : this.target();\n const box = pane === 'source' ? this.sourceChecked : this.targetChecked;\n box.set(checked ? state.rows.filter(item => !item.disabled).map(item => item.value) : []);\n }\n\n protected onSearch(pane: 'source' | 'target', event: Event): void {\n const query = (event.target as HTMLInputElement | null)?.value ?? '';\n (pane === 'source' ? this.sourceQuery : this.targetQuery).set(query);\n }\n\n protected moveRight(): void {\n if (this.disabled() || this.readonly()) return;\n // `source().checked`, not the raw staging box: rows a filter change hid are\n // neither counted nor ticked any more, so moving them would transfer rows the\n // pane never showed as chosen — and, since staging outlives an external write\n // to `value`, could land the same value on the right twice.\n const moving = this.source().checked;\n if (moving.length === 0) return;\n this.value.set([...this.selected(), ...moving]);\n this.sourceChecked.set([]);\n this.touch.emit();\n }\n\n protected moveLeft(): void {\n if (this.disabled() || this.readonly()) return;\n const moving = new Set(this.target().checked);\n if (moving.size === 0) return;\n this.value.set(this.selected().filter(v => !moving.has(v)));\n this.targetChecked.set([]);\n this.touch.emit();\n }\n\n /** Filter, then derive the header state from what survived. */\n private pane(rows: readonly WrTransferItem[], query: string, checked: readonly unknown[]): PaneState {\n const q = query.trim().toLowerCase();\n const visible = q ? rows.filter(item => item.label.toLowerCase().includes(q)) : rows;\n const enabled = visible.filter(item => !item.disabled);\n const staged = new Set(checked);\n const shown = enabled.filter(item => staged.has(item.value)).map(item => item.value);\n return {\n rows: visible,\n checked: shown,\n checkedCount: shown.length,\n enabledCount: enabled.length,\n allChecked: enabled.length > 0 && shown.length === enabled.length,\n someChecked: shown.length > 0 && shown.length < enabled.length,\n };\n }\n}\n","<!--\n Two panes and the buttons between them. Each pane is a PLAIN `<ul>` of\n checkboxes, deliberately: dressing it as a `role=\"listbox\"` of `role=\"option\"`\n rows reads better in the abstract but is invalid — an option may not contain an\n interactive control, and the axe gate says so. The checkbox is the thing the\n user operates, so it is the thing the semantics should describe; the pane\n heading names the group.\n-->\n@for (pane of panes; track pane) {\n @let state = pane === 'source' ? source() : target();\n @let title = pane === 'source' ? resolvedSourceTitle() : resolvedTargetTitle();\n\n <div class=\"wr-transfer__pane\" [class.wr-transfer__pane--target]=\"pane === 'target'\">\n <div class=\"wr-transfer__head\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"state.allChecked\"\n [indeterminate]=\"state.someChecked\"\n [disabled]=\"disabled() || state.enabledCount === 0\"\n [readonly]=\"readonly()\"\n [ariaLabel]=\"`${title} — ${resolvedSelectAll()}`\"\n (checkedChange)=\"toggleAll(pane, $event)\"\n />\n <span class=\"wr-transfer__title\">{{ title }}</span>\n <span class=\"wr-transfer__count\">{{\n countLabel({ checked: state.checkedCount, total: state.rows.length })\n }}</span>\n </div>\n\n @if (searchable()) {\n <div class=\"wr-transfer__search\">\n <input\n wrInput\n size=\"sm\"\n type=\"search\"\n [placeholder]=\"resolvedSearch()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"`${title} — ${resolvedSearch()}`\"\n (input)=\"onSearch(pane, $event)\"\n />\n </div>\n }\n\n <ul class=\"wr-transfer__list\" [attr.aria-label]=\"title\">\n @for (item of state.rows; track item.value) {\n <li class=\"wr-transfer__item\" [class.wr-transfer__item--disabled]=\"item.disabled\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"isChecked(pane, item)\"\n [disabled]=\"disabled() || !!item.disabled\"\n [readonly]=\"readonly()\"\n (checkedChange)=\"toggle(pane, item, $event)\"\n (touch)=\"touch.emit()\"\n >\n {{ item.label }}\n </wr-checkbox>\n </li>\n } @empty {\n <li class=\"wr-transfer__empty\">{{ resolvedEmpty() }}</li>\n }\n </ul>\n </div>\n\n <!-- The buttons live between the panes, so they are emitted after the first. -->\n @if (pane === 'source') {\n <div class=\"wr-transfer__actions\">\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-target\"\n [disabled]=\"!canMoveRight()\"\n [attr.aria-label]=\"resolvedToTarget()\"\n (click)=\"moveRight()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-source\"\n [disabled]=\"!canMoveLeft()\"\n [attr.aria-label]=\"resolvedToSource()\"\n (click)=\"moveLeft()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n </div>\n }\n}\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":"ngwr-transfer.mjs","sources":["../../../projects/lib/transfer/transfer.ts","../../../projects/lib/transfer/transfer.html","../../../projects/lib/transfer/ngwr-transfer.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n ElementRef,\n Injector,\n ViewEncapsulation,\n afterNextRender,\n computed,\n inject,\n input,\n isDevMode,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { WrButton } from 'ngwr/button';\nimport { WrCheckbox } from 'ngwr/checkbox';\nimport { WR_FORM_FIELD, useFormFieldAria } from 'ngwr/form';\nimport { useI18nFormatter, useI18nText } from 'ngwr/i18n';\nimport { WrInput } from 'ngwr/input';\n\nimport type { WrTransferItem } from './interfaces';\n\n/** A pane's rows plus the header state derived from them. */\ninterface PaneState {\n readonly rows: readonly WrTransferItem[];\n /**\n * The staged values this pane is actually SHOWING — visible and enabled. The\n * staging signals can hold more than that (a filter change hides rows without\n * unstaging them), and every consumer of the staged set has to agree on which\n * one it means: the count, the header checkbox and the move all read this.\n */\n readonly checked: readonly unknown[];\n readonly checkedCount: number;\n readonly enabledCount: number;\n readonly allChecked: boolean;\n readonly someChecked: boolean;\n}\n\n/**\n * Dual-listbox picker: two panes and the buttons that move rows between them.\n *\n * The full item set is `[items]` and the RIGHT pane is the value — `[(value)]`\n * holds the chosen `WrTransferItem['value']`s, everything else stays left. The\n * checked state inside each pane is transient staging, deliberately not part of\n * the value: what a form cares about is which rows ended up on the right.\n *\n * A signal-forms native control (`FormValueControl<readonly unknown[]>`), so\n * `[formField]` binds straight to `value`; `[(ngModel)]` and reactive forms keep\n * working through Angular's bridge, and `[(value)]` is the standalone shape.\n *\n * @example\n * ```html\n * <!-- Standalone -->\n * <wr-transfer [items]=\"permissions\" [(value)]=\"granted\" />\n *\n * <!-- Searchable, with pane titles -->\n * <wr-transfer\n * searchable\n * sourceTitle=\"Available\"\n * targetTitle=\"Granted\"\n * [items]=\"permissions\"\n * [formField]=\"form.granted\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/transfer\n */\n@Component({\n selector: 'wr-transfer',\n templateUrl: './transfer.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n // `role=\"group\"` so the error state below has somewhere legible to live: the\n // control is a composite with no single element carrying its semantics, and\n // `aria-invalid` on a bare `generic` host is announced by nothing.\n role: 'group',\n '[attr.aria-invalid]': 'fieldAria.ariaInvalid()',\n '[attr.aria-describedby]': 'fieldAria.describedBy()',\n },\n // The transfer is the control a `<wr-form-field>` wraps; the checkboxes and\n // the search inputs in its own template are parts, not controls. Without this\n // shield each of them found the field's token and announced the one error as\n // its own — which is why `fieldAria` below reads the field with `skipSelf`.\n providers: [{ provide: WR_FORM_FIELD, useValue: null }],\n imports: [WrButton, WrCheckbox, WrInput],\n})\nexport class WrTransfer implements FormValueControl<readonly unknown[]> {\n /** Every row, in either pane. Membership of the right pane is `value`. */\n readonly items = input<readonly WrTransferItem[]>([]);\n\n /**\n * Values currently in the RIGHT pane. Two-way bindable; bound automatically\n * by `[formField]` / `[(ngModel)]`.\n */\n readonly value = model<readonly unknown[]>([]);\n\n /** Emitted on blur / commit so a bound field marks itself touched. */\n readonly touch = output<void>();\n\n /** Disable the whole control. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Refuse changes to the value while both panes stay focusable and readable.\n * Bound automatically from the field's readonly state when used with\n * `[formField]`.\n *\n * The move buttons go inert and the row checkboxes go read-only; SEARCH keeps\n * working, because filtering a pane changes what is shown and not what is\n * chosen. No `aria-readonly` on the host: role `group` does not support the\n * state, so each row's checkbox mirrors its own instead.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria({ skipSelf: true });\n\n /** Show a filter box above each pane. @default false */\n readonly searchable = input(false, { transform: coerceBooleanProperty });\n\n /** Heading above the left pane. Falls back to `transfer.source`. */\n readonly sourceTitle = input<string | null>(null);\n\n /** Heading above the right pane. Falls back to `transfer.target`. */\n readonly targetTitle = input<string | null>(null);\n\n /** Placeholder in both filter boxes. Falls back to `transfer.search`. */\n readonly searchPlaceholder = input<string | null>(null);\n\n /** Shown in a pane with no rows. Falls back to `transfer.empty`. */\n readonly emptyText = input<string | null>(null);\n\n /**\n * Accessible name of each pane's select-all checkbox, composed with the pane\n * heading. Falls back to `transfer.selectAll`.\n */\n readonly selectAllLabel = input<string | null>(null);\n\n /** Accessible name of the move-right button. Falls back to `transfer.toTarget`. */\n readonly toTargetLabel = input<string | null>(null);\n\n /** Accessible name of the move-left button. Falls back to `transfer.toSource`. */\n readonly toSourceLabel = input<string | null>(null);\n\n protected readonly resolvedSourceTitle = useI18nText(this.sourceTitle, 'transfer.source', 'Available');\n protected readonly resolvedTargetTitle = useI18nText(this.targetTitle, 'transfer.target', 'Selected');\n protected readonly resolvedSearch = useI18nText(this.searchPlaceholder, 'transfer.search', 'Search');\n protected readonly resolvedEmpty = useI18nText(this.emptyText, 'transfer.empty', 'Nothing here');\n protected readonly resolvedSelectAll = useI18nText(this.selectAllLabel, 'transfer.selectAll', 'Select all');\n\n /**\n * The select-all checkbox's accessible name, composed IN THE CATALOG.\n *\n * It used to be a template literal — `` `${title} — ${selectAll}` `` — which\n * froze both the separator and the operand order into English. A translator\n * could change each half and not the sentence they make, which is the exact\n * defect the calendar and event-calendar catalogs were rewritten to remove.\n * The key ships as `{{pane}} — {{action}}` in every locale, so nothing reads\n * differently today and every locale can now reorder it without a code change.\n */\n protected readonly selectAllAria = useI18nFormatter('transfer.selectAllAria', '{{pane}} — {{action}}');\n protected readonly resolvedToTarget = useI18nText(this.toTargetLabel, 'transfer.toTarget', 'Move to selected');\n protected readonly resolvedToSource = useI18nText(this.toSourceLabel, 'transfer.toSource', 'Move to available');\n\n /** `{{checked}} / {{total}}` under each heading. @internal */\n protected readonly countLabel = useI18nFormatter('transfer.count', '{{checked}} / {{total}}');\n\n /** Pane order — the template iterates it so both panes share one block. */\n protected readonly panes = ['source', 'target'] as const;\n\n private readonly sourceQuery = signal('');\n private readonly targetQuery = signal('');\n\n /** Staged rows, per pane. Cleared as soon as a move commits them. */\n private readonly sourceChecked = signal<readonly unknown[]>([]);\n private readonly targetChecked = signal<readonly unknown[]>([]);\n\n // The value as a guaranteed array — a classic-forms binding can write null even\n // though the type is `readonly unknown[]`, so normalise every read. Same hazard\n // `wr-checkbox-group` guards; here an unguarded `.map` threw outright.\n private readonly selected = computed<readonly unknown[]>(() => {\n const v = this.value();\n return Array.isArray(v) ? v : [];\n });\n\n private readonly valueSet = computed(() => new Set(this.selected()));\n\n protected readonly source = computed<PaneState>(() =>\n this.pane(\n this.items().filter(item => !this.valueSet().has(item.value)),\n this.sourceQuery(),\n this.sourceChecked()\n )\n );\n\n protected readonly target = computed<PaneState>(() => {\n // Right-pane order follows `value`, not `items`: the order a user built is\n // the one they expect to read back.\n const rows = this.selected()\n .map(v => this.items().find(item => item.value === v))\n .filter((item): item is WrTransferItem => item !== undefined);\n\n if (isDevMode() && rows.length !== this.selected().length) this.warnAboutOrphans();\n\n return this.pane(rows, this.targetQuery(), this.targetChecked());\n });\n\n /**\n * Say something when `value` holds a value no `items` entry matches.\n *\n * Such a value is in NEITHER pane — the left one filters out anything already\n * selected and the right one can only render rows it found — and the header\n * counts the rows it shows, so nothing on screen contradicts anything. The\n * result is a value the user can neither see nor remove, and no sign that it\n * is there.\n *\n * A warning rather than an invented row, which matches how the library treats\n * the same question elsewhere: `wr-select` renders a value outside its option\n * list only under an explicit `freeText`, so an unmatched value is a mode a\n * consumer opts into, not a shape a component guesses at. It also stays quiet\n * about the common transient case — `items` arriving after `value` — because\n * the message names the values and a developer can see them settle.\n */\n private warnAboutOrphans(): void {\n const known = new Set(this.items().map(item => item.value));\n const orphans = this.selected().filter(v => !known.has(v));\n if (orphans.length === 0) return;\n\n // eslint-disable-next-line no-console -- dev-mode validation\n console.warn(\n `[NGWR] <wr-transfer>: ${orphans.length} value(s) in \\`value\\` match no \\`items\\` entry ` +\n `and are rendered in neither pane, so a user can neither see nor remove them: ` +\n `${orphans.map(v => JSON.stringify(v)).join(', ')}. ` +\n `If \\`items\\` loads after \\`value\\`, this settles on its own.`\n );\n }\n\n protected readonly canMoveRight = computed(\n () => !this.disabled() && !this.readonly() && this.source().checkedCount > 0\n );\n protected readonly canMoveLeft = computed(\n () => !this.disabled() && !this.readonly() && this.target().checkedCount > 0\n );\n\n protected readonly classes = computed(() => {\n const parts = ['wr-transfer'];\n if (this.disabled()) parts.push('wr-transfer--disabled');\n else if (this.readonly()) parts.push('wr-transfer--readonly');\n if (this.searchable()) parts.push('wr-transfer--searchable');\n return parts.join(' ');\n });\n\n protected isChecked(pane: 'source' | 'target', item: WrTransferItem): boolean {\n return (pane === 'source' ? this.sourceChecked() : this.targetChecked()).includes(item.value);\n }\n\n protected toggle(pane: 'source' | 'target', item: WrTransferItem, checked: boolean): void {\n if (item.disabled || this.readonly()) return;\n const box = pane === 'source' ? this.sourceChecked : this.targetChecked;\n const current = box();\n if (checked) {\n box.set([...current, item.value]);\n return;\n }\n // A `Set` compares with SameValueZero, which is what `WrTransferItem['value']`\n // is documented to use and what `isChecked`'s `includes` already does. A `!==`\n // filter cannot remove `NaN` from its own list, so such a row drew itself\n // unchecked while staying staged.\n const next = new Set(current);\n next.delete(item.value);\n box.set([...next]);\n }\n\n /** Header checkbox — stages or clears every enabled row the filter shows. */\n protected toggleAll(pane: 'source' | 'target', checked: boolean): void {\n if (this.readonly()) return;\n const state = pane === 'source' ? this.source() : this.target();\n const box = pane === 'source' ? this.sourceChecked : this.targetChecked;\n box.set(checked ? state.rows.filter(item => !item.disabled).map(item => item.value) : []);\n }\n\n protected onSearch(pane: 'source' | 'target', event: Event): void {\n const query = (event.target as HTMLInputElement | null)?.value ?? '';\n (pane === 'source' ? this.sourceQuery : this.targetQuery).set(query);\n }\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n\n /**\n * Keep focus in the widget after a move.\n *\n * A move empties the pane's staging set, which is exactly what disables the\n * button that was just pressed — so the element holding DOM focus went\n * `disabled` as a RESULT of its own activation and the browser dropped focus\n * to `<body>`. From there the next Tab restarts at the top of the document,\n * which for a keyboard user is being thrown out of a control they were in the\n * middle of using.\n *\n * The landing is the FIRST ROW OF THE PANE THE ROWS ARRIVED IN, and the\n * obvious alternative does not work: after a move BOTH buttons are disabled,\n * because each needs something ticked and the move cleared the tick on one\n * side and arrived unticked on the other. The rows that just moved are where\n * the user's attention is and where the next action lives, so that is where\n * focus goes.\n *\n * `afterNextRender`, not `queueMicrotask`: under zoneless CD the microtask\n * runs before the moved rows are in the DOM, so the focus call would land on\n * the pane as it was before the move.\n */\n private restoreFocusAfterMove(landing: 'source' | 'target'): void {\n afterNextRender(\n () => {\n const root = this.host.nativeElement;\n const pane =\n landing === 'target'\n ? root.querySelector<HTMLElement>('.wr-transfer__pane--target')\n : root.querySelector<HTMLElement>('.wr-transfer__pane:not(.wr-transfer__pane--target)');\n const row = pane?.querySelector<HTMLInputElement>('.wr-transfer__item input:not([disabled])');\n row?.focus();\n },\n { injector: this.injector }\n );\n }\n\n protected moveRight(): void {\n if (this.disabled() || this.readonly()) return;\n // `source().checked`, not the raw staging box: rows a filter change hid are\n // neither counted nor ticked any more, so moving them would transfer rows the\n // pane never showed as chosen — and, since staging outlives an external write\n // to `value`, could land the same value on the right twice.\n const moving = this.source().checked;\n if (moving.length === 0) return;\n this.value.set([...this.selected(), ...moving]);\n this.sourceChecked.set([]);\n this.touch.emit();\n this.restoreFocusAfterMove('target');\n }\n\n protected moveLeft(): void {\n if (this.disabled() || this.readonly()) return;\n const moving = new Set(this.target().checked);\n if (moving.size === 0) return;\n this.value.set(this.selected().filter(v => !moving.has(v)));\n this.targetChecked.set([]);\n this.touch.emit();\n this.restoreFocusAfterMove('source');\n }\n\n /** Filter, then derive the header state from what survived. */\n private pane(rows: readonly WrTransferItem[], query: string, checked: readonly unknown[]): PaneState {\n const q = query.trim().toLowerCase();\n const visible = q ? rows.filter(item => item.label.toLowerCase().includes(q)) : rows;\n const enabled = visible.filter(item => !item.disabled);\n const staged = new Set(checked);\n const shown = enabled.filter(item => staged.has(item.value)).map(item => item.value);\n return {\n rows: visible,\n checked: shown,\n checkedCount: shown.length,\n enabledCount: enabled.length,\n allChecked: enabled.length > 0 && shown.length === enabled.length,\n someChecked: shown.length > 0 && shown.length < enabled.length,\n };\n }\n}\n","<!--\n Two panes and the buttons between them. Each pane is a PLAIN `<ul>` of\n checkboxes, deliberately: dressing it as a `role=\"listbox\"` of `role=\"option\"`\n rows reads better in the abstract but is invalid — an option may not contain an\n interactive control, and the axe gate says so. The checkbox is the thing the\n user operates, so it is the thing the semantics should describe; the pane\n heading names the group.\n-->\n@for (pane of panes; track pane) {\n @let state = pane === 'source' ? source() : target();\n @let title = pane === 'source' ? resolvedSourceTitle() : resolvedTargetTitle();\n\n <div class=\"wr-transfer__pane\" [class.wr-transfer__pane--target]=\"pane === 'target'\">\n <div class=\"wr-transfer__head\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"state.allChecked\"\n [indeterminate]=\"state.someChecked\"\n [disabled]=\"disabled() || state.enabledCount === 0\"\n [readonly]=\"readonly()\"\n [ariaLabel]=\"selectAllAria({ action: resolvedSelectAll(), pane: title })\"\n (checkedChange)=\"toggleAll(pane, $event)\"\n />\n <span class=\"wr-transfer__title\">{{ title }}</span>\n <span class=\"wr-transfer__count\">{{\n countLabel({ checked: state.checkedCount, total: state.rows.length })\n }}</span>\n </div>\n\n @if (searchable()) {\n <div class=\"wr-transfer__search\">\n <input\n wrInput\n size=\"sm\"\n type=\"search\"\n [placeholder]=\"resolvedSearch()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"`${title} — ${resolvedSearch()}`\"\n (input)=\"onSearch(pane, $event)\"\n />\n </div>\n }\n\n <ul class=\"wr-transfer__list\" [attr.aria-label]=\"title\">\n @for (item of state.rows; track item.value) {\n <li class=\"wr-transfer__item\" [class.wr-transfer__item--disabled]=\"item.disabled\">\n <wr-checkbox\n size=\"sm\"\n [checked]=\"isChecked(pane, item)\"\n [disabled]=\"disabled() || !!item.disabled\"\n [readonly]=\"readonly()\"\n (checkedChange)=\"toggle(pane, item, $event)\"\n (touch)=\"touch.emit()\"\n >\n {{ item.label }}\n </wr-checkbox>\n </li>\n } @empty {\n <li class=\"wr-transfer__empty\">{{ resolvedEmpty() }}</li>\n }\n </ul>\n </div>\n\n <!-- The buttons live between the panes, so they are emitted after the first. -->\n @if (pane === 'source') {\n <div class=\"wr-transfer__actions\">\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-target\"\n [disabled]=\"!canMoveRight()\"\n [attr.aria-label]=\"resolvedToTarget()\"\n (click)=\"moveRight()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n wr-btn\n type=\"button\"\n size=\"sm\"\n color=\"primary\"\n outlined\n class=\"wr-transfer__move wr-transfer__move--to-source\"\n [disabled]=\"!canMoveLeft()\"\n [attr.aria-label]=\"resolvedToSource()\"\n (click)=\"moveLeft()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n </div>\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -477,6 +477,30 @@ class WrTreeHarness extends ContentContainerComponentHarness {
|
|
|
477
477
|
}
|
|
478
478
|
await clear.click();
|
|
479
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Backspace on the closed overlay trigger — the keyboard route to the ×.
|
|
482
|
+
*
|
|
483
|
+
* In single mode it clears (only while `clearable` is on); in multi mode it
|
|
484
|
+
* drops the LAST chip, one per call, whatever `clearable` says. Both ×
|
|
485
|
+
* controls are `tabindex="-1"` spans inside the trigger, so a key can never
|
|
486
|
+
* reach them directly. In single mode this is the ONLY keyboard route back to
|
|
487
|
+
* no selection; in multi mode a keyboard user can also toggle a row off in the
|
|
488
|
+
* panel ({@link selectNode} with `additive`). {@link clear} / {@link removeChip}
|
|
489
|
+
* prove the pointer path and nothing more.
|
|
490
|
+
*
|
|
491
|
+
* The key is honoured only on the CLOSED trigger, so an open panel is closed
|
|
492
|
+
* first with Escape — the step a keyboard user takes too, since focus sits in
|
|
493
|
+
* the panel while it is up.
|
|
494
|
+
*
|
|
495
|
+
* Does nothing, rather than throwing, where the component does nothing —
|
|
496
|
+
* nothing selected, disabled or read-only — so a spec asserts the outcome on
|
|
497
|
+
* the value.
|
|
498
|
+
*/
|
|
499
|
+
async clearByKeyboard() {
|
|
500
|
+
if (await this.isOpen())
|
|
501
|
+
await this.close();
|
|
502
|
+
await (await this.trigger()).sendKeys(TestKey.BACKSPACE);
|
|
503
|
+
}
|
|
480
504
|
/**
|
|
481
505
|
* The nodes currently rendered, in the order they are shown.
|
|
482
506
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-tree-testing.mjs","sources":["../../../projects/lib/tree/testing/wr-tree-node-harness.ts","../../../projects/lib/tree/testing/wr-tree-harness.ts","../../../projects/lib/tree/testing/ngwr-tree-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrTreeNodeHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one node of a `<wr-tree>` — a `role=\"treeitem\"` row.\n *\n * Everything about a tree's shape that a screen reader gets lives on these rows:\n * the list itself is FLAT, with no `role=\"group\"` and no nesting, so `aria-level`,\n * `aria-posinset` and `aria-setsize` are the only place the hierarchy exists. The\n * indent is decoration.\n *\n * @example\n * ```ts\n * const src = await tree.getNode({ label: 'src' });\n *\n * await src.expand();\n * expect(await src.getLevel()).toBe(1);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrTreeNodeHarness extends ComponentHarness {\n /**\n * The row, and nothing else the list can hold.\n *\n * A windowed tree pads its list with `<li class=\"wr-tree__spacer\" role=\"presentation\">`\n * at each end, so `li` would hand back up to two \"nodes\" with no label and no\n * state. `[role=\"treeitem\"]` selects exactly the same set as this class.\n */\n static hostSelector = '.wr-tree__row';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrTreeNodeHarnessFilters = {}): HarnessPredicate<WrTreeNodeHarness> {\n return new HarnessPredicate(WrTreeNodeHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('selected', options.selected, async (harness, selected) => (await harness.isSelected()) === selected)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption(\n 'expanded',\n options.expanded,\n // A leaf is not a closed branch: it announces no `aria-expanded` at all, so\n // it matches neither value rather than answering `false` and turning\n // `{ expanded: false }` into \"every row that is not open\".\n async (harness, expanded) => (await harness.isExpandable()) && (await harness.isExpanded()) === expanded\n )\n .addOption('level', options.level, async (harness, level) => (await harness.getLevel()) === level);\n }\n\n /**\n * The node's label.\n *\n * Read from `.wr-tree__label` rather than from the row's text, so the answer stays\n * the label alone however the row is decorated around it.\n */\n async getLabel(): Promise<string> {\n return (await this.locatorFor('.wr-tree__label')()).text();\n }\n\n /** The node's 1-based depth, as the row announces it (`aria-level`). */\n async getLevel(): Promise<number> {\n return this.announcedNumber('aria-level');\n }\n\n /**\n * The node's 1-based position within its SIBLING GROUP (`aria-posinset`) — not its\n * position in the flat list of visible rows.\n *\n * `src`'s first child is `posinset` 1 even though it is the second row on screen.\n * That is what ARIA asks for, and the whole reason the attribute is needed here:\n * the rendered list cannot express the nesting on its own.\n */\n async getPosInSet(): Promise<number> {\n return this.announcedNumber('aria-posinset');\n }\n\n /** How many nodes are in this node's sibling group, itself included (`aria-setsize`). */\n async getSetSize(): Promise<number> {\n return this.announcedNumber('aria-setsize');\n }\n\n /**\n * The node's index in the full list of VISIBLE nodes — every rendered row plus the\n * ones a window left out, in the order the tree would show them.\n *\n * It is the index the tree's own keyboard code addresses rows by, and while the\n * tree is windowed it is the only way to place a row: DOM order then says nothing,\n * because the rows in the DOM are a slice.\n */\n async getIndex(): Promise<number> {\n return this.announcedNumber('data-tree-index');\n }\n\n /**\n * The row's DOM id, or `null`.\n *\n * Only a windowed tree gives its rows ids, because that is the shape where the\n * list keeps focus and names the cursor row with `aria-activedescendant` — the id\n * exists to be pointed at.\n */\n async getId(): Promise<string | null> {\n return (await this.host()).getAttribute('id');\n }\n\n /**\n * Whether the node is a branch — it has children, whether or not they are showing.\n *\n * From `aria-expanded`: only a branch carries it, and a branch that does not is\n * announced as a leaf, so the user never learns there is anything underneath. The\n * toggle element cannot answer this — a leaf renders a `<span>` with the same\n * `.wr-tree__toggle` class purely to hold the indent.\n */\n async isExpandable(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) !== null;\n }\n\n /** Whether the branch is open. A leaf is never expanded. */\n async isExpanded(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) === 'true';\n }\n\n /** Open the branch. Already-open branches are left alone; a leaf throws. */\n async expand(): Promise<void> {\n if (await this.isExpanded()) return;\n await this.toggleExpand();\n\n if (!(await this.isExpanded())) {\n throw new Error(\n `WrTreeNodeHarness.expand(): \"${await this.getLabel()}\" did not open. A disabled node ignores its ` +\n 'own toggle, and so does every node of a disabled tree.'\n );\n }\n }\n\n /** Close the branch. Already-closed branches are left alone; a leaf throws. */\n async collapse(): Promise<void> {\n if (!(await this.isExpanded())) {\n // A closed branch is fine to ask twice; a leaf is a mistake worth naming.\n await this.toggleElement();\n return;\n }\n await this.toggleExpand();\n\n if (await this.isExpanded()) {\n throw new Error(\n `WrTreeNodeHarness.collapse(): \"${await this.getLabel()}\" did not close. A disabled node ignores ` +\n 'its own toggle, and so does every node of a disabled tree.'\n );\n }\n }\n\n /**\n * Click the branch's expand toggle.\n *\n * Expanding is the TOGGLE's job and not the row's: a click anywhere else in the row\n * selects instead, which is the one thing a spec driving this by hand tends to get\n * wrong.\n */\n async toggleExpand(): Promise<void> {\n await (await this.toggleElement()).click();\n }\n\n /**\n * Whether the node is selected.\n *\n * From `aria-selected` rather than the `--selected` modifier: both are public, but\n * the attribute is what a screen reader acts on, and a row that looks selected\n * without saying so is the bug worth catching.\n */\n async isSelected(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /**\n * Whether the tree offers selection at all.\n *\n * A `selectionMode=\"none\"` tree announces NO `aria-selected` on its rows, which is\n * how an unselectable node differs from one that is merely unselected — the two\n * look identical otherwise.\n */\n async isSelectable(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) !== null;\n }\n\n /**\n * Whether the node refuses interaction (`aria-disabled`).\n *\n * True for EVERY node while the whole tree is disabled, not just for the ones the\n * data marks — the row announces the effective state, which is the one a user runs\n * into.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether the node is where the keyboard cursor sits.\n *\n * The two patterns publish the cursor differently, and this covers both: a tree\n * that renders every row hands the single tab stop to the cursor row (roving\n * tabindex), while a windowed one keeps focus on the list and marks the row with\n * the `--active` modifier plus the list's `aria-activedescendant`.\n * `WrTreeHarness.getActiveNodeLabel()` reads that reference itself.\n */\n async isActive(): Promise<boolean> {\n const host = await this.host();\n return (await host.getAttribute('tabindex')) === '0' || host.hasClass('wr-tree__row--active');\n }\n\n /**\n * Whether the row holds real DOM focus.\n *\n * Deliberately separate from {@link isActive}: in a windowed tree the two DIVERGE\n * by design — focus stays on the `role=\"tree\"` list and only the announced cursor\n * moves — so a spec asserting that a key press moved focus needs to know which of\n * the two it is asking about.\n */\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /**\n * Click the node — which SELECTS it. A disabled node, and any node of a\n * `selectionMode=\"none\"` tree, ignores the click.\n *\n * In multi mode a plain click REPLACES the selection rather than adding to it (the\n * component's deliberate choice: one selected node clicked again clears it, and any\n * other click starts over from that node). {@link ctrlClick} is how a selection is\n * accumulated.\n */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n\n /**\n * Ctrl-click the node — ADDS it to the selection, or removes it, in multi mode.\n *\n * The component takes Cmd as well as Ctrl; the harness sends Ctrl because a unit\n * test has no platform. In single mode it behaves exactly like {@link click}.\n */\n async ctrlClick(): Promise<void> {\n return (await this.host()).click({ control: true });\n }\n\n /** The branch's toggle button, or a failure that says this row is a leaf. */\n private async toggleElement(): Promise<TestElement> {\n // `button` is load-bearing: a leaf renders a `<span>` with the same\n // `.wr-tree__toggle` class to hold the indent, and taking that for a control\n // would report every leaf as a branch that refused to open.\n const toggle = await this.locatorForOptional('button.wr-tree__toggle')();\n if (!toggle) {\n throw new Error(`WrTreeNodeHarness: \"${await this.getLabel()}\" is a leaf — there is no branch to open or close.`);\n }\n return toggle;\n }\n\n /** A numeric attribute every row carries, or a failure that says the row is broken. */\n private async announcedNumber(attribute: string): Promise<number> {\n const value = await (await this.host()).getAttribute(attribute);\n if (value === null) {\n throw new Error(\n `WrTreeNodeHarness: this row announces no \\`${attribute}\\`. Every row of a \\`<wr-tree>\\` carries ` +\n 'one, so a missing value is a rendering fault rather than a state worth reporting as null.'\n );\n }\n return Number.parseInt(value, 10);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ContentContainerComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrTreeHarnessFilters, WrTreeNodeHarnessFilters } from './interfaces';\nimport { WrTreeNodeHarness } from './wr-tree-node-harness';\n\n/** How many levels {@link WrTreeHarness.expandAll} is willing to walk. */\nconst MAX_EXPAND_PASSES = 20;\n\n/**\n * Test harness for `<wr-tree>`, with {@link WrTreeNodeHarness} for one row.\n *\n * The component has two render shapes and this harness covers both:\n * - `openOn=\"inline\"` (default) — the rows live in the host, and the tree is always\n * showing them.\n * - `openOn=\"overlay\"` — a `role=\"combobox\"` trigger opens a panel that is a template\n * portal in the shared overlay container, a sibling of the whole app. Nothing in it\n * is a descendant of the host, so every row is read through the document root,\n * SCOPED by the `aria-controls` id the trigger publishes — which is what keeps two\n * trees on one page from answering with each other's nodes.\n *\n * A tree is data-driven and projects nothing, so the scoped loader exists to point the\n * node queries at the right shape rather than to reach a consumer's own components: the\n * inherited `getHarness(…)` resolves wherever THIS tree's rows are, and there is nothing\n * else in there.\n *\n * Two things the harness cannot do, both jsdom rather than the component:\n * - it never clicks a row by coordinate, and it drives the cursor with the KEYBOARD,\n * which is the accessible path anyway. There is no layout in a unit test, so a\n * hit test would answer with nothing.\n * - it offers no SCROLL for a windowed tree. jsdom does remember a `scrollTop` write,\n * but it fires no `scroll` event for it, so the tree — which learns its offset from\n * that event — would not move until the caller also dispatched one and waited a\n * frame: a scroll gesture that only works because the test faked the notification.\n * The keyboard moves the window honestly instead — see {@link focusLast}. (jsdom has\n * no `Element.scrollTo` at all, which the tree calls while windowed, so a spec for\n * that shape has to stub it.)\n *\n * @example\n * ```ts\n * const tree = await loader.getHarness(WrTreeHarness);\n *\n * await (await tree.getNode({ label: 'src' })).expand();\n * await tree.selectNode({ label: 'main.ts' });\n *\n * expect(await tree.getSelectedLabels()).toEqual(['main.ts']);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrTreeHarness extends ContentContainerComponentHarness {\n static hostSelector = 'wr-tree';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrTreeHarnessFilters = {}): HarnessPredicate<WrTreeHarness> {\n return new HarnessPredicate(WrTreeHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.triggerTextIfAny(), text)\n )\n .addOption('nodeLabel', options.nodeLabel, async (harness, label) => {\n for (const shown of await harness.nodeLabelsIfShowing()) {\n if (await HarnessPredicate.stringMatches(shown, label)) return true;\n }\n return false;\n })\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** Whether this tree is the combobox shape (`openOn=\"overlay\"`) rather than inline. */\n async isOverlay(): Promise<boolean> {\n return (await this.host()).hasClass('wr-tree--combobox');\n }\n\n /**\n * Whether the rows are showing.\n *\n * From the trigger's `aria-expanded` rather than the `--open` modifier: both are\n * public, but that attribute is the promise the combobox makes to a screen reader.\n * An inline tree is always open — its rows are part of the host and there is\n * nothing to open or close.\n */\n async isOpen(): Promise<boolean> {\n if (!(await this.isOverlay())) return true;\n return (await (await this.trigger()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Open the panel. An already-open tree — and every inline one — is left alone.\n *\n * Note for a spec with two overlay trees: this clicks, and a click is an outside\n * pointer event for every other open overlay, so it closes any panel already up.\n * There is no second gesture to swap in: the trigger is opened by its `click`\n * handler, and while a real keyboard user reaches that through a `<button>`'s native\n * activation, a SYNTHETIC Enter / Space never produces a click — so a harness cannot\n * open a panel any other way.\n */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n\n await (await this.trigger()).click();\n if (await this.isOpen()) return;\n\n throw new Error(\n 'WrTreeHarness.open(): the panel did not open. A disabled tree refuses the trigger and closes any ' +\n 'panel that was up, so check `disabled` first.'\n );\n }\n\n /** Close the panel with Escape. An already-closed tree is left alone. */\n async close(): Promise<void> {\n // Not a no-op for an inline tree: asking to close one means the spec believes it\n // has a panel, and it does not.\n const trigger = await this.trigger();\n if (!(await this.isOpen())) return;\n\n // Escape reaches the tree through the CDK's keyboard dispatcher, which hands it to\n // the top-most overlay — so it does not matter that focus may be on a row inside.\n await trigger.sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * Whether the tree refuses interaction.\n *\n * The host modifier is the only answer both shapes carry: an inline tree has no\n * control of its own to mark `disabled`, and a closed overlay tree has no rows to\n * read `aria-disabled` from. The rows do announce it — see\n * {@link WrTreeNodeHarness.isDisabled}.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-tree--disabled');\n }\n\n /**\n * Whether the tree takes more than one selected node.\n *\n * From the list's `aria-multiselectable`, which is what a screen reader acts on. The\n * list does not exist while an overlay panel is closed, so the host modifier answers\n * then — it is only painted in the combobox shape, which is exactly the shape that\n * can be asked while closed.\n */\n async isMultiple(): Promise<boolean> {\n if (await this.isOpen()) {\n return (await (await this.list()).getAttribute('aria-multiselectable')) === 'true';\n }\n return (await this.host()).hasClass('wr-tree--multi');\n }\n\n /**\n * How much the tree lets you select.\n *\n * `'none'` is not \"nothing selected\": it is a tree whose rows announce no\n * `aria-selected` at all, and telling that from `'single'` needs a row — so an empty\n * tree throws rather than guessing.\n */\n async getSelectionMode(): Promise<'none' | 'single' | 'multi'> {\n if (await this.isMultiple()) return 'multi';\n\n const [first] = await this.getNodes();\n if (!first) {\n throw new Error(\n 'WrTreeHarness.getSelectionMode(): this tree renders no nodes, and single vs none is only visible ' +\n 'on a row — `selectionMode=\"none\"` is the absence of `aria-selected`, so there is nothing to read.'\n );\n }\n return (await first.isSelectable()) ? 'single' : 'none';\n }\n\n /**\n * Whether the list is WINDOWED right now.\n *\n * Read from the list's modifier rather than from the input, and that is the point:\n * `virtualScroll` only takes effect when there is something to window, so an empty\n * tree renders the plain shape however the input is set.\n */\n async isVirtual(): Promise<boolean> {\n return (await this.list()).hasClass('wr-tree__list--virtual');\n }\n\n /**\n * The role the list announces — `tree`.\n *\n * Worth asking: rendered without it, the rows are a bag of names, identical on\n * screen and structureless to anyone not looking at it.\n */\n async getRole(): Promise<string | null> {\n return (await this.list()).getAttribute('role');\n }\n\n /**\n * What the trigger shows as the selection: the chip labels joined by `', '` in multi\n * mode, the label in single mode, and `''` when nothing is selected (the placeholder\n * is showing). The `+N more` chip a `maxTagCount` produces is NOT part of it — it is\n * a count rather than a selection, and {@link getOverflowText} reads it. Inline trees\n * have no trigger and throw.\n */\n async getValueText(): Promise<string> {\n await this.trigger();\n\n const chips = await this.getChipLabels();\n if (chips.length > 0) return chips.join(', ');\n\n const value = await this.locatorForOptional('.wr-tree__value')();\n return value ? value.text() : '';\n }\n\n /** The placeholder, or `null` when a selection is hiding it. */\n async getPlaceholder(): Promise<string | null> {\n await this.trigger();\n\n const placeholder = await this.locatorForOptional('.wr-tree__placeholder')();\n return placeholder ? placeholder.text() : null;\n }\n\n /**\n * The chip labels on the trigger, in order (multi mode).\n *\n * The `+N more` overflow chip is not one of them — it is a count, not a selection.\n * {@link getOverflowText} reads it.\n */\n async getChipLabels(): Promise<string[]> {\n await this.trigger();\n\n const labels = await this.locatorForAll('.wr-tree__chip-label')();\n return Promise.all(labels.map(label => label.text()));\n }\n\n /**\n * The `+N more` chip's text, or `null` when every selected node has a chip of its\n * own. Only a `maxTagCount` below the number selected produces one.\n */\n async getOverflowText(): Promise<string | null> {\n await this.trigger();\n\n const more = await this.locatorForOptional('.wr-tree__chip--more')();\n return more ? more.text() : null;\n }\n\n /** Remove one chip by its label. */\n async removeChip(label: string): Promise<void> {\n const labels = await this.getChipLabels();\n const index = labels.indexOf(label);\n if (index < 0) {\n throw new Error(\n `WrTreeHarness.removeChip(): no chip labelled \"${label}\". The trigger shows [${labels.join(', ')}] — ` +\n 'a selection past `maxTagCount` has no chip to remove.'\n );\n }\n\n // Label and remove control are emitted once per chip by the same loop, so the two\n // lists line up by index.\n const removes = await this.locatorForAll('.wr-tree__chip-remove')();\n await removes[index].click();\n }\n\n /** Click the clear (×) control on the trigger. */\n async clear(): Promise<void> {\n await this.trigger();\n\n const clear = await this.locatorForOptional('.wr-tree__clear')();\n if (!clear) {\n throw new Error(\n 'WrTreeHarness.clear(): this trigger has no clear control — `clearable` is off, nothing is ' +\n 'selected, or the tree is disabled.'\n );\n }\n await clear.click();\n }\n\n /**\n * The nodes currently rendered, in the order they are shown.\n *\n * A collapsed branch's children are not rendered at all, so this is the VISIBLE\n * tree — expand first to reach deeper.\n *\n * While {@link isVirtual} is true it is narrower still: the list holds only the\n * window around the scroll offset — padded by a spacer at whichever end has rows\n * outside it, so a window resting at the top has one and a window in the middle has\n * two — and this returns the WINDOW, not the dataset. Nothing in the DOM carries a\n * total —\n * `aria-setsize` counts a SIBLING GROUP, not the flat list — so the harness cannot\n * offer one; {@link WrTreeNodeHarness.getIndex} places each row in the full visible\n * list, and the `[nodes]` data is where the total actually lives.\n *\n * Throws while an overlay panel is closed: the rows do not exist anywhere until it\n * opens, and an empty array reads like a tree that rendered nothing.\n */\n async getNodes(filters: WrTreeNodeHarnessFilters = {}): Promise<WrTreeNodeHarness[]> {\n return this.getAllHarnesses(WrTreeNodeHarness.with(filters));\n }\n\n /** The labels of the nodes currently rendered, in order. */\n async getNodeLabels(): Promise<string[]> {\n const nodes = await this.getNodes();\n return Promise.all(nodes.map(node => node.getLabel()));\n }\n\n /** The first node matching the filters, or a failure that says what is showing. */\n async getNode(filters: WrTreeNodeHarnessFilters): Promise<WrTreeNodeHarness> {\n const [node] = await this.getNodes(filters);\n if (!node) {\n const showing = await this.getNodeLabels();\n throw new Error(\n `WrTreeHarness.getNode(): no node matched ${JSON.stringify(filters)}. The tree is showing ` +\n `[${showing.join(', ')}] — a node inside a collapsed branch is not rendered at all, and a ` +\n 'windowed tree only renders its window.'\n );\n }\n return node;\n }\n\n /**\n * Open the panel if needed, then select the first node matching the filters.\n *\n * `additive` ctrl-clicks instead, which is how a multi-mode selection is built up: a\n * plain click there REPLACES the selection, by the component's own choice.\n *\n * Throws rather than clicking into the void on a disabled node or a\n * `selectionMode=\"none\"` tree — both ignore the click, and the silence turns into a\n * confusing failure at the next assertion. Reach for `getNode().click()` when\n * proving exactly that.\n */\n async selectNode(filters: WrTreeNodeHarnessFilters, options: { additive?: boolean } = {}): Promise<void> {\n await this.open();\n\n const node = await this.getNode(filters);\n const label = await node.getLabel();\n\n if (!(await node.isSelectable())) {\n throw new Error(\n `WrTreeHarness.selectNode(): \"${label}\" announces no selected state, so this tree is in ` +\n 'selectionMode=\"none\" — clicking it cannot select anything.'\n );\n }\n if (await node.isDisabled()) {\n throw new Error(\n `WrTreeHarness.selectNode(): \"${label}\" is disabled and the tree ignores the click. A disabled ` +\n 'TREE disables every node — check isDisabled() on the tree too.'\n );\n }\n\n await (options.additive === true ? node.ctrlClick() : node.click());\n }\n\n /**\n * The labels of the selected nodes, in row order.\n *\n * Only among the rows that are RENDERED: a selected node inside a collapsed branch,\n * or outside a window, is not here. The `[(selected)]` model is the whole set.\n */\n async getSelectedLabels(): Promise<string[]> {\n const nodes = await this.getNodes({ selected: true });\n return Promise.all(nodes.map(node => node.getLabel()));\n }\n\n /**\n * Open every branch, level by level — each pass reveals the next one down.\n *\n * Refuses on a windowed tree instead of half-doing it: only the window is rendered,\n * so the branches outside it cannot be reached from the DOM at all. Write the\n * `[(expanded)]` model there.\n */\n async expandAll(): Promise<void> {\n if (await this.isVirtual()) {\n throw new Error(\n 'WrTreeHarness.expandAll(): a windowed tree renders only its window, so this cannot reach the ' +\n 'branches outside it. Set the `[(expanded)]` model instead.'\n );\n }\n\n for (let pass = 0; pass < MAX_EXPAND_PASSES; pass++) {\n const collapsed = await this.getNodes({ expanded: false });\n if (collapsed.length === 0) return;\n for (const node of collapsed) await node.expand();\n }\n\n throw new Error(\n `WrTreeHarness.expandAll(): still finding closed branches after ${MAX_EXPAND_PASSES} levels — is the ` +\n 'node data recursive?'\n );\n }\n\n /**\n * The label of the node the keyboard cursor is on, or `null`.\n *\n * Read the way the shape publishes it: a windowed tree keeps focus on the list and\n * names the row with `aria-activedescendant` (which goes `null` while the cursor has\n * been scrolled out of the window, so the reference never dangles at a row that was\n * removed), while a plain one hands the single tab stop to the cursor row.\n */\n async getActiveNodeLabel(): Promise<string | null> {\n const nodes = await this.getNodes();\n\n if (await this.isVirtual()) {\n const active = await (await this.list()).getAttribute('aria-activedescendant');\n if (!active) return null;\n for (const node of nodes) {\n if ((await node.getId()) === active) return node.getLabel();\n }\n return null;\n }\n\n for (const node of nodes) {\n if (await node.isActive()) return node.getLabel();\n }\n return null;\n }\n\n /** Move the cursor down one row — ArrowDown. Stops at the last row. */\n async focusNext(): Promise<void> {\n await this.press(TestKey.DOWN_ARROW);\n }\n\n /** Move the cursor up one row — ArrowUp. Stops at the first row. */\n async focusPrevious(): Promise<void> {\n await this.press(TestKey.UP_ARROW);\n }\n\n /** Move the cursor to the first row — Home. */\n async focusFirst(): Promise<void> {\n await this.press(TestKey.HOME);\n }\n\n /**\n * Move the cursor to the last row — End.\n *\n * On a windowed tree this also moves the WINDOW: the tree writes its scroll offset\n * synchronously so the row it is about to point at exists in the same change\n * detection pass. It is the honest way to reach the far end of a window in a unit\n * test — jsdom keeps a `scrollTop` write but never fires the `scroll` event the tree\n * listens for, so a scroll gesture would move the window only for a test that also\n * dispatched the notification itself.\n */\n async focusLast(): Promise<void> {\n await this.press(TestKey.END);\n }\n\n /**\n * ArrowRight on the cursor row — the APG's one key with two jobs: it OPENS a closed\n * branch, and on an already-open one it steps into the first child. A leaf ignores\n * it.\n *\n * These two send the PHYSICAL key, because that is what a keyboard has. Under\n * `dir=\"rtl\"` the tree follows the indent and swaps the pair, so a spec exercising\n * an RTL tree opens with {@link collapseActive} and closes with this one. The\n * names describe the LTR job, which is the one worth naming.\n */\n async expandActive(): Promise<void> {\n await this.press(TestKey.RIGHT_ARROW);\n }\n\n /**\n * ArrowLeft on the cursor row — CLOSES an open branch, and on anything else steps\n * out to the parent row. A root-level leaf ignores it.\n *\n * Physical, and mirrored by the component under `dir=\"rtl\"` — see\n * {@link expandActive}.\n */\n async collapseActive(): Promise<void> {\n await this.press(TestKey.LEFT_ARROW);\n }\n\n /**\n * Select the cursor row from the keyboard — Enter.\n *\n * `additive` holds Ctrl, the keyboard twin of {@link WrTreeNodeHarness.ctrlClick}:\n * in multi mode a plain Enter replaces the selection, Ctrl-Enter adds to it. A\n * disabled row is skipped by the component, silently — assert the model.\n */\n async selectActive(options: { additive?: boolean } = {}): Promise<void> {\n const list = await this.list();\n if (options.additive === true) {\n await list.sendKeys({ control: true }, TestKey.ENTER);\n return;\n }\n await list.sendKeys(TestKey.ENTER);\n }\n\n /**\n * The loader every node query runs through — the host inline, THIS tree's overlay\n * panel otherwise.\n *\n * Scoped by the published panel id rather than by `.wr-tree-panel`: the overlay\n * container is shared, so a bare class query would answer with whichever tree\n * happened to open first.\n */\n protected override async getRootHarnessLoader(): Promise<HarnessLoader> {\n if (!(await this.isOverlay())) return super.getRootHarnessLoader();\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.panelId()}`);\n }\n\n /**\n * Send a key to the `role=\"tree\"` list.\n *\n * The list owns the keydown handler in both patterns — the roving-tabindex one\n * focuses a row, whose event bubbles to exactly here — so this is the honest target\n * either way, and the one that works when jsdom has left real focus on `<body>`.\n */\n private async press(key: TestKey): Promise<void> {\n await (await this.list()).sendKeys(key);\n }\n\n /**\n * The `<ul role=\"tree\">` of THIS tree — in the host inline, in this tree's own\n * overlay panel otherwise.\n */\n private async list(): Promise<TestElement> {\n if (!(await this.isOverlay())) return this.locatorFor('.wr-tree__list')();\n return this.documentRootLocatorFactory().locatorFor(`#${await this.panelId()} .wr-tree__list`)();\n }\n\n /** The combobox trigger, or a failure that says this tree renders inline. */\n private async trigger(): Promise<TestElement> {\n const trigger = await this.locatorForOptional('.wr-tree__trigger')();\n if (!trigger) {\n throw new Error(\n 'WrTreeHarness: this tree renders inline, so it has no trigger, no chips and no panel — its rows ' +\n 'are part of the host. `openOn=\"overlay\"` is the combobox shape.'\n );\n }\n return trigger;\n }\n\n /** The id the trigger publishes as `aria-controls`, which its panel carries. */\n private async panelId(): Promise<string> {\n const id = await (await this.trigger()).getAttribute('aria-controls');\n if (!id) {\n throw new Error(\n 'WrTreeHarness: the trigger publishes no `aria-controls` id, so its panel cannot be told apart ' +\n \"from another tree's.\"\n );\n }\n if (!(await this.isOpen())) {\n throw new Error('WrTreeHarness: the panel is closed — call open() first. A closed tree renders no rows.');\n }\n return id;\n }\n\n /** The trigger's text for the `text` filter, or `null` for an inline tree (which never matches). */\n private async triggerTextIfAny(): Promise<string | null> {\n const trigger = await this.locatorForOptional('.wr-tree__trigger')();\n return trigger ? this.getValueText() : null;\n }\n\n /** The rendered labels for the `nodeLabel` filter — a closed tree matches nothing rather than failing. */\n private async nodeLabelsIfShowing(): Promise<string[]> {\n if (!(await this.isOpen())) return [];\n return this.getNodeLabels();\n }\n}\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":"ngwr-tree-testing.mjs","sources":["../../../projects/lib/tree/testing/wr-tree-node-harness.ts","../../../projects/lib/tree/testing/wr-tree-harness.ts","../../../projects/lib/tree/testing/ngwr-tree-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrTreeNodeHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one node of a `<wr-tree>` — a `role=\"treeitem\"` row.\n *\n * Everything about a tree's shape that a screen reader gets lives on these rows:\n * the list itself is FLAT, with no `role=\"group\"` and no nesting, so `aria-level`,\n * `aria-posinset` and `aria-setsize` are the only place the hierarchy exists. The\n * indent is decoration.\n *\n * @example\n * ```ts\n * const src = await tree.getNode({ label: 'src' });\n *\n * await src.expand();\n * expect(await src.getLevel()).toBe(1);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrTreeNodeHarness extends ComponentHarness {\n /**\n * The row, and nothing else the list can hold.\n *\n * A windowed tree pads its list with `<li class=\"wr-tree__spacer\" role=\"presentation\">`\n * at each end, so `li` would hand back up to two \"nodes\" with no label and no\n * state. `[role=\"treeitem\"]` selects exactly the same set as this class.\n */\n static hostSelector = '.wr-tree__row';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrTreeNodeHarnessFilters = {}): HarnessPredicate<WrTreeNodeHarness> {\n return new HarnessPredicate(WrTreeNodeHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('selected', options.selected, async (harness, selected) => (await harness.isSelected()) === selected)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption(\n 'expanded',\n options.expanded,\n // A leaf is not a closed branch: it announces no `aria-expanded` at all, so\n // it matches neither value rather than answering `false` and turning\n // `{ expanded: false }` into \"every row that is not open\".\n async (harness, expanded) => (await harness.isExpandable()) && (await harness.isExpanded()) === expanded\n )\n .addOption('level', options.level, async (harness, level) => (await harness.getLevel()) === level);\n }\n\n /**\n * The node's label.\n *\n * Read from `.wr-tree__label` rather than from the row's text, so the answer stays\n * the label alone however the row is decorated around it.\n */\n async getLabel(): Promise<string> {\n return (await this.locatorFor('.wr-tree__label')()).text();\n }\n\n /** The node's 1-based depth, as the row announces it (`aria-level`). */\n async getLevel(): Promise<number> {\n return this.announcedNumber('aria-level');\n }\n\n /**\n * The node's 1-based position within its SIBLING GROUP (`aria-posinset`) — not its\n * position in the flat list of visible rows.\n *\n * `src`'s first child is `posinset` 1 even though it is the second row on screen.\n * That is what ARIA asks for, and the whole reason the attribute is needed here:\n * the rendered list cannot express the nesting on its own.\n */\n async getPosInSet(): Promise<number> {\n return this.announcedNumber('aria-posinset');\n }\n\n /** How many nodes are in this node's sibling group, itself included (`aria-setsize`). */\n async getSetSize(): Promise<number> {\n return this.announcedNumber('aria-setsize');\n }\n\n /**\n * The node's index in the full list of VISIBLE nodes — every rendered row plus the\n * ones a window left out, in the order the tree would show them.\n *\n * It is the index the tree's own keyboard code addresses rows by, and while the\n * tree is windowed it is the only way to place a row: DOM order then says nothing,\n * because the rows in the DOM are a slice.\n */\n async getIndex(): Promise<number> {\n return this.announcedNumber('data-tree-index');\n }\n\n /**\n * The row's DOM id, or `null`.\n *\n * Only a windowed tree gives its rows ids, because that is the shape where the\n * list keeps focus and names the cursor row with `aria-activedescendant` — the id\n * exists to be pointed at.\n */\n async getId(): Promise<string | null> {\n return (await this.host()).getAttribute('id');\n }\n\n /**\n * Whether the node is a branch — it has children, whether or not they are showing.\n *\n * From `aria-expanded`: only a branch carries it, and a branch that does not is\n * announced as a leaf, so the user never learns there is anything underneath. The\n * toggle element cannot answer this — a leaf renders a `<span>` with the same\n * `.wr-tree__toggle` class purely to hold the indent.\n */\n async isExpandable(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) !== null;\n }\n\n /** Whether the branch is open. A leaf is never expanded. */\n async isExpanded(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) === 'true';\n }\n\n /** Open the branch. Already-open branches are left alone; a leaf throws. */\n async expand(): Promise<void> {\n if (await this.isExpanded()) return;\n await this.toggleExpand();\n\n if (!(await this.isExpanded())) {\n throw new Error(\n `WrTreeNodeHarness.expand(): \"${await this.getLabel()}\" did not open. A disabled node ignores its ` +\n 'own toggle, and so does every node of a disabled tree.'\n );\n }\n }\n\n /** Close the branch. Already-closed branches are left alone; a leaf throws. */\n async collapse(): Promise<void> {\n if (!(await this.isExpanded())) {\n // A closed branch is fine to ask twice; a leaf is a mistake worth naming.\n await this.toggleElement();\n return;\n }\n await this.toggleExpand();\n\n if (await this.isExpanded()) {\n throw new Error(\n `WrTreeNodeHarness.collapse(): \"${await this.getLabel()}\" did not close. A disabled node ignores ` +\n 'its own toggle, and so does every node of a disabled tree.'\n );\n }\n }\n\n /**\n * Click the branch's expand toggle.\n *\n * Expanding is the TOGGLE's job and not the row's: a click anywhere else in the row\n * selects instead, which is the one thing a spec driving this by hand tends to get\n * wrong.\n */\n async toggleExpand(): Promise<void> {\n await (await this.toggleElement()).click();\n }\n\n /**\n * Whether the node is selected.\n *\n * From `aria-selected` rather than the `--selected` modifier: both are public, but\n * the attribute is what a screen reader acts on, and a row that looks selected\n * without saying so is the bug worth catching.\n */\n async isSelected(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /**\n * Whether the tree offers selection at all.\n *\n * A `selectionMode=\"none\"` tree announces NO `aria-selected` on its rows, which is\n * how an unselectable node differs from one that is merely unselected — the two\n * look identical otherwise.\n */\n async isSelectable(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) !== null;\n }\n\n /**\n * Whether the node refuses interaction (`aria-disabled`).\n *\n * True for EVERY node while the whole tree is disabled, not just for the ones the\n * data marks — the row announces the effective state, which is the one a user runs\n * into.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether the node is where the keyboard cursor sits.\n *\n * The two patterns publish the cursor differently, and this covers both: a tree\n * that renders every row hands the single tab stop to the cursor row (roving\n * tabindex), while a windowed one keeps focus on the list and marks the row with\n * the `--active` modifier plus the list's `aria-activedescendant`.\n * `WrTreeHarness.getActiveNodeLabel()` reads that reference itself.\n */\n async isActive(): Promise<boolean> {\n const host = await this.host();\n return (await host.getAttribute('tabindex')) === '0' || host.hasClass('wr-tree__row--active');\n }\n\n /**\n * Whether the row holds real DOM focus.\n *\n * Deliberately separate from {@link isActive}: in a windowed tree the two DIVERGE\n * by design — focus stays on the `role=\"tree\"` list and only the announced cursor\n * moves — so a spec asserting that a key press moved focus needs to know which of\n * the two it is asking about.\n */\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /**\n * Click the node — which SELECTS it. A disabled node, and any node of a\n * `selectionMode=\"none\"` tree, ignores the click.\n *\n * In multi mode a plain click REPLACES the selection rather than adding to it (the\n * component's deliberate choice: one selected node clicked again clears it, and any\n * other click starts over from that node). {@link ctrlClick} is how a selection is\n * accumulated.\n */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n\n /**\n * Ctrl-click the node — ADDS it to the selection, or removes it, in multi mode.\n *\n * The component takes Cmd as well as Ctrl; the harness sends Ctrl because a unit\n * test has no platform. In single mode it behaves exactly like {@link click}.\n */\n async ctrlClick(): Promise<void> {\n return (await this.host()).click({ control: true });\n }\n\n /** The branch's toggle button, or a failure that says this row is a leaf. */\n private async toggleElement(): Promise<TestElement> {\n // `button` is load-bearing: a leaf renders a `<span>` with the same\n // `.wr-tree__toggle` class to hold the indent, and taking that for a control\n // would report every leaf as a branch that refused to open.\n const toggle = await this.locatorForOptional('button.wr-tree__toggle')();\n if (!toggle) {\n throw new Error(`WrTreeNodeHarness: \"${await this.getLabel()}\" is a leaf — there is no branch to open or close.`);\n }\n return toggle;\n }\n\n /** A numeric attribute every row carries, or a failure that says the row is broken. */\n private async announcedNumber(attribute: string): Promise<number> {\n const value = await (await this.host()).getAttribute(attribute);\n if (value === null) {\n throw new Error(\n `WrTreeNodeHarness: this row announces no \\`${attribute}\\`. Every row of a \\`<wr-tree>\\` carries ` +\n 'one, so a missing value is a rendering fault rather than a state worth reporting as null.'\n );\n }\n return Number.parseInt(value, 10);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ContentContainerComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrTreeHarnessFilters, WrTreeNodeHarnessFilters } from './interfaces';\nimport { WrTreeNodeHarness } from './wr-tree-node-harness';\n\n/** How many levels {@link WrTreeHarness.expandAll} is willing to walk. */\nconst MAX_EXPAND_PASSES = 20;\n\n/**\n * Test harness for `<wr-tree>`, with {@link WrTreeNodeHarness} for one row.\n *\n * The component has two render shapes and this harness covers both:\n * - `openOn=\"inline\"` (default) — the rows live in the host, and the tree is always\n * showing them.\n * - `openOn=\"overlay\"` — a `role=\"combobox\"` trigger opens a panel that is a template\n * portal in the shared overlay container, a sibling of the whole app. Nothing in it\n * is a descendant of the host, so every row is read through the document root,\n * SCOPED by the `aria-controls` id the trigger publishes — which is what keeps two\n * trees on one page from answering with each other's nodes.\n *\n * A tree is data-driven and projects nothing, so the scoped loader exists to point the\n * node queries at the right shape rather than to reach a consumer's own components: the\n * inherited `getHarness(…)` resolves wherever THIS tree's rows are, and there is nothing\n * else in there.\n *\n * Two things the harness cannot do, both jsdom rather than the component:\n * - it never clicks a row by coordinate, and it drives the cursor with the KEYBOARD,\n * which is the accessible path anyway. There is no layout in a unit test, so a\n * hit test would answer with nothing.\n * - it offers no SCROLL for a windowed tree. jsdom does remember a `scrollTop` write,\n * but it fires no `scroll` event for it, so the tree — which learns its offset from\n * that event — would not move until the caller also dispatched one and waited a\n * frame: a scroll gesture that only works because the test faked the notification.\n * The keyboard moves the window honestly instead — see {@link focusLast}. (jsdom has\n * no `Element.scrollTo` at all, which the tree calls while windowed, so a spec for\n * that shape has to stub it.)\n *\n * @example\n * ```ts\n * const tree = await loader.getHarness(WrTreeHarness);\n *\n * await (await tree.getNode({ label: 'src' })).expand();\n * await tree.selectNode({ label: 'main.ts' });\n *\n * expect(await tree.getSelectedLabels()).toEqual(['main.ts']);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrTreeHarness extends ContentContainerComponentHarness {\n static hostSelector = 'wr-tree';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrTreeHarnessFilters = {}): HarnessPredicate<WrTreeHarness> {\n return new HarnessPredicate(WrTreeHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.triggerTextIfAny(), text)\n )\n .addOption('nodeLabel', options.nodeLabel, async (harness, label) => {\n for (const shown of await harness.nodeLabelsIfShowing()) {\n if (await HarnessPredicate.stringMatches(shown, label)) return true;\n }\n return false;\n })\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** Whether this tree is the combobox shape (`openOn=\"overlay\"`) rather than inline. */\n async isOverlay(): Promise<boolean> {\n return (await this.host()).hasClass('wr-tree--combobox');\n }\n\n /**\n * Whether the rows are showing.\n *\n * From the trigger's `aria-expanded` rather than the `--open` modifier: both are\n * public, but that attribute is the promise the combobox makes to a screen reader.\n * An inline tree is always open — its rows are part of the host and there is\n * nothing to open or close.\n */\n async isOpen(): Promise<boolean> {\n if (!(await this.isOverlay())) return true;\n return (await (await this.trigger()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Open the panel. An already-open tree — and every inline one — is left alone.\n *\n * Note for a spec with two overlay trees: this clicks, and a click is an outside\n * pointer event for every other open overlay, so it closes any panel already up.\n * There is no second gesture to swap in: the trigger is opened by its `click`\n * handler, and while a real keyboard user reaches that through a `<button>`'s native\n * activation, a SYNTHETIC Enter / Space never produces a click — so a harness cannot\n * open a panel any other way.\n */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n\n await (await this.trigger()).click();\n if (await this.isOpen()) return;\n\n throw new Error(\n 'WrTreeHarness.open(): the panel did not open. A disabled tree refuses the trigger and closes any ' +\n 'panel that was up, so check `disabled` first.'\n );\n }\n\n /** Close the panel with Escape. An already-closed tree is left alone. */\n async close(): Promise<void> {\n // Not a no-op for an inline tree: asking to close one means the spec believes it\n // has a panel, and it does not.\n const trigger = await this.trigger();\n if (!(await this.isOpen())) return;\n\n // Escape reaches the tree through the CDK's keyboard dispatcher, which hands it to\n // the top-most overlay — so it does not matter that focus may be on a row inside.\n await trigger.sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * Whether the tree refuses interaction.\n *\n * The host modifier is the only answer both shapes carry: an inline tree has no\n * control of its own to mark `disabled`, and a closed overlay tree has no rows to\n * read `aria-disabled` from. The rows do announce it — see\n * {@link WrTreeNodeHarness.isDisabled}.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-tree--disabled');\n }\n\n /**\n * Whether the tree takes more than one selected node.\n *\n * From the list's `aria-multiselectable`, which is what a screen reader acts on. The\n * list does not exist while an overlay panel is closed, so the host modifier answers\n * then — it is only painted in the combobox shape, which is exactly the shape that\n * can be asked while closed.\n */\n async isMultiple(): Promise<boolean> {\n if (await this.isOpen()) {\n return (await (await this.list()).getAttribute('aria-multiselectable')) === 'true';\n }\n return (await this.host()).hasClass('wr-tree--multi');\n }\n\n /**\n * How much the tree lets you select.\n *\n * `'none'` is not \"nothing selected\": it is a tree whose rows announce no\n * `aria-selected` at all, and telling that from `'single'` needs a row — so an empty\n * tree throws rather than guessing.\n */\n async getSelectionMode(): Promise<'none' | 'single' | 'multi'> {\n if (await this.isMultiple()) return 'multi';\n\n const [first] = await this.getNodes();\n if (!first) {\n throw new Error(\n 'WrTreeHarness.getSelectionMode(): this tree renders no nodes, and single vs none is only visible ' +\n 'on a row — `selectionMode=\"none\"` is the absence of `aria-selected`, so there is nothing to read.'\n );\n }\n return (await first.isSelectable()) ? 'single' : 'none';\n }\n\n /**\n * Whether the list is WINDOWED right now.\n *\n * Read from the list's modifier rather than from the input, and that is the point:\n * `virtualScroll` only takes effect when there is something to window, so an empty\n * tree renders the plain shape however the input is set.\n */\n async isVirtual(): Promise<boolean> {\n return (await this.list()).hasClass('wr-tree__list--virtual');\n }\n\n /**\n * The role the list announces — `tree`.\n *\n * Worth asking: rendered without it, the rows are a bag of names, identical on\n * screen and structureless to anyone not looking at it.\n */\n async getRole(): Promise<string | null> {\n return (await this.list()).getAttribute('role');\n }\n\n /**\n * What the trigger shows as the selection: the chip labels joined by `', '` in multi\n * mode, the label in single mode, and `''` when nothing is selected (the placeholder\n * is showing). The `+N more` chip a `maxTagCount` produces is NOT part of it — it is\n * a count rather than a selection, and {@link getOverflowText} reads it. Inline trees\n * have no trigger and throw.\n */\n async getValueText(): Promise<string> {\n await this.trigger();\n\n const chips = await this.getChipLabels();\n if (chips.length > 0) return chips.join(', ');\n\n const value = await this.locatorForOptional('.wr-tree__value')();\n return value ? value.text() : '';\n }\n\n /** The placeholder, or `null` when a selection is hiding it. */\n async getPlaceholder(): Promise<string | null> {\n await this.trigger();\n\n const placeholder = await this.locatorForOptional('.wr-tree__placeholder')();\n return placeholder ? placeholder.text() : null;\n }\n\n /**\n * The chip labels on the trigger, in order (multi mode).\n *\n * The `+N more` overflow chip is not one of them — it is a count, not a selection.\n * {@link getOverflowText} reads it.\n */\n async getChipLabels(): Promise<string[]> {\n await this.trigger();\n\n const labels = await this.locatorForAll('.wr-tree__chip-label')();\n return Promise.all(labels.map(label => label.text()));\n }\n\n /**\n * The `+N more` chip's text, or `null` when every selected node has a chip of its\n * own. Only a `maxTagCount` below the number selected produces one.\n */\n async getOverflowText(): Promise<string | null> {\n await this.trigger();\n\n const more = await this.locatorForOptional('.wr-tree__chip--more')();\n return more ? more.text() : null;\n }\n\n /** Remove one chip by its label. */\n async removeChip(label: string): Promise<void> {\n const labels = await this.getChipLabels();\n const index = labels.indexOf(label);\n if (index < 0) {\n throw new Error(\n `WrTreeHarness.removeChip(): no chip labelled \"${label}\". The trigger shows [${labels.join(', ')}] — ` +\n 'a selection past `maxTagCount` has no chip to remove.'\n );\n }\n\n // Label and remove control are emitted once per chip by the same loop, so the two\n // lists line up by index.\n const removes = await this.locatorForAll('.wr-tree__chip-remove')();\n await removes[index].click();\n }\n\n /** Click the clear (×) control on the trigger. */\n async clear(): Promise<void> {\n await this.trigger();\n\n const clear = await this.locatorForOptional('.wr-tree__clear')();\n if (!clear) {\n throw new Error(\n 'WrTreeHarness.clear(): this trigger has no clear control — `clearable` is off, nothing is ' +\n 'selected, or the tree is disabled.'\n );\n }\n await clear.click();\n }\n\n /**\n * Backspace on the closed overlay trigger — the keyboard route to the ×.\n *\n * In single mode it clears (only while `clearable` is on); in multi mode it\n * drops the LAST chip, one per call, whatever `clearable` says. Both ×\n * controls are `tabindex=\"-1\"` spans inside the trigger, so a key can never\n * reach them directly. In single mode this is the ONLY keyboard route back to\n * no selection; in multi mode a keyboard user can also toggle a row off in the\n * panel ({@link selectNode} with `additive`). {@link clear} / {@link removeChip}\n * prove the pointer path and nothing more.\n *\n * The key is honoured only on the CLOSED trigger, so an open panel is closed\n * first with Escape — the step a keyboard user takes too, since focus sits in\n * the panel while it is up.\n *\n * Does nothing, rather than throwing, where the component does nothing —\n * nothing selected, disabled or read-only — so a spec asserts the outcome on\n * the value.\n */\n async clearByKeyboard(): Promise<void> {\n if (await this.isOpen()) await this.close();\n await (await this.trigger()).sendKeys(TestKey.BACKSPACE);\n }\n\n /**\n * The nodes currently rendered, in the order they are shown.\n *\n * A collapsed branch's children are not rendered at all, so this is the VISIBLE\n * tree — expand first to reach deeper.\n *\n * While {@link isVirtual} is true it is narrower still: the list holds only the\n * window around the scroll offset — padded by a spacer at whichever end has rows\n * outside it, so a window resting at the top has one and a window in the middle has\n * two — and this returns the WINDOW, not the dataset. Nothing in the DOM carries a\n * total —\n * `aria-setsize` counts a SIBLING GROUP, not the flat list — so the harness cannot\n * offer one; {@link WrTreeNodeHarness.getIndex} places each row in the full visible\n * list, and the `[nodes]` data is where the total actually lives.\n *\n * Throws while an overlay panel is closed: the rows do not exist anywhere until it\n * opens, and an empty array reads like a tree that rendered nothing.\n */\n async getNodes(filters: WrTreeNodeHarnessFilters = {}): Promise<WrTreeNodeHarness[]> {\n return this.getAllHarnesses(WrTreeNodeHarness.with(filters));\n }\n\n /** The labels of the nodes currently rendered, in order. */\n async getNodeLabels(): Promise<string[]> {\n const nodes = await this.getNodes();\n return Promise.all(nodes.map(node => node.getLabel()));\n }\n\n /** The first node matching the filters, or a failure that says what is showing. */\n async getNode(filters: WrTreeNodeHarnessFilters): Promise<WrTreeNodeHarness> {\n const [node] = await this.getNodes(filters);\n if (!node) {\n const showing = await this.getNodeLabels();\n throw new Error(\n `WrTreeHarness.getNode(): no node matched ${JSON.stringify(filters)}. The tree is showing ` +\n `[${showing.join(', ')}] — a node inside a collapsed branch is not rendered at all, and a ` +\n 'windowed tree only renders its window.'\n );\n }\n return node;\n }\n\n /**\n * Open the panel if needed, then select the first node matching the filters.\n *\n * `additive` ctrl-clicks instead, which is how a multi-mode selection is built up: a\n * plain click there REPLACES the selection, by the component's own choice.\n *\n * Throws rather than clicking into the void on a disabled node or a\n * `selectionMode=\"none\"` tree — both ignore the click, and the silence turns into a\n * confusing failure at the next assertion. Reach for `getNode().click()` when\n * proving exactly that.\n */\n async selectNode(filters: WrTreeNodeHarnessFilters, options: { additive?: boolean } = {}): Promise<void> {\n await this.open();\n\n const node = await this.getNode(filters);\n const label = await node.getLabel();\n\n if (!(await node.isSelectable())) {\n throw new Error(\n `WrTreeHarness.selectNode(): \"${label}\" announces no selected state, so this tree is in ` +\n 'selectionMode=\"none\" — clicking it cannot select anything.'\n );\n }\n if (await node.isDisabled()) {\n throw new Error(\n `WrTreeHarness.selectNode(): \"${label}\" is disabled and the tree ignores the click. A disabled ` +\n 'TREE disables every node — check isDisabled() on the tree too.'\n );\n }\n\n await (options.additive === true ? node.ctrlClick() : node.click());\n }\n\n /**\n * The labels of the selected nodes, in row order.\n *\n * Only among the rows that are RENDERED: a selected node inside a collapsed branch,\n * or outside a window, is not here. The `[(selected)]` model is the whole set.\n */\n async getSelectedLabels(): Promise<string[]> {\n const nodes = await this.getNodes({ selected: true });\n return Promise.all(nodes.map(node => node.getLabel()));\n }\n\n /**\n * Open every branch, level by level — each pass reveals the next one down.\n *\n * Refuses on a windowed tree instead of half-doing it: only the window is rendered,\n * so the branches outside it cannot be reached from the DOM at all. Write the\n * `[(expanded)]` model there.\n */\n async expandAll(): Promise<void> {\n if (await this.isVirtual()) {\n throw new Error(\n 'WrTreeHarness.expandAll(): a windowed tree renders only its window, so this cannot reach the ' +\n 'branches outside it. Set the `[(expanded)]` model instead.'\n );\n }\n\n for (let pass = 0; pass < MAX_EXPAND_PASSES; pass++) {\n const collapsed = await this.getNodes({ expanded: false });\n if (collapsed.length === 0) return;\n for (const node of collapsed) await node.expand();\n }\n\n throw new Error(\n `WrTreeHarness.expandAll(): still finding closed branches after ${MAX_EXPAND_PASSES} levels — is the ` +\n 'node data recursive?'\n );\n }\n\n /**\n * The label of the node the keyboard cursor is on, or `null`.\n *\n * Read the way the shape publishes it: a windowed tree keeps focus on the list and\n * names the row with `aria-activedescendant` (which goes `null` while the cursor has\n * been scrolled out of the window, so the reference never dangles at a row that was\n * removed), while a plain one hands the single tab stop to the cursor row.\n */\n async getActiveNodeLabel(): Promise<string | null> {\n const nodes = await this.getNodes();\n\n if (await this.isVirtual()) {\n const active = await (await this.list()).getAttribute('aria-activedescendant');\n if (!active) return null;\n for (const node of nodes) {\n if ((await node.getId()) === active) return node.getLabel();\n }\n return null;\n }\n\n for (const node of nodes) {\n if (await node.isActive()) return node.getLabel();\n }\n return null;\n }\n\n /** Move the cursor down one row — ArrowDown. Stops at the last row. */\n async focusNext(): Promise<void> {\n await this.press(TestKey.DOWN_ARROW);\n }\n\n /** Move the cursor up one row — ArrowUp. Stops at the first row. */\n async focusPrevious(): Promise<void> {\n await this.press(TestKey.UP_ARROW);\n }\n\n /** Move the cursor to the first row — Home. */\n async focusFirst(): Promise<void> {\n await this.press(TestKey.HOME);\n }\n\n /**\n * Move the cursor to the last row — End.\n *\n * On a windowed tree this also moves the WINDOW: the tree writes its scroll offset\n * synchronously so the row it is about to point at exists in the same change\n * detection pass. It is the honest way to reach the far end of a window in a unit\n * test — jsdom keeps a `scrollTop` write but never fires the `scroll` event the tree\n * listens for, so a scroll gesture would move the window only for a test that also\n * dispatched the notification itself.\n */\n async focusLast(): Promise<void> {\n await this.press(TestKey.END);\n }\n\n /**\n * ArrowRight on the cursor row — the APG's one key with two jobs: it OPENS a closed\n * branch, and on an already-open one it steps into the first child. A leaf ignores\n * it.\n *\n * These two send the PHYSICAL key, because that is what a keyboard has. Under\n * `dir=\"rtl\"` the tree follows the indent and swaps the pair, so a spec exercising\n * an RTL tree opens with {@link collapseActive} and closes with this one. The\n * names describe the LTR job, which is the one worth naming.\n */\n async expandActive(): Promise<void> {\n await this.press(TestKey.RIGHT_ARROW);\n }\n\n /**\n * ArrowLeft on the cursor row — CLOSES an open branch, and on anything else steps\n * out to the parent row. A root-level leaf ignores it.\n *\n * Physical, and mirrored by the component under `dir=\"rtl\"` — see\n * {@link expandActive}.\n */\n async collapseActive(): Promise<void> {\n await this.press(TestKey.LEFT_ARROW);\n }\n\n /**\n * Select the cursor row from the keyboard — Enter.\n *\n * `additive` holds Ctrl, the keyboard twin of {@link WrTreeNodeHarness.ctrlClick}:\n * in multi mode a plain Enter replaces the selection, Ctrl-Enter adds to it. A\n * disabled row is skipped by the component, silently — assert the model.\n */\n async selectActive(options: { additive?: boolean } = {}): Promise<void> {\n const list = await this.list();\n if (options.additive === true) {\n await list.sendKeys({ control: true }, TestKey.ENTER);\n return;\n }\n await list.sendKeys(TestKey.ENTER);\n }\n\n /**\n * The loader every node query runs through — the host inline, THIS tree's overlay\n * panel otherwise.\n *\n * Scoped by the published panel id rather than by `.wr-tree-panel`: the overlay\n * container is shared, so a bare class query would answer with whichever tree\n * happened to open first.\n */\n protected override async getRootHarnessLoader(): Promise<HarnessLoader> {\n if (!(await this.isOverlay())) return super.getRootHarnessLoader();\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.panelId()}`);\n }\n\n /**\n * Send a key to the `role=\"tree\"` list.\n *\n * The list owns the keydown handler in both patterns — the roving-tabindex one\n * focuses a row, whose event bubbles to exactly here — so this is the honest target\n * either way, and the one that works when jsdom has left real focus on `<body>`.\n */\n private async press(key: TestKey): Promise<void> {\n await (await this.list()).sendKeys(key);\n }\n\n /**\n * The `<ul role=\"tree\">` of THIS tree — in the host inline, in this tree's own\n * overlay panel otherwise.\n */\n private async list(): Promise<TestElement> {\n if (!(await this.isOverlay())) return this.locatorFor('.wr-tree__list')();\n return this.documentRootLocatorFactory().locatorFor(`#${await this.panelId()} .wr-tree__list`)();\n }\n\n /** The combobox trigger, or a failure that says this tree renders inline. */\n private async trigger(): Promise<TestElement> {\n const trigger = await this.locatorForOptional('.wr-tree__trigger')();\n if (!trigger) {\n throw new Error(\n 'WrTreeHarness: this tree renders inline, so it has no trigger, no chips and no panel — its rows ' +\n 'are part of the host. `openOn=\"overlay\"` is the combobox shape.'\n );\n }\n return trigger;\n }\n\n /** The id the trigger publishes as `aria-controls`, which its panel carries. */\n private async panelId(): Promise<string> {\n const id = await (await this.trigger()).getAttribute('aria-controls');\n if (!id) {\n throw new Error(\n 'WrTreeHarness: the trigger publishes no `aria-controls` id, so its panel cannot be told apart ' +\n \"from another tree's.\"\n );\n }\n if (!(await this.isOpen())) {\n throw new Error('WrTreeHarness: the panel is closed — call open() first. A closed tree renders no rows.');\n }\n return id;\n }\n\n /** The trigger's text for the `text` filter, or `null` for an inline tree (which never matches). */\n private async triggerTextIfAny(): Promise<string | null> {\n const trigger = await this.locatorForOptional('.wr-tree__trigger')();\n return trigger ? this.getValueText() : null;\n }\n\n /** The rendered labels for the `nodeLabel` filter — a closed tree matches nothing rather than failing. */\n private async nodeLabelsIfShowing(): Promise<string[]> {\n if (!(await this.isOpen())) return [];\n return this.getNodeLabels();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|