@malva-ui/core 0.1.12
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/AGENTS.md +92 -0
- package/README.md +61 -0
- package/accordion/index.d.ts +3 -0
- package/accordion/lib/accordion/accordion.d.ts +47 -0
- package/accordion/lib/accordion-item/accordion-header.d.ts +22 -0
- package/accordion/lib/accordion-item/accordion-item.d.ts +49 -0
- package/accordion/malva-ui-core-accordion.d.ts +5 -0
- package/action-bar/index.d.ts +4 -0
- package/action-bar/lib/action-bar/action-bar.d.ts +59 -0
- package/action-bar/lib/action-bar/components/action-bar-actions.d.ts +9 -0
- package/action-bar/lib/action-bar/components/action-bar-logo.d.ts +5 -0
- package/action-bar/lib/action-bar/components/action-bar-spacer.d.ts +5 -0
- package/action-bar/malva-ui-core-action-bar.d.ts +5 -0
- package/alert/index.d.ts +2 -0
- package/alert/lib/alert/alert.d.ts +84 -0
- package/alert/lib/alert/alert.directives.d.ts +44 -0
- package/alert/malva-ui-core-alert.d.ts +5 -0
- package/autocomplete/index.d.ts +1 -0
- package/autocomplete/lib/autocomplete/autocomplete.d.ts +296 -0
- package/autocomplete/malva-ui-core-autocomplete.d.ts +5 -0
- package/avatar/index.d.ts +2 -0
- package/avatar/lib/avatar/avatar.d.ts +125 -0
- package/avatar/lib/color-from-text.pipe.d.ts +21 -0
- package/avatar/malva-ui-core-avatar.d.ts +5 -0
- package/avatar-group/index.d.ts +1 -0
- package/avatar-group/lib/avatar-group/avatar-group.d.ts +115 -0
- package/avatar-group/malva-ui-core-avatar-group.d.ts +5 -0
- package/badge/index.d.ts +2 -0
- package/badge/lib/badge/badge.d.ts +59 -0
- package/badge/lib/badge-icon.d.ts +35 -0
- package/badge/malva-ui-core-badge.d.ts +5 -0
- package/bottom-nav/index.d.ts +1 -0
- package/bottom-nav/lib/bottom-nav/bottom-nav.d.ts +99 -0
- package/bottom-nav/malva-ui-core-bottom-nav.d.ts +5 -0
- package/breadcrumb/index.d.ts +5 -0
- package/breadcrumb/lib/breadcrumb/breadcrumb-item-host.d.ts +32 -0
- package/breadcrumb/lib/breadcrumb/breadcrumb-item.d.ts +41 -0
- package/breadcrumb/lib/breadcrumb/breadcrumb-separator.d.ts +31 -0
- package/breadcrumb/lib/breadcrumb/breadcrumb.d.ts +151 -0
- package/breadcrumb/lib/breadcrumb/breadcrumb.types.d.ts +26 -0
- package/breadcrumb/malva-ui-core-breadcrumb.d.ts +5 -0
- package/button/index.d.ts +7 -0
- package/button/lib/button/button.d.ts +61 -0
- package/button/lib/button-close/button-close.d.ts +24 -0
- package/button/lib/button-group/button-group.d.ts +21 -0
- package/button/lib/button-split/button-split.d.ts +21 -0
- package/button/lib/button-toggle/button-toggle.d.ts +33 -0
- package/button/lib/button-variant.token.d.ts +13 -0
- package/button/lib/button.directives.d.ts +14 -0
- package/button/lib/button.types.d.ts +8 -0
- package/button/malva-ui-core-button.d.ts +5 -0
- package/calendar/index.d.ts +3 -0
- package/calendar/lib/calendar/calendar.d.ts +182 -0
- package/calendar/lib/date-provider/date-adapter.d.ts +92 -0
- package/calendar/lib/date-provider/native-date-adapter.d.ts +60 -0
- package/calendar/malva-ui-core-calendar.d.ts +5 -0
- package/card/index.d.ts +5 -0
- package/card/lib/card/card.d.ts +43 -0
- package/card/lib/card-actions-def.d.ts +10 -0
- package/card/lib/card-footer-def.d.ts +13 -0
- package/card/lib/card-header-def.d.ts +10 -0
- package/card/lib/card-subheader-def.d.ts +16 -0
- package/card/malva-ui-core-card.d.ts +5 -0
- package/chat/index.d.ts +9 -0
- package/chat/lib/chat/chat.d.ts +133 -0
- package/chat/lib/chat-audio/chat-audio.d.ts +51 -0
- package/chat/lib/chat-audio/chat-audio.service.d.ts +21 -0
- package/chat/lib/chat-date/chat-date.d.ts +16 -0
- package/chat/lib/chat-format.d.ts +5 -0
- package/chat/lib/chat-media-grid/chat-media-grid.d.ts +32 -0
- package/chat/lib/chat-message/chat-message.d.ts +45 -0
- package/chat/lib/chat-render-list.d.ts +16 -0
- package/chat/lib/chat-tokens.d.ts +25 -0
- package/chat/lib/chat-typing/chat-typing.d.ts +16 -0
- package/chat/lib/chat.types.d.ts +80 -0
- package/chat/lib/defs/chat-author-def.d.ts +30 -0
- package/chat/lib/defs/chat-date-def.d.ts +22 -0
- package/chat/lib/defs/chat-message-def.d.ts +23 -0
- package/chat/malva-ui-core-chat.d.ts +5 -0
- package/checkbox/index.d.ts +3 -0
- package/checkbox/lib/checkbox/checkbox.d.ts +129 -0
- package/checkbox/lib/checkbox-group/checkbox-group.d.ts +24 -0
- package/checkbox/lib/checkbox-group-token.d.ts +6 -0
- package/checkbox/malva-ui-core-checkbox.d.ts +5 -0
- package/chip/index.d.ts +2 -0
- package/chip/lib/chip/chip.d.ts +107 -0
- package/chip/lib/chip/chip.directives.d.ts +18 -0
- package/chip/malva-ui-core-chip.d.ts +5 -0
- package/color-picker/index.d.ts +3 -0
- package/color-picker/lib/color-picker/color-picker.d.ts +188 -0
- package/color-picker/lib/color-picker-popup/color-picker-popup.d.ts +151 -0
- package/color-picker/lib/color-utils/color-utils.d.ts +102 -0
- package/color-picker/malva-ui-core-color-picker.d.ts +5 -0
- package/combobox/index.d.ts +2 -0
- package/combobox/lib/combobox/combobox.d.ts +400 -0
- package/combobox/lib/combobox-template.directives.d.ts +10 -0
- package/combobox/malva-ui-core-combobox.d.ts +5 -0
- package/copy-to-clipboard/index.d.ts +1 -0
- package/copy-to-clipboard/lib/copy-to-clipboard/copy-to-clipboard.d.ts +130 -0
- package/copy-to-clipboard/malva-ui-core-copy-to-clipboard.d.ts +5 -0
- package/data-table/index.d.ts +8 -0
- package/data-table/lib/data-source.d.ts +5 -0
- package/data-table/lib/data-table/data-table-layout.d.ts +31 -0
- package/data-table/lib/data-table/data-table.d.ts +699 -0
- package/data-table/lib/data-table/services/data-table-column-visibility.service.d.ts +19 -0
- package/data-table/lib/data-table/services/data-table-editing.service.d.ts +37 -0
- package/data-table/lib/data-table/services/data-table-pinning.service.d.ts +37 -0
- package/data-table/lib/data-table-cell.d.ts +44 -0
- package/data-table/lib/data-table-edit-cell.d.ts +44 -0
- package/data-table/lib/data-table-error.d.ts +37 -0
- package/data-table/lib/data-table-footer.d.ts +26 -0
- package/data-table/lib/data-table-no-data.d.ts +19 -0
- package/data-table/lib/filter-dropdown/filter-dropdown.d.ts +54 -0
- package/data-table/lib/types.d.ts +134 -0
- package/data-table/malva-ui-core-data-table.d.ts +5 -0
- package/date-range-picker/index.d.ts +1 -0
- package/date-range-picker/lib/date-range-picker/date-range-picker.d.ts +145 -0
- package/date-range-picker/malva-ui-core-date-range-picker.d.ts +5 -0
- package/day-picker/index.d.ts +1 -0
- package/day-picker/lib/day-picker/day-picker.d.ts +51 -0
- package/day-picker/malva-ui-core-day-picker.d.ts +5 -0
- package/dialog/index.d.ts +11 -0
- package/dialog/lib/confirm-dialog-options.d.ts +41 -0
- package/dialog/lib/confirm-dialog.d.ts +52 -0
- package/dialog/lib/dialog/dialog.d.ts +22 -0
- package/dialog/lib/dialog-body.d.ts +9 -0
- package/dialog/lib/dialog-close.d.ts +27 -0
- package/dialog/lib/dialog-config.d.ts +112 -0
- package/dialog/lib/dialog-container.d.ts +40 -0
- package/dialog/lib/dialog-footer.d.ts +13 -0
- package/dialog/lib/dialog-header.d.ts +74 -0
- package/dialog/lib/dialog-ref.d.ts +95 -0
- package/dialog/lib/dialog-template.d.ts +65 -0
- package/dialog/lib/dialog-text-content.d.ts +24 -0
- package/dialog/lib/dialog.service.d.ts +63 -0
- package/dialog/lib/generate-routable-dialog-route.d.ts +49 -0
- package/dialog/lib/inject-dialog-ref.d.ts +8 -0
- package/dialog/lib/routable-dialog.d.ts +30 -0
- package/dialog/malva-ui-core-dialog.d.ts +5 -0
- package/divider/index.d.ts +1 -0
- package/divider/lib/divider/divider.d.ts +64 -0
- package/divider/malva-ui-core-divider.d.ts +5 -0
- package/docs/ai/accordion.md +102 -0
- package/docs/ai/action-bar.md +198 -0
- package/docs/ai/alert.md +190 -0
- package/docs/ai/autocomplete.md +111 -0
- package/docs/ai/avatar-group.md +184 -0
- package/docs/ai/avatar.md +150 -0
- package/docs/ai/badge.md +163 -0
- package/docs/ai/bottom-nav.md +267 -0
- package/docs/ai/breadcrumb.md +174 -0
- package/docs/ai/button.md +306 -0
- package/docs/ai/calendar.md +237 -0
- package/docs/ai/card.md +196 -0
- package/docs/ai/chat.md +145 -0
- package/docs/ai/checkbox.md +193 -0
- package/docs/ai/chip.md +139 -0
- package/docs/ai/color-picker.md +270 -0
- package/docs/ai/combobox.md +325 -0
- package/docs/ai/copy-to-clipboard.md +159 -0
- package/docs/ai/core.md +216 -0
- package/docs/ai/data-table.md +917 -0
- package/docs/ai/date-range-picker.md +101 -0
- package/docs/ai/day-picker.md +135 -0
- package/docs/ai/dialog.md +852 -0
- package/docs/ai/divider.md +159 -0
- package/docs/ai/drawer.md +607 -0
- package/docs/ai/dropdown.md +343 -0
- package/docs/ai/empty-state.md +167 -0
- package/docs/ai/expand.md +234 -0
- package/docs/ai/file-upload.md +402 -0
- package/docs/ai/filter.md +43 -0
- package/docs/ai/form-utils.md +319 -0
- package/docs/ai/form.md +129 -0
- package/docs/ai/input.md +175 -0
- package/docs/ai/kbd.md +70 -0
- package/docs/ai/layout.md +192 -0
- package/docs/ai/link.md +179 -0
- package/docs/ai/list.md +249 -0
- package/docs/ai/loader.md +122 -0
- package/docs/ai/menu.md +335 -0
- package/docs/ai/notification.md +281 -0
- package/docs/ai/number-input.md +123 -0
- package/docs/ai/page.md +422 -0
- package/docs/ai/pagination.md +237 -0
- package/docs/ai/pin-input.md +173 -0
- package/docs/ai/popup.md +347 -0
- package/docs/ai/progress.md +84 -0
- package/docs/ai/radio.md +201 -0
- package/docs/ai/rating.md +76 -0
- package/docs/ai/scrollbar.md +170 -0
- package/docs/ai/search-field.md +85 -0
- package/docs/ai/segmented.md +331 -0
- package/docs/ai/select.md +512 -0
- package/docs/ai/sidebar.md +992 -0
- package/docs/ai/skeleton.md +187 -0
- package/docs/ai/slider.md +180 -0
- package/docs/ai/split-pane.md +235 -0
- package/docs/ai/status-indicator.md +95 -0
- package/docs/ai/stepper.md +385 -0
- package/docs/ai/switch.md +216 -0
- package/docs/ai/tabs.md +564 -0
- package/docs/ai/textarea.md +249 -0
- package/docs/ai/tile.md +150 -0
- package/docs/ai/time-picker.md +214 -0
- package/docs/ai/timeline.md +320 -0
- package/docs/ai/title.md +105 -0
- package/docs/ai/toast.md +484 -0
- package/docs/ai/tokenizer.md +236 -0
- package/docs/ai/toolbar.md +155 -0
- package/docs/ai/tooltip.md +154 -0
- package/docs/ai/tree.md +177 -0
- package/drawer/index.d.ts +11 -0
- package/drawer/lib/drawer/drawer.d.ts +106 -0
- package/drawer/lib/drawer-body.d.ts +8 -0
- package/drawer/lib/drawer-content.d.ts +6 -0
- package/drawer/lib/drawer-footer.d.ts +5 -0
- package/drawer/lib/drawer-header.d.ts +5 -0
- package/drawer/lib/drawer-ref.d.ts +29 -0
- package/drawer/lib/drawer-resize.d.ts +65 -0
- package/drawer/lib/drawer-section/drawer-section.d.ts +15 -0
- package/drawer/lib/drawer-sections/drawer-sections.d.ts +22 -0
- package/drawer/lib/drawer-sections.service.d.ts +42 -0
- package/drawer/lib/drawer.service.d.ts +66 -0
- package/drawer/lib/generate-routable-drawer-route.d.ts +42 -0
- package/drawer/lib/routable-drawer.d.ts +30 -0
- package/drawer/malva-ui-core-drawer.d.ts +5 -0
- package/dropdown/index.d.ts +9 -0
- package/dropdown/lib/active-descendant.d.ts +64 -0
- package/dropdown/lib/dropdown-panel/dropdown-panel.d.ts +178 -0
- package/dropdown/lib/highlight-match.pipe.d.ts +27 -0
- package/dropdown/lib/option-matcher.d.ts +87 -0
- package/dropdown/lib/options-adapter.d.ts +142 -0
- package/dropdown/lib/options-result.d.ts +6 -0
- package/dropdown/lib/reconciliation.d.ts +19 -0
- package/dropdown/lib/select-data-source.d.ts +18 -0
- package/dropdown/lib/select-option.d.ts +37 -0
- package/dropdown/malva-ui-core-dropdown.d.ts +5 -0
- package/empty-state/index.d.ts +1 -0
- package/empty-state/lib/empty-state/empty-state.d.ts +34 -0
- package/empty-state/malva-ui-core-empty-state.d.ts +5 -0
- package/esm2022/accordion/index.js +4 -0
- package/esm2022/accordion/index.js.map +1 -0
- package/esm2022/accordion/lib/accordion/accordion.js +65 -0
- package/esm2022/accordion/lib/accordion/accordion.js.map +1 -0
- package/esm2022/accordion/lib/accordion-item/accordion-header.js +30 -0
- package/esm2022/accordion/lib/accordion-item/accordion-header.js.map +1 -0
- package/esm2022/accordion/lib/accordion-item/accordion-item.js +70 -0
- package/esm2022/accordion/lib/accordion-item/accordion-item.js.map +1 -0
- package/esm2022/accordion/malva-ui-core-accordion.js +5 -0
- package/esm2022/accordion/malva-ui-core-accordion.js.map +1 -0
- package/esm2022/action-bar/index.js +5 -0
- package/esm2022/action-bar/index.js.map +1 -0
- package/esm2022/action-bar/lib/action-bar/action-bar.js +64 -0
- package/esm2022/action-bar/lib/action-bar/action-bar.js.map +1 -0
- package/esm2022/action-bar/lib/action-bar/components/action-bar-actions.js +20 -0
- package/esm2022/action-bar/lib/action-bar/components/action-bar-actions.js.map +1 -0
- package/esm2022/action-bar/lib/action-bar/components/action-bar-logo.js +13 -0
- package/esm2022/action-bar/lib/action-bar/components/action-bar-logo.js.map +1 -0
- package/esm2022/action-bar/lib/action-bar/components/action-bar-spacer.js +13 -0
- package/esm2022/action-bar/lib/action-bar/components/action-bar-spacer.js.map +1 -0
- package/esm2022/action-bar/malva-ui-core-action-bar.js +5 -0
- package/esm2022/action-bar/malva-ui-core-action-bar.js.map +1 -0
- package/esm2022/alert/index.js +3 -0
- package/esm2022/alert/index.js.map +1 -0
- package/esm2022/alert/lib/alert/alert.directives.js +53 -0
- package/esm2022/alert/lib/alert/alert.directives.js.map +1 -0
- package/esm2022/alert/lib/alert/alert.js +118 -0
- package/esm2022/alert/lib/alert/alert.js.map +1 -0
- package/esm2022/alert/malva-ui-core-alert.js +5 -0
- package/esm2022/alert/malva-ui-core-alert.js.map +1 -0
- package/esm2022/autocomplete/index.js +2 -0
- package/esm2022/autocomplete/index.js.map +1 -0
- package/esm2022/autocomplete/lib/autocomplete/autocomplete.js +765 -0
- package/esm2022/autocomplete/lib/autocomplete/autocomplete.js.map +1 -0
- package/esm2022/autocomplete/malva-ui-core-autocomplete.js +5 -0
- package/esm2022/autocomplete/malva-ui-core-autocomplete.js.map +1 -0
- package/esm2022/avatar/index.js +3 -0
- package/esm2022/avatar/index.js.map +1 -0
- package/esm2022/avatar/lib/avatar/avatar.js +192 -0
- package/esm2022/avatar/lib/avatar/avatar.js.map +1 -0
- package/esm2022/avatar/lib/color-from-text.pipe.js +39 -0
- package/esm2022/avatar/lib/color-from-text.pipe.js.map +1 -0
- package/esm2022/avatar/malva-ui-core-avatar.js +5 -0
- package/esm2022/avatar/malva-ui-core-avatar.js.map +1 -0
- package/esm2022/avatar-group/index.js +2 -0
- package/esm2022/avatar-group/index.js.map +1 -0
- package/esm2022/avatar-group/lib/avatar-group/avatar-group.js +217 -0
- package/esm2022/avatar-group/lib/avatar-group/avatar-group.js.map +1 -0
- package/esm2022/avatar-group/malva-ui-core-avatar-group.js +5 -0
- package/esm2022/avatar-group/malva-ui-core-avatar-group.js.map +1 -0
- package/esm2022/badge/index.js +3 -0
- package/esm2022/badge/index.js.map +1 -0
- package/esm2022/badge/lib/badge/badge.js +71 -0
- package/esm2022/badge/lib/badge/badge.js.map +1 -0
- package/esm2022/badge/lib/badge-icon.js +47 -0
- package/esm2022/badge/lib/badge-icon.js.map +1 -0
- package/esm2022/badge/malva-ui-core-badge.js +5 -0
- package/esm2022/badge/malva-ui-core-badge.js.map +1 -0
- package/esm2022/bottom-nav/index.js +2 -0
- package/esm2022/bottom-nav/index.js.map +1 -0
- package/esm2022/bottom-nav/lib/bottom-nav/bottom-nav.js +155 -0
- package/esm2022/bottom-nav/lib/bottom-nav/bottom-nav.js.map +1 -0
- package/esm2022/bottom-nav/malva-ui-core-bottom-nav.js +5 -0
- package/esm2022/bottom-nav/malva-ui-core-bottom-nav.js.map +1 -0
- package/esm2022/breadcrumb/index.js +6 -0
- package/esm2022/breadcrumb/index.js.map +1 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb-item-host.js +43 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb-item-host.js.map +1 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb-item.js +110 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb-item.js.map +1 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb-separator.js +38 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb-separator.js.map +1 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb.js +284 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb.js.map +1 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb.types.js +1 -0
- package/esm2022/breadcrumb/lib/breadcrumb/breadcrumb.types.js.map +1 -0
- package/esm2022/breadcrumb/malva-ui-core-breadcrumb.js +5 -0
- package/esm2022/breadcrumb/malva-ui-core-breadcrumb.js.map +1 -0
- package/esm2022/button/index.js +8 -0
- package/esm2022/button/index.js.map +1 -0
- package/esm2022/button/lib/button/button.js +137 -0
- package/esm2022/button/lib/button/button.js.map +1 -0
- package/esm2022/button/lib/button-close/button-close.js +52 -0
- package/esm2022/button/lib/button-close/button-close.js.map +1 -0
- package/esm2022/button/lib/button-group/button-group.js +44 -0
- package/esm2022/button/lib/button-group/button-group.js.map +1 -0
- package/esm2022/button/lib/button-split/button-split.js +44 -0
- package/esm2022/button/lib/button-split/button-split.js.map +1 -0
- package/esm2022/button/lib/button-toggle/button-toggle.js +94 -0
- package/esm2022/button/lib/button-toggle/button-toggle.js.map +1 -0
- package/esm2022/button/lib/button-variant.token.js +4 -0
- package/esm2022/button/lib/button-variant.token.js.map +1 -0
- package/esm2022/button/lib/button.directives.js +37 -0
- package/esm2022/button/lib/button.directives.js.map +1 -0
- package/esm2022/button/lib/button.types.js +1 -0
- package/esm2022/button/lib/button.types.js.map +1 -0
- package/esm2022/button/malva-ui-core-button.js +5 -0
- package/esm2022/button/malva-ui-core-button.js.map +1 -0
- package/esm2022/calendar/index.js +4 -0
- package/esm2022/calendar/index.js.map +1 -0
- package/esm2022/calendar/lib/calendar/calendar.js +672 -0
- package/esm2022/calendar/lib/calendar/calendar.js.map +1 -0
- package/esm2022/calendar/lib/date-provider/date-adapter.js +111 -0
- package/esm2022/calendar/lib/date-provider/date-adapter.js.map +1 -0
- package/esm2022/calendar/lib/date-provider/native-date-adapter.js +151 -0
- package/esm2022/calendar/lib/date-provider/native-date-adapter.js.map +1 -0
- package/esm2022/calendar/malva-ui-core-calendar.js +5 -0
- package/esm2022/calendar/malva-ui-core-calendar.js.map +1 -0
- package/esm2022/card/index.js +6 -0
- package/esm2022/card/index.js.map +1 -0
- package/esm2022/card/lib/card/card.js +54 -0
- package/esm2022/card/lib/card/card.js.map +1 -0
- package/esm2022/card/lib/card-actions-def.js +27 -0
- package/esm2022/card/lib/card-actions-def.js.map +1 -0
- package/esm2022/card/lib/card-footer-def.js +32 -0
- package/esm2022/card/lib/card-footer-def.js.map +1 -0
- package/esm2022/card/lib/card-header-def.js +27 -0
- package/esm2022/card/lib/card-header-def.js.map +1 -0
- package/esm2022/card/lib/card-subheader-def.js +33 -0
- package/esm2022/card/lib/card-subheader-def.js.map +1 -0
- package/esm2022/card/malva-ui-core-card.js +5 -0
- package/esm2022/card/malva-ui-core-card.js.map +1 -0
- package/esm2022/chat/index.js +10 -0
- package/esm2022/chat/index.js.map +1 -0
- package/esm2022/chat/lib/chat/chat.js +326 -0
- package/esm2022/chat/lib/chat/chat.js.map +1 -0
- package/esm2022/chat/lib/chat-audio/chat-audio.js +110 -0
- package/esm2022/chat/lib/chat-audio/chat-audio.js.map +1 -0
- package/esm2022/chat/lib/chat-audio/chat-audio.service.js +28 -0
- package/esm2022/chat/lib/chat-audio/chat-audio.service.js.map +1 -0
- package/esm2022/chat/lib/chat-date/chat-date.js +46 -0
- package/esm2022/chat/lib/chat-date/chat-date.js.map +1 -0
- package/esm2022/chat/lib/chat-format.js +11 -0
- package/esm2022/chat/lib/chat-format.js.map +1 -0
- package/esm2022/chat/lib/chat-media-grid/chat-media-grid.js +53 -0
- package/esm2022/chat/lib/chat-media-grid/chat-media-grid.js.map +1 -0
- package/esm2022/chat/lib/chat-message/chat-message.js +86 -0
- package/esm2022/chat/lib/chat-message/chat-message.js.map +1 -0
- package/esm2022/chat/lib/chat-render-list.js +55 -0
- package/esm2022/chat/lib/chat-render-list.js.map +1 -0
- package/esm2022/chat/lib/chat-tokens.js +9 -0
- package/esm2022/chat/lib/chat-tokens.js.map +1 -0
- package/esm2022/chat/lib/chat-typing/chat-typing.js +50 -0
- package/esm2022/chat/lib/chat-typing/chat-typing.js.map +1 -0
- package/esm2022/chat/lib/chat.types.js +1 -0
- package/esm2022/chat/lib/chat.types.js.map +1 -0
- package/esm2022/chat/lib/defs/chat-author-def.js +27 -0
- package/esm2022/chat/lib/defs/chat-author-def.js.map +1 -0
- package/esm2022/chat/lib/defs/chat-date-def.js +24 -0
- package/esm2022/chat/lib/defs/chat-date-def.js.map +1 -0
- package/esm2022/chat/lib/defs/chat-message-def.js +30 -0
- package/esm2022/chat/lib/defs/chat-message-def.js.map +1 -0
- package/esm2022/chat/malva-ui-core-chat.js +5 -0
- package/esm2022/chat/malva-ui-core-chat.js.map +1 -0
- package/esm2022/checkbox/index.js +4 -0
- package/esm2022/checkbox/index.js.map +1 -0
- package/esm2022/checkbox/lib/checkbox/checkbox.js +201 -0
- package/esm2022/checkbox/lib/checkbox/checkbox.js.map +1 -0
- package/esm2022/checkbox/lib/checkbox-group/checkbox-group.js +68 -0
- package/esm2022/checkbox/lib/checkbox-group/checkbox-group.js.map +1 -0
- package/esm2022/checkbox/lib/checkbox-group-token.js +3 -0
- package/esm2022/checkbox/lib/checkbox-group-token.js.map +1 -0
- package/esm2022/checkbox/malva-ui-core-checkbox.js +5 -0
- package/esm2022/checkbox/malva-ui-core-checkbox.js.map +1 -0
- package/esm2022/chip/index.js +3 -0
- package/esm2022/chip/index.js.map +1 -0
- package/esm2022/chip/lib/chip/chip.directives.js +32 -0
- package/esm2022/chip/lib/chip/chip.directives.js.map +1 -0
- package/esm2022/chip/lib/chip/chip.js +140 -0
- package/esm2022/chip/lib/chip/chip.js.map +1 -0
- package/esm2022/chip/malva-ui-core-chip.js +5 -0
- package/esm2022/chip/malva-ui-core-chip.js.map +1 -0
- package/esm2022/color-picker/index.js +4 -0
- package/esm2022/color-picker/index.js.map +1 -0
- package/esm2022/color-picker/lib/color-picker/color-picker.js +530 -0
- package/esm2022/color-picker/lib/color-picker/color-picker.js.map +1 -0
- package/esm2022/color-picker/lib/color-picker-popup/color-picker-popup.js +419 -0
- package/esm2022/color-picker/lib/color-picker-popup/color-picker-popup.js.map +1 -0
- package/esm2022/color-picker/lib/color-utils/color-utils.js +256 -0
- package/esm2022/color-picker/lib/color-utils/color-utils.js.map +1 -0
- package/esm2022/color-picker/malva-ui-core-color-picker.js +5 -0
- package/esm2022/color-picker/malva-ui-core-color-picker.js.map +1 -0
- package/esm2022/combobox/index.js +3 -0
- package/esm2022/combobox/index.js.map +1 -0
- package/esm2022/combobox/lib/combobox/combobox.js +911 -0
- package/esm2022/combobox/lib/combobox/combobox.js.map +1 -0
- package/esm2022/combobox/lib/combobox-template.directives.js +24 -0
- package/esm2022/combobox/lib/combobox-template.directives.js.map +1 -0
- package/esm2022/combobox/malva-ui-core-combobox.js +5 -0
- package/esm2022/combobox/malva-ui-core-combobox.js.map +1 -0
- package/esm2022/copy-to-clipboard/index.js +2 -0
- package/esm2022/copy-to-clipboard/index.js.map +1 -0
- package/esm2022/copy-to-clipboard/lib/copy-to-clipboard/copy-to-clipboard.js +201 -0
- package/esm2022/copy-to-clipboard/lib/copy-to-clipboard/copy-to-clipboard.js.map +1 -0
- package/esm2022/copy-to-clipboard/malva-ui-core-copy-to-clipboard.js +5 -0
- package/esm2022/copy-to-clipboard/malva-ui-core-copy-to-clipboard.js.map +1 -0
- package/esm2022/data-table/index.js +9 -0
- package/esm2022/data-table/index.js.map +1 -0
- package/esm2022/data-table/lib/data-source.js +6 -0
- package/esm2022/data-table/lib/data-source.js.map +1 -0
- package/esm2022/data-table/lib/data-table/data-table-layout.js +84 -0
- package/esm2022/data-table/lib/data-table/data-table-layout.js.map +1 -0
- package/esm2022/data-table/lib/data-table/data-table.js +1846 -0
- package/esm2022/data-table/lib/data-table/data-table.js.map +1 -0
- package/esm2022/data-table/lib/data-table/services/data-table-column-visibility.service.js +37 -0
- package/esm2022/data-table/lib/data-table/services/data-table-column-visibility.service.js.map +1 -0
- package/esm2022/data-table/lib/data-table/services/data-table-editing.service.js +63 -0
- package/esm2022/data-table/lib/data-table/services/data-table-editing.service.js.map +1 -0
- package/esm2022/data-table/lib/data-table/services/data-table-pinning.service.js +68 -0
- package/esm2022/data-table/lib/data-table/services/data-table-pinning.service.js.map +1 -0
- package/esm2022/data-table/lib/data-table-cell.js +46 -0
- package/esm2022/data-table/lib/data-table-cell.js.map +1 -0
- package/esm2022/data-table/lib/data-table-edit-cell.js +46 -0
- package/esm2022/data-table/lib/data-table-edit-cell.js.map +1 -0
- package/esm2022/data-table/lib/data-table-error.js +37 -0
- package/esm2022/data-table/lib/data-table-error.js.map +1 -0
- package/esm2022/data-table/lib/data-table-footer.js +33 -0
- package/esm2022/data-table/lib/data-table-footer.js.map +1 -0
- package/esm2022/data-table/lib/data-table-no-data.js +26 -0
- package/esm2022/data-table/lib/data-table-no-data.js.map +1 -0
- package/esm2022/data-table/lib/filter-dropdown/filter-dropdown.js +224 -0
- package/esm2022/data-table/lib/filter-dropdown/filter-dropdown.js.map +1 -0
- package/esm2022/data-table/lib/types.js +1 -0
- package/esm2022/data-table/lib/types.js.map +1 -0
- package/esm2022/data-table/malva-ui-core-data-table.js +5 -0
- package/esm2022/data-table/malva-ui-core-data-table.js.map +1 -0
- package/esm2022/date-range-picker/index.js +2 -0
- package/esm2022/date-range-picker/index.js.map +1 -0
- package/esm2022/date-range-picker/lib/date-range-picker/date-range-picker.js +269 -0
- package/esm2022/date-range-picker/lib/date-range-picker/date-range-picker.js.map +1 -0
- package/esm2022/date-range-picker/malva-ui-core-date-range-picker.js +5 -0
- package/esm2022/date-range-picker/malva-ui-core-date-range-picker.js.map +1 -0
- package/esm2022/day-picker/index.js +2 -0
- package/esm2022/day-picker/index.js.map +1 -0
- package/esm2022/day-picker/lib/day-picker/day-picker.js +124 -0
- package/esm2022/day-picker/lib/day-picker/day-picker.js.map +1 -0
- package/esm2022/day-picker/malva-ui-core-day-picker.js +5 -0
- package/esm2022/day-picker/malva-ui-core-day-picker.js.map +1 -0
- package/esm2022/dialog/index.js +12 -0
- package/esm2022/dialog/index.js.map +1 -0
- package/esm2022/dialog/lib/confirm-dialog-options.js +1 -0
- package/esm2022/dialog/lib/confirm-dialog-options.js.map +1 -0
- package/esm2022/dialog/lib/confirm-dialog.js +155 -0
- package/esm2022/dialog/lib/confirm-dialog.js.map +1 -0
- package/esm2022/dialog/lib/dialog/dialog.js +40 -0
- package/esm2022/dialog/lib/dialog/dialog.js.map +1 -0
- package/esm2022/dialog/lib/dialog-body.js +33 -0
- package/esm2022/dialog/lib/dialog-body.js.map +1 -0
- package/esm2022/dialog/lib/dialog-close.js +45 -0
- package/esm2022/dialog/lib/dialog-close.js.map +1 -0
- package/esm2022/dialog/lib/dialog-config.js +23 -0
- package/esm2022/dialog/lib/dialog-config.js.map +1 -0
- package/esm2022/dialog/lib/dialog-container.js +69 -0
- package/esm2022/dialog/lib/dialog-container.js.map +1 -0
- package/esm2022/dialog/lib/dialog-footer.js +29 -0
- package/esm2022/dialog/lib/dialog-footer.js.map +1 -0
- package/esm2022/dialog/lib/dialog-header.js +181 -0
- package/esm2022/dialog/lib/dialog-header.js.map +1 -0
- package/esm2022/dialog/lib/dialog-ref.js +167 -0
- package/esm2022/dialog/lib/dialog-ref.js.map +1 -0
- package/esm2022/dialog/lib/dialog-template.js +136 -0
- package/esm2022/dialog/lib/dialog-template.js.map +1 -0
- package/esm2022/dialog/lib/dialog-text-content.js +59 -0
- package/esm2022/dialog/lib/dialog-text-content.js.map +1 -0
- package/esm2022/dialog/lib/dialog.service.js +205 -0
- package/esm2022/dialog/lib/dialog.service.js.map +1 -0
- package/esm2022/dialog/lib/generate-routable-dialog-route.js +59 -0
- package/esm2022/dialog/lib/generate-routable-dialog-route.js.map +1 -0
- package/esm2022/dialog/lib/inject-dialog-ref.js +16 -0
- package/esm2022/dialog/lib/inject-dialog-ref.js.map +1 -0
- package/esm2022/dialog/lib/routable-dialog.js +91 -0
- package/esm2022/dialog/lib/routable-dialog.js.map +1 -0
- package/esm2022/dialog/malva-ui-core-dialog.js +5 -0
- package/esm2022/dialog/malva-ui-core-dialog.js.map +1 -0
- package/esm2022/divider/index.js +2 -0
- package/esm2022/divider/index.js.map +1 -0
- package/esm2022/divider/lib/divider/divider.js +73 -0
- package/esm2022/divider/lib/divider/divider.js.map +1 -0
- package/esm2022/divider/malva-ui-core-divider.js +5 -0
- package/esm2022/divider/malva-ui-core-divider.js.map +1 -0
- package/esm2022/drawer/index.js +12 -0
- package/esm2022/drawer/index.js.map +1 -0
- package/esm2022/drawer/lib/drawer/drawer.js +180 -0
- package/esm2022/drawer/lib/drawer/drawer.js.map +1 -0
- package/esm2022/drawer/lib/drawer-body.js +32 -0
- package/esm2022/drawer/lib/drawer-body.js.map +1 -0
- package/esm2022/drawer/lib/drawer-content.js +14 -0
- package/esm2022/drawer/lib/drawer-content.js.map +1 -0
- package/esm2022/drawer/lib/drawer-footer.js +14 -0
- package/esm2022/drawer/lib/drawer-footer.js.map +1 -0
- package/esm2022/drawer/lib/drawer-header.js +14 -0
- package/esm2022/drawer/lib/drawer-header.js.map +1 -0
- package/esm2022/drawer/lib/drawer-ref.js +31 -0
- package/esm2022/drawer/lib/drawer-ref.js.map +1 -0
- package/esm2022/drawer/lib/drawer-resize.js +243 -0
- package/esm2022/drawer/lib/drawer-resize.js.map +1 -0
- package/esm2022/drawer/lib/drawer-section/drawer-section.js +26 -0
- package/esm2022/drawer/lib/drawer-section/drawer-section.js.map +1 -0
- package/esm2022/drawer/lib/drawer-sections/drawer-sections.js +58 -0
- package/esm2022/drawer/lib/drawer-sections/drawer-sections.js.map +1 -0
- package/esm2022/drawer/lib/drawer-sections.service.js +83 -0
- package/esm2022/drawer/lib/drawer-sections.service.js.map +1 -0
- package/esm2022/drawer/lib/drawer.service.js +86 -0
- package/esm2022/drawer/lib/drawer.service.js.map +1 -0
- package/esm2022/drawer/lib/generate-routable-drawer-route.js +52 -0
- package/esm2022/drawer/lib/generate-routable-drawer-route.js.map +1 -0
- package/esm2022/drawer/lib/routable-drawer.js +91 -0
- package/esm2022/drawer/lib/routable-drawer.js.map +1 -0
- package/esm2022/drawer/malva-ui-core-drawer.js +5 -0
- package/esm2022/drawer/malva-ui-core-drawer.js.map +1 -0
- package/esm2022/dropdown/index.js +10 -0
- package/esm2022/dropdown/index.js.map +1 -0
- package/esm2022/dropdown/lib/active-descendant.js +92 -0
- package/esm2022/dropdown/lib/active-descendant.js.map +1 -0
- package/esm2022/dropdown/lib/dropdown-panel/dropdown-panel.js +295 -0
- package/esm2022/dropdown/lib/dropdown-panel/dropdown-panel.js.map +1 -0
- package/esm2022/dropdown/lib/highlight-match.pipe.js +36 -0
- package/esm2022/dropdown/lib/highlight-match.pipe.js.map +1 -0
- package/esm2022/dropdown/lib/option-matcher.js +124 -0
- package/esm2022/dropdown/lib/option-matcher.js.map +1 -0
- package/esm2022/dropdown/lib/options-adapter.js +356 -0
- package/esm2022/dropdown/lib/options-adapter.js.map +1 -0
- package/esm2022/dropdown/lib/options-result.js +11 -0
- package/esm2022/dropdown/lib/options-result.js.map +1 -0
- package/esm2022/dropdown/lib/reconciliation.js +32 -0
- package/esm2022/dropdown/lib/reconciliation.js.map +1 -0
- package/esm2022/dropdown/lib/select-data-source.js +21 -0
- package/esm2022/dropdown/lib/select-data-source.js.map +1 -0
- package/esm2022/dropdown/lib/select-option.js +25 -0
- package/esm2022/dropdown/lib/select-option.js.map +1 -0
- package/esm2022/dropdown/malva-ui-core-dropdown.js +5 -0
- package/esm2022/dropdown/malva-ui-core-dropdown.js.map +1 -0
- package/esm2022/empty-state/index.js +2 -0
- package/esm2022/empty-state/index.js.map +1 -0
- package/esm2022/empty-state/lib/empty-state/empty-state.js +43 -0
- package/esm2022/empty-state/lib/empty-state/empty-state.js.map +1 -0
- package/esm2022/empty-state/malva-ui-core-empty-state.js +5 -0
- package/esm2022/empty-state/malva-ui-core-empty-state.js.map +1 -0
- package/esm2022/expand/index.js +3 -0
- package/esm2022/expand/index.js.map +1 -0
- package/esm2022/expand/lib/expand/expand-content.js +33 -0
- package/esm2022/expand/lib/expand/expand-content.js.map +1 -0
- package/esm2022/expand/lib/expand/expand.js +93 -0
- package/esm2022/expand/lib/expand/expand.js.map +1 -0
- package/esm2022/expand/malva-ui-core-expand.js +5 -0
- package/esm2022/expand/malva-ui-core-expand.js.map +1 -0
- package/esm2022/file-upload/index.js +5 -0
- package/esm2022/file-upload/index.js.map +1 -0
- package/esm2022/file-upload/lib/file-upload/file-upload.js +324 -0
- package/esm2022/file-upload/lib/file-upload/file-upload.js.map +1 -0
- package/esm2022/file-upload/lib/file-upload/file-upload.types.js +1 -0
- package/esm2022/file-upload/lib/file-upload/file-upload.types.js.map +1 -0
- package/esm2022/file-upload/lib/file-upload-action.js +65 -0
- package/esm2022/file-upload/lib/file-upload-action.js.map +1 -0
- package/esm2022/file-upload/lib/file-upload-item/file-upload-item.js +55 -0
- package/esm2022/file-upload/lib/file-upload-item/file-upload-item.js.map +1 -0
- package/esm2022/file-upload/malva-ui-core-file-upload.js +5 -0
- package/esm2022/file-upload/malva-ui-core-file-upload.js.map +1 -0
- package/esm2022/filter/index.js +4 -0
- package/esm2022/filter/index.js.map +1 -0
- package/esm2022/filter/lib/filter/filter.js +552 -0
- package/esm2022/filter/lib/filter/filter.js.map +1 -0
- package/esm2022/filter/lib/filter.types.js +1 -0
- package/esm2022/filter/lib/filter.types.js.map +1 -0
- package/esm2022/filter/lib/smart-filter-bar/smart-filter-bar.js +518 -0
- package/esm2022/filter/lib/smart-filter-bar/smart-filter-bar.js.map +1 -0
- package/esm2022/filter/malva-ui-core-filter.js +5 -0
- package/esm2022/filter/malva-ui-core-filter.js.map +1 -0
- package/esm2022/form/index.js +7 -0
- package/esm2022/form/index.js.map +1 -0
- package/esm2022/form/lib/fieldset/fieldset.js +101 -0
- package/esm2022/form/lib/fieldset/fieldset.js.map +1 -0
- package/esm2022/form/lib/fieldset-span.js +49 -0
- package/esm2022/form/lib/fieldset-span.js.map +1 -0
- package/esm2022/form/lib/form/form.js +60 -0
- package/esm2022/form/lib/form/form.js.map +1 -0
- package/esm2022/form/lib/form-actions.js +34 -0
- package/esm2022/form/lib/form-actions.js.map +1 -0
- package/esm2022/form/lib/form-gap.js +15 -0
- package/esm2022/form/lib/form-gap.js.map +1 -0
- package/esm2022/form/lib/form-header.js +27 -0
- package/esm2022/form/lib/form-header.js.map +1 -0
- package/esm2022/form/lib/form.types.js +1 -0
- package/esm2022/form/lib/form.types.js.map +1 -0
- package/esm2022/form/malva-ui-core-form.js +5 -0
- package/esm2022/form/malva-ui-core-form.js.map +1 -0
- package/esm2022/form-utils/index.js +16 -0
- package/esm2022/form-utils/index.js.map +1 -0
- package/esm2022/form-utils/lib/aria-value-bridge.js +57 -0
- package/esm2022/form-utils/lib/aria-value-bridge.js.map +1 -0
- package/esm2022/form-utils/lib/description/description.js +24 -0
- package/esm2022/form-utils/lib/description/description.js.map +1 -0
- package/esm2022/form-utils/lib/focusable-group-base.js +87 -0
- package/esm2022/form-utils/lib/focusable-group-base.js.map +1 -0
- package/esm2022/form-utils/lib/form-control-base/signal-form-control-base.js +192 -0
- package/esm2022/form-utils/lib/form-control-base/signal-form-control-base.js.map +1 -0
- package/esm2022/form-utils/lib/form-control-sides.js +34 -0
- package/esm2022/form-utils/lib/form-control-sides.js.map +1 -0
- package/esm2022/form-utils/lib/form-control-wrapper/form-control-wrapper-control.js +14 -0
- package/esm2022/form-utils/lib/form-control-wrapper/form-control-wrapper-control.js.map +1 -0
- package/esm2022/form-utils/lib/form-control-wrapper/form-control-wrapper.js +47 -0
- package/esm2022/form-utils/lib/form-control-wrapper/form-control-wrapper.js.map +1 -0
- package/esm2022/form-utils/lib/form-field/form-field.js +245 -0
- package/esm2022/form-utils/lib/form-field/form-field.js.map +1 -0
- package/esm2022/form-utils/lib/hint/hint.js +11 -0
- package/esm2022/form-utils/lib/hint/hint.js.map +1 -0
- package/esm2022/form-utils/lib/label/label.js +55 -0
- package/esm2022/form-utils/lib/label/label.js.map +1 -0
- package/esm2022/form-utils/lib/message/message.js +20 -0
- package/esm2022/form-utils/lib/message/message.js.map +1 -0
- package/esm2022/form-utils/lib/models/error-display-strategy.js +1 -0
- package/esm2022/form-utils/lib/models/error-display-strategy.js.map +1 -0
- package/esm2022/form-utils/lib/models/form-control-connector.js +3 -0
- package/esm2022/form-utils/lib/models/form-control-connector.js.map +1 -0
- package/esm2022/form-utils/lib/models/form-state.js +1 -0
- package/esm2022/form-utils/lib/models/form-state.js.map +1 -0
- package/esm2022/form-utils/lib/selection.service.js +81 -0
- package/esm2022/form-utils/lib/selection.service.js.map +1 -0
- package/esm2022/form-utils/malva-ui-core-form-utils.js +5 -0
- package/esm2022/form-utils/malva-ui-core-form-utils.js.map +1 -0
- package/esm2022/form-utils/testing/index.js +2 -0
- package/esm2022/form-utils/testing/index.js.map +1 -0
- package/esm2022/form-utils/testing/lib/forms-binding-matrix.js +32 -0
- package/esm2022/form-utils/testing/lib/forms-binding-matrix.js.map +1 -0
- package/esm2022/form-utils/testing/malva-ui-core-form-utils-testing.js +5 -0
- package/esm2022/form-utils/testing/malva-ui-core-form-utils-testing.js.map +1 -0
- package/esm2022/index.js +70 -0
- package/esm2022/index.js.map +1 -0
- package/esm2022/input/index.js +4 -0
- package/esm2022/input/index.js.map +1 -0
- package/esm2022/input/lib/input/input-native.js +49 -0
- package/esm2022/input/lib/input/input-native.js.map +1 -0
- package/esm2022/input/lib/input/input-value-accessor.js +4 -0
- package/esm2022/input/lib/input/input-value-accessor.js.map +1 -0
- package/esm2022/input/lib/input/input.js +277 -0
- package/esm2022/input/lib/input/input.js.map +1 -0
- package/esm2022/input/lib/input/password-strength.js +46 -0
- package/esm2022/input/lib/input/password-strength.js.map +1 -0
- package/esm2022/input/malva-ui-core-input.js +5 -0
- package/esm2022/input/malva-ui-core-input.js.map +1 -0
- package/esm2022/kbd/index.js +2 -0
- package/esm2022/kbd/index.js.map +1 -0
- package/esm2022/kbd/lib/kbd/kbd.js +124 -0
- package/esm2022/kbd/lib/kbd/kbd.js.map +1 -0
- package/esm2022/kbd/malva-ui-core-kbd.js +5 -0
- package/esm2022/kbd/malva-ui-core-kbd.js.map +1 -0
- package/esm2022/layout/index.js +5 -0
- package/esm2022/layout/index.js.map +1 -0
- package/esm2022/layout/lib/layout/directives/MlvLayoutSide.js +14 -0
- package/esm2022/layout/lib/layout/directives/MlvLayoutSide.js.map +1 -0
- package/esm2022/layout/lib/layout/directives/MlvLayoutTop.js +14 -0
- package/esm2022/layout/lib/layout/directives/MlvLayoutTop.js.map +1 -0
- package/esm2022/layout/lib/layout/layout.js +43 -0
- package/esm2022/layout/lib/layout/layout.js.map +1 -0
- package/esm2022/layout/lib/layout/services/theme.service.js +121 -0
- package/esm2022/layout/lib/layout/services/theme.service.js.map +1 -0
- package/esm2022/layout/malva-ui-core-layout.js +5 -0
- package/esm2022/layout/malva-ui-core-layout.js.map +1 -0
- package/esm2022/link/index.js +3 -0
- package/esm2022/link/index.js.map +1 -0
- package/esm2022/link/lib/link/link.directives.js +24 -0
- package/esm2022/link/lib/link/link.directives.js.map +1 -0
- package/esm2022/link/lib/link/link.js +61 -0
- package/esm2022/link/lib/link/link.js.map +1 -0
- package/esm2022/link/malva-ui-core-link.js +5 -0
- package/esm2022/link/malva-ui-core-link.js.map +1 -0
- package/esm2022/list/index.js +14 -0
- package/esm2022/list/index.js.map +1 -0
- package/esm2022/list/lib/list/list.js +28 -0
- package/esm2022/list/lib/list/list.js.map +1 -0
- package/esm2022/list/lib/list-item/list-item.js +125 -0
- package/esm2022/list/lib/list-item/list-item.js.map +1 -0
- package/esm2022/list/lib/list-item-actions.js +23 -0
- package/esm2022/list/lib/list-item-actions.js.map +1 -0
- package/esm2022/list/lib/list-item-byline.js +13 -0
- package/esm2022/list/lib/list-item-byline.js.map +1 -0
- package/esm2022/list/lib/list-item-group/list-item-group.js +25 -0
- package/esm2022/list/lib/list-item-group/list-item-group.js.map +1 -0
- package/esm2022/list/lib/list-item-link/list-item-link.js +20 -0
- package/esm2022/list/lib/list-item-link/list-item-link.js.map +1 -0
- package/esm2022/list/lib/list-item-media.js +13 -0
- package/esm2022/list/lib/list-item-media.js.map +1 -0
- package/esm2022/list/lib/list-item-meta.js +13 -0
- package/esm2022/list/lib/list-item-meta.js.map +1 -0
- package/esm2022/list/lib/list-item-prefix.js +13 -0
- package/esm2022/list/lib/list-item-prefix.js.map +1 -0
- package/esm2022/list/lib/list-item-suffix.js +13 -0
- package/esm2022/list/lib/list-item-suffix.js.map +1 -0
- package/esm2022/list/lib/list-item-template.js +14 -0
- package/esm2022/list/lib/list-item-template.js.map +1 -0
- package/esm2022/list/lib/list-item-title.js +13 -0
- package/esm2022/list/lib/list-item-title.js.map +1 -0
- package/esm2022/list/lib/list-selectable.js +116 -0
- package/esm2022/list/lib/list-selectable.js.map +1 -0
- package/esm2022/list/malva-ui-core-list.js +5 -0
- package/esm2022/list/malva-ui-core-list.js.map +1 -0
- package/esm2022/loader/index.js +2 -0
- package/esm2022/loader/index.js.map +1 -0
- package/esm2022/loader/lib/loader/loader.js +134 -0
- package/esm2022/loader/lib/loader/loader.js.map +1 -0
- package/esm2022/loader/malva-ui-core-loader.js +5 -0
- package/esm2022/loader/malva-ui-core-loader.js.map +1 -0
- package/esm2022/malva-ui-core.js +5 -0
- package/esm2022/malva-ui-core.js.map +1 -0
- package/esm2022/menu/index.js +10 -0
- package/esm2022/menu/index.js.map +1 -0
- package/esm2022/menu/lib/menu/menu-group-label.js +35 -0
- package/esm2022/menu/lib/menu/menu-group-label.js.map +1 -0
- package/esm2022/menu/lib/menu/menu-group.js +44 -0
- package/esm2022/menu/lib/menu/menu-group.js.map +1 -0
- package/esm2022/menu/lib/menu/menu-item.js +117 -0
- package/esm2022/menu/lib/menu/menu-item.js.map +1 -0
- package/esm2022/menu/lib/menu/menu-separator.js +41 -0
- package/esm2022/menu/lib/menu/menu-separator.js.map +1 -0
- package/esm2022/menu/lib/menu/menu-trigger.js +653 -0
- package/esm2022/menu/lib/menu/menu-trigger.js.map +1 -0
- package/esm2022/menu/lib/menu/menu.js +308 -0
- package/esm2022/menu/lib/menu/menu.js.map +1 -0
- package/esm2022/menu/lib/menu/menu.types.js +4 -0
- package/esm2022/menu/lib/menu/menu.types.js.map +1 -0
- package/esm2022/menu/lib/menu/menubar.js +257 -0
- package/esm2022/menu/lib/menu/menubar.js.map +1 -0
- package/esm2022/menu/lib/menu/menubar.types.js +4 -0
- package/esm2022/menu/lib/menu/menubar.types.js.map +1 -0
- package/esm2022/menu/lib/menu/submenu-aim.js +30 -0
- package/esm2022/menu/lib/menu/submenu-aim.js.map +1 -0
- package/esm2022/menu/malva-ui-core-menu.js +5 -0
- package/esm2022/menu/malva-ui-core-menu.js.map +1 -0
- package/esm2022/notification/index.js +5 -0
- package/esm2022/notification/index.js.map +1 -0
- package/esm2022/notification/lib/notification-item/notification-item.js +65 -0
- package/esm2022/notification/lib/notification-item/notification-item.js.map +1 -0
- package/esm2022/notification/lib/notification-ref.js +13 -0
- package/esm2022/notification/lib/notification-ref.js.map +1 -0
- package/esm2022/notification/lib/notification.service.js +118 -0
- package/esm2022/notification/lib/notification.service.js.map +1 -0
- package/esm2022/notification/lib/notification.types.js +6 -0
- package/esm2022/notification/lib/notification.types.js.map +1 -0
- package/esm2022/notification/malva-ui-core-notification.js +5 -0
- package/esm2022/notification/malva-ui-core-notification.js.map +1 -0
- package/esm2022/number-input/index.js +2 -0
- package/esm2022/number-input/index.js.map +1 -0
- package/esm2022/number-input/lib/number-input/number-input.js +395 -0
- package/esm2022/number-input/lib/number-input/number-input.js.map +1 -0
- package/esm2022/number-input/malva-ui-core-number-input.js +5 -0
- package/esm2022/number-input/malva-ui-core-number-input.js.map +1 -0
- package/esm2022/page/index.js +15 -0
- package/esm2022/page/index.js.map +1 -0
- package/esm2022/page/lib/page/page-scroll.token.js +9 -0
- package/esm2022/page/lib/page/page-scroll.token.js.map +1 -0
- package/esm2022/page/lib/page/page-snap-controller.js +170 -0
- package/esm2022/page/lib/page/page-snap-controller.js.map +1 -0
- package/esm2022/page/lib/page/page-snap.directive.js +65 -0
- package/esm2022/page/lib/page/page-snap.directive.js.map +1 -0
- package/esm2022/page/lib/page/page.js +173 -0
- package/esm2022/page/lib/page/page.js.map +1 -0
- package/esm2022/page/lib/page-content/page-aside.js +13 -0
- package/esm2022/page/lib/page-content/page-aside.js.map +1 -0
- package/esm2022/page/lib/page-content/page-content.js +72 -0
- package/esm2022/page/lib/page-content/page-content.js.map +1 -0
- package/esm2022/page/lib/page-dock/page-dock.js +128 -0
- package/esm2022/page/lib/page-dock/page-dock.js.map +1 -0
- package/esm2022/page/lib/page-dock/page-dock.slots.js +39 -0
- package/esm2022/page/lib/page-dock/page-dock.slots.js.map +1 -0
- package/esm2022/page/lib/page-header/page-header.directives.js +88 -0
- package/esm2022/page/lib/page-header/page-header.directives.js.map +1 -0
- package/esm2022/page/lib/page-header/page-header.js +124 -0
- package/esm2022/page/lib/page-header/page-header.js.map +1 -0
- package/esm2022/page/lib/page-shell/page-shell-color.js +193 -0
- package/esm2022/page/lib/page-shell/page-shell-color.js.map +1 -0
- package/esm2022/page/lib/page-shell/page-shell-css-color.js +207 -0
- package/esm2022/page/lib/page-shell/page-shell-css-color.js.map +1 -0
- package/esm2022/page/lib/page-shell/page-shell.js +131 -0
- package/esm2022/page/lib/page-shell/page-shell.js.map +1 -0
- package/esm2022/page/lib/page-shell/page-shell.slots.js +41 -0
- package/esm2022/page/lib/page-shell/page-shell.slots.js.map +1 -0
- package/esm2022/page/lib/page-summary/page-summary-item.js +26 -0
- package/esm2022/page/lib/page-summary/page-summary-item.js.map +1 -0
- package/esm2022/page/lib/page-summary/page-summary.js +87 -0
- package/esm2022/page/lib/page-summary/page-summary.js.map +1 -0
- package/esm2022/page/malva-ui-core-page.js +5 -0
- package/esm2022/page/malva-ui-core-page.js.map +1 -0
- package/esm2022/pagination/index.js +5 -0
- package/esm2022/pagination/index.js.map +1 -0
- package/esm2022/pagination/lib/pagination/pagination.directives.js +58 -0
- package/esm2022/pagination/lib/pagination/pagination.directives.js.map +1 -0
- package/esm2022/pagination/lib/pagination/pagination.js +293 -0
- package/esm2022/pagination/lib/pagination/pagination.js.map +1 -0
- package/esm2022/pagination/lib/pagination/pagination.model.js +1 -0
- package/esm2022/pagination/lib/pagination/pagination.model.js.map +1 -0
- package/esm2022/pagination/lib/pagination/pagination.service.js +108 -0
- package/esm2022/pagination/lib/pagination/pagination.service.js.map +1 -0
- package/esm2022/pagination/malva-ui-core-pagination.js +5 -0
- package/esm2022/pagination/malva-ui-core-pagination.js.map +1 -0
- package/esm2022/pin-input/index.js +3 -0
- package/esm2022/pin-input/index.js.map +1 -0
- package/esm2022/pin-input/lib/pin-input/pin-input-separator.js +31 -0
- package/esm2022/pin-input/lib/pin-input/pin-input-separator.js.map +1 -0
- package/esm2022/pin-input/lib/pin-input/pin-input.js +365 -0
- package/esm2022/pin-input/lib/pin-input/pin-input.js.map +1 -0
- package/esm2022/pin-input/malva-ui-core-pin-input.js +5 -0
- package/esm2022/pin-input/malva-ui-core-pin-input.js.map +1 -0
- package/esm2022/popup/index.js +10 -0
- package/esm2022/popup/index.js.map +1 -0
- package/esm2022/popup/lib/popup/popup.js +500 -0
- package/esm2022/popup/lib/popup/popup.js.map +1 -0
- package/esm2022/popup/lib/popup-container/popup-container.js +169 -0
- package/esm2022/popup/lib/popup-container/popup-container.js.map +1 -0
- package/esm2022/popup/lib/popup-container/popup-container.token.js +9 -0
- package/esm2022/popup/lib/popup-container/popup-container.token.js.map +1 -0
- package/esm2022/popup/lib/popup-content.js +14 -0
- package/esm2022/popup/lib/popup-content.js.map +1 -0
- package/esm2022/popup/lib/popup-header-content.js +26 -0
- package/esm2022/popup/lib/popup-header-content.js.map +1 -0
- package/esm2022/popup/lib/popup-pinned-content.js +25 -0
- package/esm2022/popup/lib/popup-pinned-content.js.map +1 -0
- package/esm2022/popup/lib/popup-positions.js +252 -0
- package/esm2022/popup/lib/popup-positions.js.map +1 -0
- package/esm2022/popup/lib/popup-trigger/popup-trigger.js +308 -0
- package/esm2022/popup/lib/popup-trigger/popup-trigger.js.map +1 -0
- package/esm2022/popup/lib/popup.service.js +188 -0
- package/esm2022/popup/lib/popup.service.js.map +1 -0
- package/esm2022/popup/malva-ui-core-popup.js +5 -0
- package/esm2022/popup/malva-ui-core-popup.js.map +1 -0
- package/esm2022/progress/index.js +2 -0
- package/esm2022/progress/index.js.map +1 -0
- package/esm2022/progress/lib/progress/progress.js +102 -0
- package/esm2022/progress/lib/progress/progress.js.map +1 -0
- package/esm2022/progress/malva-ui-core-progress.js +5 -0
- package/esm2022/progress/malva-ui-core-progress.js.map +1 -0
- package/esm2022/radio/index.js +3 -0
- package/esm2022/radio/index.js.map +1 -0
- package/esm2022/radio/lib/radio/radio.js +134 -0
- package/esm2022/radio/lib/radio/radio.js.map +1 -0
- package/esm2022/radio/lib/radio-group/radio-group.js +176 -0
- package/esm2022/radio/lib/radio-group/radio-group.js.map +1 -0
- package/esm2022/radio/lib/radio-group-token.js +3 -0
- package/esm2022/radio/lib/radio-group-token.js.map +1 -0
- package/esm2022/radio/malva-ui-core-radio.js +5 -0
- package/esm2022/radio/malva-ui-core-radio.js.map +1 -0
- package/esm2022/rating/index.js +2 -0
- package/esm2022/rating/index.js.map +1 -0
- package/esm2022/rating/lib/rating/rating.js +208 -0
- package/esm2022/rating/lib/rating/rating.js.map +1 -0
- package/esm2022/rating/malva-ui-core-rating.js +5 -0
- package/esm2022/rating/malva-ui-core-rating.js.map +1 -0
- package/esm2022/scrollbar/index.js +2 -0
- package/esm2022/scrollbar/index.js.map +1 -0
- package/esm2022/scrollbar/lib/scrollbar/scrollbar.js +388 -0
- package/esm2022/scrollbar/lib/scrollbar/scrollbar.js.map +1 -0
- package/esm2022/scrollbar/malva-ui-core-scrollbar.js +5 -0
- package/esm2022/scrollbar/malva-ui-core-scrollbar.js.map +1 -0
- package/esm2022/search-field/index.js +3 -0
- package/esm2022/search-field/index.js.map +1 -0
- package/esm2022/search-field/lib/search-field/search-field.js +535 -0
- package/esm2022/search-field/lib/search-field/search-field.js.map +1 -0
- package/esm2022/search-field/lib/search-field/search-overlay-content.js +35 -0
- package/esm2022/search-field/lib/search-field/search-overlay-content.js.map +1 -0
- package/esm2022/search-field/malva-ui-core-search-field.js +5 -0
- package/esm2022/search-field/malva-ui-core-search-field.js.map +1 -0
- package/esm2022/segmented/index.js +5 -0
- package/esm2022/segmented/index.js.map +1 -0
- package/esm2022/segmented/lib/segmented/segmented.js +248 -0
- package/esm2022/segmented/lib/segmented/segmented.js.map +1 -0
- package/esm2022/segmented/lib/segmented-item/segmented-item.js +194 -0
- package/esm2022/segmented/lib/segmented-item/segmented-item.js.map +1 -0
- package/esm2022/segmented/lib/segmented-token.js +4 -0
- package/esm2022/segmented/lib/segmented-token.js.map +1 -0
- package/esm2022/segmented/lib/segmented.types.js +1 -0
- package/esm2022/segmented/lib/segmented.types.js.map +1 -0
- package/esm2022/segmented/malva-ui-core-segmented.js +5 -0
- package/esm2022/segmented/malva-ui-core-segmented.js.map +1 -0
- package/esm2022/select/index.js +4 -0
- package/esm2022/select/index.js.map +1 -0
- package/esm2022/select/lib/select/select.js +685 -0
- package/esm2022/select/lib/select/select.js.map +1 -0
- package/esm2022/select/lib/select-option.js +2 -0
- package/esm2022/select/lib/select-option.js.map +1 -0
- package/esm2022/select/lib/select-template.directives.js +34 -0
- package/esm2022/select/lib/select-template.directives.js.map +1 -0
- package/esm2022/select/malva-ui-core-select.js +5 -0
- package/esm2022/select/malva-ui-core-select.js.map +1 -0
- package/esm2022/sidebar/index.js +16 -0
- package/esm2022/sidebar/index.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar/sidebar.js +251 -0
- package/esm2022/sidebar/lib/sidebar/sidebar.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-content.js +37 -0
- package/esm2022/sidebar/lib/sidebar-content.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-context.js +3 -0
- package/esm2022/sidebar/lib/sidebar-context.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-footer.js +20 -0
- package/esm2022/sidebar/lib/sidebar-footer.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-group/sidebar-group.js +341 -0
- package/esm2022/sidebar/lib/sidebar-group/sidebar-group.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-header.js +20 -0
- package/esm2022/sidebar/lib/sidebar-header.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-item/sidebar-item.js +216 -0
- package/esm2022/sidebar/lib/sidebar-item/sidebar-item.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-item-host.js +39 -0
- package/esm2022/sidebar/lib/sidebar-item-host.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-item-icon.js +14 -0
- package/esm2022/sidebar/lib/sidebar-item-icon.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-mode.js +1 -0
- package/esm2022/sidebar/lib/sidebar-mode.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-rail/sidebar-rail.js +202 -0
- package/esm2022/sidebar/lib/sidebar-rail/sidebar-rail.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-trigger/sidebar-trigger.js +159 -0
- package/esm2022/sidebar/lib/sidebar-trigger/sidebar-trigger.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-workspace/sidebar-workspace.directives.js +35 -0
- package/esm2022/sidebar/lib/sidebar-workspace/sidebar-workspace.directives.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-workspace/sidebar-workspace.js +107 -0
- package/esm2022/sidebar/lib/sidebar-workspace/sidebar-workspace.js.map +1 -0
- package/esm2022/sidebar/lib/sidebar-workspace/sidebar-workspace.types.js +1 -0
- package/esm2022/sidebar/lib/sidebar-workspace/sidebar-workspace.types.js.map +1 -0
- package/esm2022/sidebar/malva-ui-core-sidebar.js +5 -0
- package/esm2022/sidebar/malva-ui-core-sidebar.js.map +1 -0
- package/esm2022/skeleton/index.js +2 -0
- package/esm2022/skeleton/index.js.map +1 -0
- package/esm2022/skeleton/lib/skeleton/skeleton.js +78 -0
- package/esm2022/skeleton/lib/skeleton/skeleton.js.map +1 -0
- package/esm2022/skeleton/malva-ui-core-skeleton.js +5 -0
- package/esm2022/skeleton/malva-ui-core-skeleton.js.map +1 -0
- package/esm2022/slider/index.js +3 -0
- package/esm2022/slider/index.js.map +1 -0
- package/esm2022/slider/lib/slider/slider-tooltip-def.js +31 -0
- package/esm2022/slider/lib/slider/slider-tooltip-def.js.map +1 -0
- package/esm2022/slider/lib/slider/slider.js +468 -0
- package/esm2022/slider/lib/slider/slider.js.map +1 -0
- package/esm2022/slider/malva-ui-core-slider.js +5 -0
- package/esm2022/slider/malva-ui-core-slider.js.map +1 -0
- package/esm2022/split-pane/index.js +4 -0
- package/esm2022/split-pane/index.js.map +1 -0
- package/esm2022/split-pane/lib/split-pane/split-pane-panel.js +49 -0
- package/esm2022/split-pane/lib/split-pane/split-pane-panel.js.map +1 -0
- package/esm2022/split-pane/lib/split-pane/split-pane.js +290 -0
- package/esm2022/split-pane/lib/split-pane/split-pane.js.map +1 -0
- package/esm2022/split-pane/lib/split-pane/split-pane.types.js +1 -0
- package/esm2022/split-pane/lib/split-pane/split-pane.types.js.map +1 -0
- package/esm2022/split-pane/malva-ui-core-split-pane.js +5 -0
- package/esm2022/split-pane/malva-ui-core-split-pane.js.map +1 -0
- package/esm2022/status-indicator/index.js +2 -0
- package/esm2022/status-indicator/index.js.map +1 -0
- package/esm2022/status-indicator/lib/status-indicator/status-indicator.js +71 -0
- package/esm2022/status-indicator/lib/status-indicator/status-indicator.js.map +1 -0
- package/esm2022/status-indicator/malva-ui-core-status-indicator.js +5 -0
- package/esm2022/status-indicator/malva-ui-core-status-indicator.js.map +1 -0
- package/esm2022/stepper/index.js +5 -0
- package/esm2022/stepper/index.js.map +1 -0
- package/esm2022/stepper/lib/stepper/step-header.js +46 -0
- package/esm2022/stepper/lib/stepper/step-header.js.map +1 -0
- package/esm2022/stepper/lib/stepper/step.js +65 -0
- package/esm2022/stepper/lib/stepper/step.js.map +1 -0
- package/esm2022/stepper/lib/stepper/stepper-token.js +4 -0
- package/esm2022/stepper/lib/stepper/stepper-token.js.map +1 -0
- package/esm2022/stepper/lib/stepper/stepper.js +216 -0
- package/esm2022/stepper/lib/stepper/stepper.js.map +1 -0
- package/esm2022/stepper/lib/stepper/stepper.types.js +1 -0
- package/esm2022/stepper/lib/stepper/stepper.types.js.map +1 -0
- package/esm2022/stepper/malva-ui-core-stepper.js +5 -0
- package/esm2022/stepper/malva-ui-core-stepper.js.map +1 -0
- package/esm2022/switch/index.js +3 -0
- package/esm2022/switch/index.js.map +1 -0
- package/esm2022/switch/lib/switch/switch.js +190 -0
- package/esm2022/switch/lib/switch/switch.js.map +1 -0
- package/esm2022/switch/lib/switch-group/switch-group.js +68 -0
- package/esm2022/switch/lib/switch-group/switch-group.js.map +1 -0
- package/esm2022/switch/lib/switch-group-token.js +3 -0
- package/esm2022/switch/lib/switch-group-token.js.map +1 -0
- package/esm2022/switch/malva-ui-core-switch.js +5 -0
- package/esm2022/switch/malva-ui-core-switch.js.map +1 -0
- package/esm2022/tabs/index.js +9 -0
- package/esm2022/tabs/index.js.map +1 -0
- package/esm2022/tabs/lib/tab/tab.js +72 -0
- package/esm2022/tabs/lib/tab/tab.js.map +1 -0
- package/esm2022/tabs/lib/tab-content/tab-content.js +19 -0
- package/esm2022/tabs/lib/tab-content/tab-content.js.map +1 -0
- package/esm2022/tabs/lib/tab-content-def.js +14 -0
- package/esm2022/tabs/lib/tab-content-def.js.map +1 -0
- package/esm2022/tabs/lib/tab-def.js +17 -0
- package/esm2022/tabs/lib/tab-def.js.map +1 -0
- package/esm2022/tabs/lib/tab-group-token.js +3 -0
- package/esm2022/tabs/lib/tab-group-token.js.map +1 -0
- package/esm2022/tabs/lib/tab-item/tab-item.js +44 -0
- package/esm2022/tabs/lib/tab-item/tab-item.js.map +1 -0
- package/esm2022/tabs/lib/tabs/tabs.js +542 -0
- package/esm2022/tabs/lib/tabs/tabs.js.map +1 -0
- package/esm2022/tabs/lib/tabs.service.js +83 -0
- package/esm2022/tabs/lib/tabs.service.js.map +1 -0
- package/esm2022/tabs/malva-ui-core-tabs.js +5 -0
- package/esm2022/tabs/malva-ui-core-tabs.js.map +1 -0
- package/esm2022/textarea/index.js +2 -0
- package/esm2022/textarea/index.js.map +1 -0
- package/esm2022/textarea/lib/textarea/textarea.js +194 -0
- package/esm2022/textarea/lib/textarea/textarea.js.map +1 -0
- package/esm2022/textarea/malva-ui-core-textarea.js +5 -0
- package/esm2022/textarea/malva-ui-core-textarea.js.map +1 -0
- package/esm2022/tile/index.js +3 -0
- package/esm2022/tile/index.js.map +1 -0
- package/esm2022/tile/lib/tile/tile.js +39 -0
- package/esm2022/tile/lib/tile/tile.js.map +1 -0
- package/esm2022/tile/lib/tile-header.js +14 -0
- package/esm2022/tile/lib/tile-header.js.map +1 -0
- package/esm2022/tile/malva-ui-core-tile.js +5 -0
- package/esm2022/tile/malva-ui-core-tile.js.map +1 -0
- package/esm2022/time-picker/index.js +2 -0
- package/esm2022/time-picker/index.js.map +1 -0
- package/esm2022/time-picker/lib/time-picker/time-picker.js +310 -0
- package/esm2022/time-picker/lib/time-picker/time-picker.js.map +1 -0
- package/esm2022/time-picker/lib/time-picker-column/time-picker-column.js +216 -0
- package/esm2022/time-picker/lib/time-picker-column/time-picker-column.js.map +1 -0
- package/esm2022/time-picker/malva-ui-core-time-picker.js +5 -0
- package/esm2022/time-picker/malva-ui-core-time-picker.js.map +1 -0
- package/esm2022/timeline/index.js +6 -0
- package/esm2022/timeline/index.js.map +1 -0
- package/esm2022/timeline/lib/timeline/timeline-item-icon.js +31 -0
- package/esm2022/timeline/lib/timeline/timeline-item-icon.js.map +1 -0
- package/esm2022/timeline/lib/timeline/timeline-item-meta.js +31 -0
- package/esm2022/timeline/lib/timeline/timeline-item-meta.js.map +1 -0
- package/esm2022/timeline/lib/timeline/timeline-item.js +69 -0
- package/esm2022/timeline/lib/timeline/timeline-item.js.map +1 -0
- package/esm2022/timeline/lib/timeline/timeline.js +36 -0
- package/esm2022/timeline/lib/timeline/timeline.js.map +1 -0
- package/esm2022/timeline/lib/timeline/timeline.types.js +1 -0
- package/esm2022/timeline/lib/timeline/timeline.types.js.map +1 -0
- package/esm2022/timeline/malva-ui-core-timeline.js +5 -0
- package/esm2022/timeline/malva-ui-core-timeline.js.map +1 -0
- package/esm2022/title/index.js +2 -0
- package/esm2022/title/index.js.map +1 -0
- package/esm2022/title/lib/title/title.js +171 -0
- package/esm2022/title/lib/title/title.js.map +1 -0
- package/esm2022/title/malva-ui-core-title.js +5 -0
- package/esm2022/title/malva-ui-core-title.js.map +1 -0
- package/esm2022/toast/index.js +13 -0
- package/esm2022/toast/index.js.map +1 -0
- package/esm2022/toast/lib/abstract-toast-container.js +50 -0
- package/esm2022/toast/lib/abstract-toast-container.js.map +1 -0
- package/esm2022/toast/lib/abstract-toast-item.js +75 -0
- package/esm2022/toast/lib/abstract-toast-item.js.map +1 -0
- package/esm2022/toast/lib/abstract-toast.service.js +176 -0
- package/esm2022/toast/lib/abstract-toast.service.js.map +1 -0
- package/esm2022/toast/lib/toast-container/toast-container.js +61 -0
- package/esm2022/toast/lib/toast-container/toast-container.js.map +1 -0
- package/esm2022/toast/lib/toast-item/toast-item.js +62 -0
- package/esm2022/toast/lib/toast-item/toast-item.js.map +1 -0
- package/esm2022/toast/lib/toast-ref.js +54 -0
- package/esm2022/toast/lib/toast-ref.js.map +1 -0
- package/esm2022/toast/lib/toast-timer.js +37 -0
- package/esm2022/toast/lib/toast-timer.js.map +1 -0
- package/esm2022/toast/lib/toast.directives.js +76 -0
- package/esm2022/toast/lib/toast.directives.js.map +1 -0
- package/esm2022/toast/lib/toast.service.js +127 -0
- package/esm2022/toast/lib/toast.service.js.map +1 -0
- package/esm2022/toast/lib/toast.types.js +39 -0
- package/esm2022/toast/lib/toast.types.js.map +1 -0
- package/esm2022/toast/malva-ui-core-toast.js +5 -0
- package/esm2022/toast/malva-ui-core-toast.js.map +1 -0
- package/esm2022/tokenizer/index.js +4 -0
- package/esm2022/tokenizer/index.js.map +1 -0
- package/esm2022/tokenizer/lib/token/token.js +123 -0
- package/esm2022/tokenizer/lib/token/token.js.map +1 -0
- package/esm2022/tokenizer/lib/token-template.js +23 -0
- package/esm2022/tokenizer/lib/token-template.js.map +1 -0
- package/esm2022/tokenizer/lib/tokenizer/tokenizer.js +324 -0
- package/esm2022/tokenizer/lib/tokenizer/tokenizer.js.map +1 -0
- package/esm2022/tokenizer/malva-ui-core-tokenizer.js +5 -0
- package/esm2022/tokenizer/malva-ui-core-tokenizer.js.map +1 -0
- package/esm2022/toolbar/index.js +4 -0
- package/esm2022/toolbar/index.js.map +1 -0
- package/esm2022/toolbar/lib/toolbar/toolbar-spacer.js +13 -0
- package/esm2022/toolbar/lib/toolbar/toolbar-spacer.js.map +1 -0
- package/esm2022/toolbar/lib/toolbar/toolbar-widget.js +106 -0
- package/esm2022/toolbar/lib/toolbar/toolbar-widget.js.map +1 -0
- package/esm2022/toolbar/lib/toolbar/toolbar.js +19 -0
- package/esm2022/toolbar/lib/toolbar/toolbar.js.map +1 -0
- package/esm2022/toolbar/malva-ui-core-toolbar.js +5 -0
- package/esm2022/toolbar/malva-ui-core-toolbar.js.map +1 -0
- package/esm2022/tooltip/index.js +4 -0
- package/esm2022/tooltip/index.js.map +1 -0
- package/esm2022/tooltip/lib/tooltip/tooltip-panel.js +69 -0
- package/esm2022/tooltip/lib/tooltip/tooltip-panel.js.map +1 -0
- package/esm2022/tooltip/lib/tooltip/tooltip.js +352 -0
- package/esm2022/tooltip/lib/tooltip/tooltip.js.map +1 -0
- package/esm2022/tooltip/lib/tooltip/tooltip.types.js +1 -0
- package/esm2022/tooltip/lib/tooltip/tooltip.types.js.map +1 -0
- package/esm2022/tooltip/malva-ui-core-tooltip.js +5 -0
- package/esm2022/tooltip/malva-ui-core-tooltip.js.map +1 -0
- package/esm2022/tree/index.js +5 -0
- package/esm2022/tree/index.js.map +1 -0
- package/esm2022/tree/lib/tree/tree-context.js +7 -0
- package/esm2022/tree/lib/tree/tree-context.js.map +1 -0
- package/esm2022/tree/lib/tree/tree-node-def.js +34 -0
- package/esm2022/tree/lib/tree/tree-node-def.js.map +1 -0
- package/esm2022/tree/lib/tree/tree-node.js +1 -0
- package/esm2022/tree/lib/tree/tree-node.js.map +1 -0
- package/esm2022/tree/lib/tree/tree-subtree.js +107 -0
- package/esm2022/tree/lib/tree/tree-subtree.js.map +1 -0
- package/esm2022/tree/lib/tree/tree.js +362 -0
- package/esm2022/tree/lib/tree/tree.js.map +1 -0
- package/esm2022/tree/malva-ui-core-tree.js +5 -0
- package/esm2022/tree/malva-ui-core-tree.js.map +1 -0
- package/expand/index.d.ts +2 -0
- package/expand/lib/expand/expand-content.d.ts +26 -0
- package/expand/lib/expand/expand.d.ts +74 -0
- package/expand/malva-ui-core-expand.d.ts +5 -0
- package/file-upload/README.md +7 -0
- package/file-upload/index.d.ts +4 -0
- package/file-upload/lib/file-upload/file-upload.d.ts +138 -0
- package/file-upload/lib/file-upload/file-upload.types.d.ts +42 -0
- package/file-upload/lib/file-upload-action.d.ts +53 -0
- package/file-upload/lib/file-upload-item/file-upload-item.d.ts +30 -0
- package/file-upload/malva-ui-core-file-upload.d.ts +5 -0
- package/filter/README.md +22 -0
- package/filter/index.d.ts +3 -0
- package/filter/lib/filter/filter.d.ts +156 -0
- package/filter/lib/filter.types.d.ts +75 -0
- package/filter/lib/smart-filter-bar/smart-filter-bar.d.ts +153 -0
- package/filter/malva-ui-core-filter.d.ts +5 -0
- package/form/index.d.ts +6 -0
- package/form/lib/fieldset/fieldset.d.ts +58 -0
- package/form/lib/fieldset-span.d.ts +28 -0
- package/form/lib/form/form.d.ts +37 -0
- package/form/lib/form-actions.d.ts +20 -0
- package/form/lib/form-gap.d.ts +5 -0
- package/form/lib/form-header.d.ts +18 -0
- package/form/lib/form.types.d.ts +13 -0
- package/form/malva-ui-core-form.d.ts +5 -0
- package/form-utils/index.d.ts +15 -0
- package/form-utils/lib/aria-value-bridge.d.ts +45 -0
- package/form-utils/lib/description/description.d.ts +16 -0
- package/form-utils/lib/focusable-group-base.d.ts +76 -0
- package/form-utils/lib/form-control-base/signal-form-control-base.d.ts +159 -0
- package/form-utils/lib/form-control-sides.d.ts +20 -0
- package/form-utils/lib/form-control-wrapper/form-control-wrapper-control.d.ts +6 -0
- package/form-utils/lib/form-control-wrapper/form-control-wrapper.d.ts +20 -0
- package/form-utils/lib/form-field/form-field.d.ts +68 -0
- package/form-utils/lib/hint/hint.d.ts +5 -0
- package/form-utils/lib/label/label.d.ts +22 -0
- package/form-utils/lib/message/message.d.ts +7 -0
- package/form-utils/lib/models/error-display-strategy.d.ts +1 -0
- package/form-utils/lib/models/form-control-connector.d.ts +41 -0
- package/form-utils/lib/models/form-state.d.ts +1 -0
- package/form-utils/lib/selection.service.d.ts +27 -0
- package/form-utils/malva-ui-core-form-utils.d.ts +5 -0
- package/form-utils/testing/index.d.ts +1 -0
- package/form-utils/testing/lib/forms-binding-matrix.d.ts +47 -0
- package/form-utils/testing/malva-ui-core-form-utils-testing.d.ts +5 -0
- package/index.d.ts +69 -0
- package/input/index.d.ts +3 -0
- package/input/lib/input/input-native.d.ts +36 -0
- package/input/lib/input/input-value-accessor.d.ts +11 -0
- package/input/lib/input/input.d.ts +189 -0
- package/input/lib/input/password-strength.d.ts +34 -0
- package/input/malva-ui-core-input.d.ts +5 -0
- package/kbd/index.d.ts +1 -0
- package/kbd/lib/kbd/kbd.d.ts +52 -0
- package/kbd/malva-ui-core-kbd.d.ts +5 -0
- package/layout/index.d.ts +4 -0
- package/layout/lib/layout/directives/MlvLayoutSide.d.ts +6 -0
- package/layout/lib/layout/directives/MlvLayoutTop.d.ts +6 -0
- package/layout/lib/layout/layout.d.ts +20 -0
- package/layout/lib/layout/services/theme.service.d.ts +33 -0
- package/layout/malva-ui-core-layout.d.ts +5 -0
- package/link/index.d.ts +2 -0
- package/link/lib/link/link.d.ts +15 -0
- package/link/lib/link/link.directives.d.ts +10 -0
- package/link/malva-ui-core-link.d.ts +5 -0
- package/list/index.d.ts +13 -0
- package/list/lib/list/list.d.ts +25 -0
- package/list/lib/list-item/list-item.d.ts +67 -0
- package/list/lib/list-item-actions.d.ts +12 -0
- package/list/lib/list-item-byline.d.ts +5 -0
- package/list/lib/list-item-group/list-item-group.d.ts +11 -0
- package/list/lib/list-item-link/list-item-link.d.ts +8 -0
- package/list/lib/list-item-media.d.ts +5 -0
- package/list/lib/list-item-meta.d.ts +5 -0
- package/list/lib/list-item-prefix.d.ts +5 -0
- package/list/lib/list-item-suffix.d.ts +5 -0
- package/list/lib/list-item-template.d.ts +10 -0
- package/list/lib/list-item-title.d.ts +5 -0
- package/list/lib/list-selectable.d.ts +73 -0
- package/list/malva-ui-core-list.d.ts +5 -0
- package/loader/index.d.ts +1 -0
- package/loader/lib/loader/loader.d.ts +84 -0
- package/loader/malva-ui-core-loader.d.ts +5 -0
- package/malva-ui-core.d.ts +5 -0
- package/menu/index.d.ts +9 -0
- package/menu/lib/menu/menu-group-label.d.ts +22 -0
- package/menu/lib/menu/menu-group.d.ts +27 -0
- package/menu/lib/menu/menu-item.d.ts +76 -0
- package/menu/lib/menu/menu-separator.d.ts +18 -0
- package/menu/lib/menu/menu-trigger.d.ts +255 -0
- package/menu/lib/menu/menu.d.ts +149 -0
- package/menu/lib/menu/menu.types.d.ts +13 -0
- package/menu/lib/menu/menubar.d.ts +131 -0
- package/menu/lib/menu/menubar.types.d.ts +82 -0
- package/menu/lib/menu/submenu-aim.d.ts +10 -0
- package/menu/malva-ui-core-menu.d.ts +5 -0
- package/notification/index.d.ts +4 -0
- package/notification/lib/notification-item/notification-item.d.ts +20 -0
- package/notification/lib/notification-ref.d.ts +12 -0
- package/notification/lib/notification.service.d.ts +43 -0
- package/notification/lib/notification.types.d.ts +52 -0
- package/notification/malva-ui-core-notification.d.ts +5 -0
- package/number-input/index.d.ts +1 -0
- package/number-input/lib/number-input/number-input.d.ts +162 -0
- package/number-input/malva-ui-core-number-input.d.ts +5 -0
- package/package.json +315 -0
- package/page/README.md +8 -0
- package/page/index.d.ts +14 -0
- package/page/lib/page/page-scroll.token.d.ts +19 -0
- package/page/lib/page/page-snap-controller.d.ts +87 -0
- package/page/lib/page/page-snap.directive.d.ts +40 -0
- package/page/lib/page/page.d.ts +74 -0
- package/page/lib/page-content/page-aside.d.ts +7 -0
- package/page/lib/page-content/page-content.d.ts +37 -0
- package/page/lib/page-dock/page-dock.d.ts +50 -0
- package/page/lib/page-dock/page-dock.slots.d.ts +16 -0
- package/page/lib/page-header/page-header.d.ts +77 -0
- package/page/lib/page-header/page-header.directives.d.ts +50 -0
- package/page/lib/page-shell/page-shell-color.d.ts +48 -0
- package/page/lib/page-shell/page-shell-css-color.d.ts +20 -0
- package/page/lib/page-shell/page-shell.d.ts +44 -0
- package/page/lib/page-shell/page-shell.slots.d.ts +16 -0
- package/page/lib/page-summary/page-summary-item.d.ts +11 -0
- package/page/lib/page-summary/page-summary.d.ts +36 -0
- package/page/malva-ui-core-page.d.ts +5 -0
- package/pagination/index.d.ts +4 -0
- package/pagination/lib/pagination/pagination.d.ts +182 -0
- package/pagination/lib/pagination/pagination.directives.d.ts +44 -0
- package/pagination/lib/pagination/pagination.model.d.ts +6 -0
- package/pagination/lib/pagination/pagination.service.d.ts +26 -0
- package/pagination/malva-ui-core-pagination.d.ts +5 -0
- package/pin-input/index.d.ts +2 -0
- package/pin-input/lib/pin-input/pin-input-separator.d.ts +24 -0
- package/pin-input/lib/pin-input/pin-input.d.ts +176 -0
- package/pin-input/malva-ui-core-pin-input.d.ts +5 -0
- package/popup/index.d.ts +9 -0
- package/popup/lib/popup/popup.d.ts +343 -0
- package/popup/lib/popup-container/popup-container.d.ts +61 -0
- package/popup/lib/popup-container/popup-container.token.d.ts +34 -0
- package/popup/lib/popup-content.d.ts +6 -0
- package/popup/lib/popup-header-content.d.ts +18 -0
- package/popup/lib/popup-pinned-content.d.ts +17 -0
- package/popup/lib/popup-positions.d.ts +105 -0
- package/popup/lib/popup-trigger/popup-trigger.d.ts +102 -0
- package/popup/lib/popup.service.d.ts +159 -0
- package/popup/malva-ui-core-popup.d.ts +5 -0
- package/progress/index.d.ts +1 -0
- package/progress/lib/progress/progress.d.ts +69 -0
- package/progress/malva-ui-core-progress.d.ts +5 -0
- package/radio/index.d.ts +2 -0
- package/radio/lib/radio/radio.d.ts +87 -0
- package/radio/lib/radio-group/radio-group.d.ts +35 -0
- package/radio/lib/radio-group-token.d.ts +7 -0
- package/radio/malva-ui-core-radio.d.ts +5 -0
- package/rating/index.d.ts +1 -0
- package/rating/lib/rating/rating.d.ts +100 -0
- package/rating/malva-ui-core-rating.d.ts +5 -0
- package/schematics/collection.json +12 -0
- package/schematics/ng-add/index.cjs +272 -0
- package/schematics/ng-add/schema.json +92 -0
- package/scrollbar/index.d.ts +1 -0
- package/scrollbar/lib/scrollbar/scrollbar.d.ts +166 -0
- package/scrollbar/malva-ui-core-scrollbar.d.ts +5 -0
- package/search-field/README.md +32 -0
- package/search-field/index.d.ts +2 -0
- package/search-field/lib/search-field/search-field.d.ts +347 -0
- package/search-field/lib/search-field/search-overlay-content.d.ts +59 -0
- package/search-field/malva-ui-core-search-field.d.ts +5 -0
- package/segmented/index.d.ts +4 -0
- package/segmented/lib/segmented/segmented.d.ts +80 -0
- package/segmented/lib/segmented-item/segmented-item.d.ts +89 -0
- package/segmented/lib/segmented-token.d.ts +22 -0
- package/segmented/lib/segmented.types.d.ts +9 -0
- package/segmented/malva-ui-core-segmented.d.ts +5 -0
- package/select/index.d.ts +3 -0
- package/select/lib/select/select.d.ts +379 -0
- package/select/lib/select-option.d.ts +1 -0
- package/select/lib/select-template.directives.d.ts +14 -0
- package/select/malva-ui-core-select.d.ts +5 -0
- package/sidebar/index.d.ts +15 -0
- package/sidebar/lib/sidebar/sidebar.d.ts +134 -0
- package/sidebar/lib/sidebar-content.d.ts +11 -0
- package/sidebar/lib/sidebar-context.d.ts +23 -0
- package/sidebar/lib/sidebar-footer.d.ts +9 -0
- package/sidebar/lib/sidebar-group/sidebar-group.d.ts +127 -0
- package/sidebar/lib/sidebar-header.d.ts +9 -0
- package/sidebar/lib/sidebar-item/sidebar-item.d.ts +104 -0
- package/sidebar/lib/sidebar-item-host.d.ts +18 -0
- package/sidebar/lib/sidebar-item-icon.d.ts +6 -0
- package/sidebar/lib/sidebar-mode.d.ts +2 -0
- package/sidebar/lib/sidebar-rail/sidebar-rail.d.ts +54 -0
- package/sidebar/lib/sidebar-trigger/sidebar-trigger.d.ts +67 -0
- package/sidebar/lib/sidebar-workspace/sidebar-workspace.d.ts +54 -0
- package/sidebar/lib/sidebar-workspace/sidebar-workspace.directives.d.ts +19 -0
- package/sidebar/lib/sidebar-workspace/sidebar-workspace.types.d.ts +20 -0
- package/sidebar/malva-ui-core-sidebar.d.ts +5 -0
- package/skeleton/index.d.ts +1 -0
- package/skeleton/lib/skeleton/skeleton.d.ts +60 -0
- package/skeleton/malva-ui-core-skeleton.d.ts +5 -0
- package/slider/index.d.ts +2 -0
- package/slider/lib/slider/slider-tooltip-def.d.ts +39 -0
- package/slider/lib/slider/slider.d.ts +171 -0
- package/slider/malva-ui-core-slider.d.ts +5 -0
- package/split-pane/index.d.ts +3 -0
- package/split-pane/lib/split-pane/split-pane-panel.d.ts +34 -0
- package/split-pane/lib/split-pane/split-pane.d.ts +98 -0
- package/split-pane/lib/split-pane/split-pane.types.d.ts +7 -0
- package/split-pane/malva-ui-core-split-pane.d.ts +5 -0
- package/status-indicator/index.d.ts +1 -0
- package/status-indicator/lib/status-indicator/status-indicator.d.ts +61 -0
- package/status-indicator/malva-ui-core-status-indicator.d.ts +5 -0
- package/stepper/index.d.ts +4 -0
- package/stepper/lib/stepper/step-header.d.ts +34 -0
- package/stepper/lib/stepper/step.d.ts +51 -0
- package/stepper/lib/stepper/stepper-token.d.ts +13 -0
- package/stepper/lib/stepper/stepper.d.ts +98 -0
- package/stepper/lib/stepper/stepper.types.d.ts +4 -0
- package/stepper/malva-ui-core-stepper.d.ts +5 -0
- package/styles/malva-ui.css +1706 -0
- package/styles/tokens.md +698 -0
- package/switch/index.d.ts +2 -0
- package/switch/lib/switch/switch.d.ts +118 -0
- package/switch/lib/switch-group/switch-group.d.ts +24 -0
- package/switch/lib/switch-group-token.d.ts +6 -0
- package/switch/malva-ui-core-switch.d.ts +5 -0
- package/tabs/index.d.ts +8 -0
- package/tabs/lib/tab/tab.d.ts +46 -0
- package/tabs/lib/tab-content/tab-content.d.ts +11 -0
- package/tabs/lib/tab-content-def.d.ts +6 -0
- package/tabs/lib/tab-def.d.ts +11 -0
- package/tabs/lib/tab-group-token.d.ts +10 -0
- package/tabs/lib/tab-item/tab-item.d.ts +28 -0
- package/tabs/lib/tabs/tabs.d.ts +202 -0
- package/tabs/lib/tabs.service.d.ts +24 -0
- package/tabs/malva-ui-core-tabs.d.ts +5 -0
- package/textarea/index.d.ts +1 -0
- package/textarea/lib/textarea/textarea.d.ts +77 -0
- package/textarea/malva-ui-core-textarea.d.ts +5 -0
- package/tile/index.d.ts +2 -0
- package/tile/lib/tile/tile.d.ts +20 -0
- package/tile/lib/tile-header.d.ts +6 -0
- package/tile/malva-ui-core-tile.d.ts +5 -0
- package/time-picker/README.md +7 -0
- package/time-picker/index.d.ts +1 -0
- package/time-picker/lib/time-picker/time-picker.d.ts +135 -0
- package/time-picker/lib/time-picker-column/time-picker-column.d.ts +111 -0
- package/time-picker/malva-ui-core-time-picker.d.ts +5 -0
- package/timeline/index.d.ts +5 -0
- package/timeline/lib/timeline/timeline-item-icon.d.ts +24 -0
- package/timeline/lib/timeline/timeline-item-meta.d.ts +24 -0
- package/timeline/lib/timeline/timeline-item.d.ts +52 -0
- package/timeline/lib/timeline/timeline.d.ts +27 -0
- package/timeline/lib/timeline/timeline.types.d.ts +18 -0
- package/timeline/malva-ui-core-timeline.d.ts +5 -0
- package/title/index.d.ts +1 -0
- package/title/lib/title/title.d.ts +63 -0
- package/title/malva-ui-core-title.d.ts +5 -0
- package/toast/index.d.ts +10 -0
- package/toast/lib/abstract-toast-container.d.ts +29 -0
- package/toast/lib/abstract-toast-item.d.ts +44 -0
- package/toast/lib/abstract-toast.service.d.ts +75 -0
- package/toast/lib/toast-container/toast-container.d.ts +23 -0
- package/toast/lib/toast-item/toast-item.d.ts +25 -0
- package/toast/lib/toast-ref.d.ts +33 -0
- package/toast/lib/toast-timer.d.ts +24 -0
- package/toast/lib/toast.directives.d.ts +13 -0
- package/toast/lib/toast.service.d.ts +41 -0
- package/toast/lib/toast.types.d.ts +139 -0
- package/toast/malva-ui-core-toast.d.ts +5 -0
- package/tokenizer/index.d.ts +3 -0
- package/tokenizer/lib/token/token.d.ts +60 -0
- package/tokenizer/lib/token-template.d.ts +29 -0
- package/tokenizer/lib/tokenizer/tokenizer.d.ts +109 -0
- package/tokenizer/malva-ui-core-tokenizer.d.ts +5 -0
- package/toolbar/index.d.ts +3 -0
- package/toolbar/lib/toolbar/toolbar-spacer.d.ts +5 -0
- package/toolbar/lib/toolbar/toolbar-widget.d.ts +75 -0
- package/toolbar/lib/toolbar/toolbar.d.ts +8 -0
- package/toolbar/malva-ui-core-toolbar.d.ts +5 -0
- package/tooltip/README.md +7 -0
- package/tooltip/index.d.ts +3 -0
- package/tooltip/lib/tooltip/tooltip-panel.d.ts +26 -0
- package/tooltip/lib/tooltip/tooltip.d.ts +148 -0
- package/tooltip/lib/tooltip/tooltip.types.d.ts +17 -0
- package/tooltip/malva-ui-core-tooltip.d.ts +5 -0
- package/tree/index.d.ts +4 -0
- package/tree/lib/tree/tree-context.d.ts +62 -0
- package/tree/lib/tree/tree-node-def.d.ts +33 -0
- package/tree/lib/tree/tree-node.d.ts +45 -0
- package/tree/lib/tree/tree-subtree.d.ts +63 -0
- package/tree/lib/tree/tree.d.ts +156 -0
- package/tree/malva-ui-core-tree.d.ts +5 -0
|
@@ -0,0 +1,1846 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, ViewEncapsulation, afterNextRender, computed, contentChildren, inject, Injector, input, model, NgZone, numberAttribute, output, signal, effect, untracked, viewChild, viewChildren, } from '@angular/core';
|
|
2
|
+
import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
3
|
+
import { Grid, GridRow, GridCell, GridCellWidget } from '@angular/aria/grid';
|
|
4
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
5
|
+
import { CdkVirtualScrollViewport, ScrollingModule, } from '@angular/cdk/scrolling';
|
|
6
|
+
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
7
|
+
import { LucideArrowUp, LucideArrowDown, LucideChevronsUpDown, LucideChevronRight, LucideFilter, LucideTable2, LucideFunnel, LucidePencil, LucideCheck, LucideX, LucidePlus, LucidePin, LucidePinOff, LucideEye, LucideTriangleAlert, } from '@lucide/angular';
|
|
8
|
+
import { MlvLoader } from '@malva-ui/core/loader';
|
|
9
|
+
import { MlvCheckbox } from '@malva-ui/core/checkbox';
|
|
10
|
+
import { MlvPagination } from '@malva-ui/core/pagination';
|
|
11
|
+
import { MlvSearchField } from '@malva-ui/core/search-field';
|
|
12
|
+
import { MlvResizeObserverService } from '@malva-ui/cdk/utils';
|
|
13
|
+
import { MlvInfiniteScroll } from '@malva-ui/cdk/infinite-scroll';
|
|
14
|
+
import { MlvButton, MlvButtonIcon } from '@malva-ui/core/button';
|
|
15
|
+
import { MlvBadge } from '@malva-ui/core/badge';
|
|
16
|
+
import { MlvEmptyState } from '@malva-ui/core/empty-state';
|
|
17
|
+
import { MlvDensityDirective, MLV_DENSITY_ELEMENT, } from '@malva-ui/cdk/density';
|
|
18
|
+
import { MLV_DATA_TABLE_I18N, MlvI18nResolverService } from '@malva-ui/i18n';
|
|
19
|
+
import { MlvDataSource, MlvArrayDataSource } from '../data-source';
|
|
20
|
+
import { MlvDataTableEditingService } from './services/data-table-editing.service';
|
|
21
|
+
import { MlvDataTableColumnVisibilityService } from './services/data-table-column-visibility.service';
|
|
22
|
+
import { MlvDataTablePinningService } from './services/data-table-pinning.service';
|
|
23
|
+
import { MlvDataTableCell } from '../data-table-cell';
|
|
24
|
+
import { MlvDataTableEditCell } from '../data-table-edit-cell';
|
|
25
|
+
import { MlvDataTableNoData } from '../data-table-no-data';
|
|
26
|
+
import { MlvDataTableError } from '../data-table-error';
|
|
27
|
+
import { MlvDataTableFooter } from '../data-table-footer';
|
|
28
|
+
import { MlvFilterDropdown } from '../filter-dropdown/filter-dropdown';
|
|
29
|
+
import { MlvPopup, MlvPopupContent, MlvPopupTrigger, } from '@malva-ui/core/popup';
|
|
30
|
+
import { columnCellStyles, columnWidthStyles, flattenRows, isColumnVisible, originalRow, parsePixelWidth, } from './data-table-layout';
|
|
31
|
+
import * as i0 from "@angular/core";
|
|
32
|
+
import * as i1 from "@malva-ui/cdk/density";
|
|
33
|
+
import * as i2 from "@angular/cdk/scrolling";
|
|
34
|
+
const DEFAULT_COLUMN_WIDTH = 150;
|
|
35
|
+
const DEFAULT_COLUMN_MIN_WIDTH = 60;
|
|
36
|
+
const DEFAULT_COLUMN_MAX_WIDTH = 2000;
|
|
37
|
+
const DEFAULT_SELECTION_COLUMN_WIDTH = 40;
|
|
38
|
+
const DEFAULT_ACTIONS_COLUMN_WIDTH = 80;
|
|
39
|
+
const COLUMN_RESIZE_KEYBOARD_STEP = 8;
|
|
40
|
+
const COLUMN_RESIZE_KEYBOARD_LARGE_STEP = 32;
|
|
41
|
+
export class MlvDataTable {
|
|
42
|
+
/** @protected Injected i18n translations for the data table. */
|
|
43
|
+
_i18n = inject(MLV_DATA_TABLE_I18N);
|
|
44
|
+
// ---- Inputs ----------------------------------------------------------------
|
|
45
|
+
columns = input.required(/* @ts-ignore */
|
|
46
|
+
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
47
|
+
data = input(undefined, /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
49
|
+
rowHeight = input(40, /* @ts-ignore */
|
|
50
|
+
...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
|
|
51
|
+
/** Enable CDK virtual scroll for large datasets. */
|
|
52
|
+
virtualScroll = input(false, { ...(ngDevMode ? { debugName: "virtualScroll" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
53
|
+
/** Alternating row shading. */
|
|
54
|
+
striped = input(false, { ...(ngDevMode ? { debugName: "striped" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
55
|
+
/** Cell border lines. */
|
|
56
|
+
bordered = input(false, { ...(ngDevMode ? { debugName: "bordered" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
57
|
+
/** Sticky table header on scroll. */
|
|
58
|
+
stickyHeader = input(true, { ...(ngDevMode ? { debugName: "stickyHeader" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
59
|
+
/** Maximum height of the scrollable table area (e.g. `'28rem'`, `'400px'`). Enables vertical scroll. */
|
|
60
|
+
maxHeight = input(undefined, /* @ts-ignore */
|
|
61
|
+
...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
|
|
62
|
+
/** Column group definitions for multi-row headers. */
|
|
63
|
+
columnGroups = input([], /* @ts-ignore */
|
|
64
|
+
...(ngDevMode ? [{ debugName: "columnGroups" }] : /* istanbul ignore next */ []));
|
|
65
|
+
/** Enable editable mode on the table. */
|
|
66
|
+
editable = input(false, { ...(ngDevMode ? { debugName: "editable" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
67
|
+
/** Edit granularity: `'row'` edits entire rows, `'cell'` edits individual cells. */
|
|
68
|
+
editMode = input('row', /* @ts-ignore */
|
|
69
|
+
...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
|
|
70
|
+
/** Show an "Add row" button in the toolbar when editable. */
|
|
71
|
+
addRow = input(false, { ...(ngDevMode ? { debugName: "addRow" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
72
|
+
/** Show a loading overlay on top of the table. */
|
|
73
|
+
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
74
|
+
/**
|
|
75
|
+
* Failed-request state. `false` (default) renders normally; `true` renders the
|
|
76
|
+
* error block with the localized default message; a non-empty string renders
|
|
77
|
+
* that string as the message.
|
|
78
|
+
*
|
|
79
|
+
* While set — and while {@link loading} is falsy — the error block replaces the
|
|
80
|
+
* table body, taking precedence over both the rows and the `mlvDataTableNoData`
|
|
81
|
+
* template so a failed request is never mistaken for an empty result set.
|
|
82
|
+
* Project `<ng-template mlvDataTableError>` to replace the default block.
|
|
83
|
+
*/
|
|
84
|
+
error = input(false, /* @ts-ignore */
|
|
85
|
+
...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
86
|
+
/** Row selection mode: `'single'`, `'multi'`, or `false` to disable. */
|
|
87
|
+
selectable = input(false, /* @ts-ignore */
|
|
88
|
+
...(ngDevMode ? [{ debugName: "selectable" }] : /* istanbul ignore next */ []));
|
|
89
|
+
/**
|
|
90
|
+
* Accessible name for the trailing actions column header (rendered when
|
|
91
|
+
* {@link editable} is on). The actions `<th>` has no visible label, so this
|
|
92
|
+
* string is exposed to assistive tech via visually-hidden text to satisfy the
|
|
93
|
+
* `empty-table-header` requirement. Defaults to `'Actions'`.
|
|
94
|
+
*
|
|
95
|
+
* NB: this is a plain English default input rather than a `MLV_DATA_TABLE_I18N`
|
|
96
|
+
* token entry — adding an i18n string is a follow-up (see `libs-data-table.md`).
|
|
97
|
+
*/
|
|
98
|
+
actionsHeaderLabel = input('Actions', /* @ts-ignore */
|
|
99
|
+
...(ngDevMode ? [{ debugName: "actionsHeaderLabel" }] : /* istanbul ignore next */ []));
|
|
100
|
+
/**
|
|
101
|
+
* **Experimental.** Opt into full cell-level keyboard navigation backed by the
|
|
102
|
+
* `@angular/aria` grid pattern (`ngGrid`/`ngGridRow`/`ngGridCell`/`ngGridCellWidget`).
|
|
103
|
+
*
|
|
104
|
+
* When `false` (default), the table keeps its stable **row-level** roving
|
|
105
|
+
* navigation (ArrowUp/Down move between rows, Home/End jump, Space/Enter toggle
|
|
106
|
+
* row selection). When `true`, arrow keys move focus between individual **cells**
|
|
107
|
+
* (Arrow keys in both axes, Home/End within a row), interactive cells expose an
|
|
108
|
+
* `ngGridCellWidget` so Enter pauses grid navigation to interact (edit inputs,
|
|
109
|
+
* action buttons), and Escape returns to grid navigation.
|
|
110
|
+
*
|
|
111
|
+
* Limitations of the current prototype (see `libs-data-table.md`):
|
|
112
|
+
* - **Requires non-virtual mode.** Cell navigation is inert while
|
|
113
|
+
* {@link virtualScroll} is `true` — CDK row recycling and the split
|
|
114
|
+
* header/body/footer tables are incompatible with the grid's DOM-registered
|
|
115
|
+
* cell/row collection. `virtualScroll` wins; the table falls back to
|
|
116
|
+
* row-level roving.
|
|
117
|
+
* - Row selection stays row-level (via the checkbox widget); the aria grid's
|
|
118
|
+
* own cell-selection model is intentionally not wired.
|
|
119
|
+
*/
|
|
120
|
+
cellNavigation = input(false, { ...(ngDevMode ? { debugName: "cellNavigation" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
121
|
+
/**
|
|
122
|
+
* Pagination strategy.
|
|
123
|
+
* - `'paged'` (default) — renders the classic paginator footer and swaps rows per page.
|
|
124
|
+
* - `'infinite'` — hides the paginator and emits `loadMore` when the user scrolls near the bottom of the scroll container. Consumers should append the new page's rows to the existing data source.
|
|
125
|
+
*/
|
|
126
|
+
paginationMode = input('paged', /* @ts-ignore */
|
|
127
|
+
...(ngDevMode ? [{ debugName: "paginationMode" }] : /* istanbul ignore next */ []));
|
|
128
|
+
/**
|
|
129
|
+
* Whether more data is available to load. Only consulted in `paginationMode: 'infinite'`.
|
|
130
|
+
* Bind this to the consumer's own "has more pages" flag to stop the directive from
|
|
131
|
+
* firing once the backing data source has been exhausted.
|
|
132
|
+
*/
|
|
133
|
+
hasMore = input(true, { ...(ngDevMode ? { debugName: "hasMore" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
134
|
+
/**
|
|
135
|
+
* Distance in px from the bottom of the scrollable area at which `loadMore` fires.
|
|
136
|
+
* Only consulted in `paginationMode: 'infinite'`. Defaults to `200`.
|
|
137
|
+
*/
|
|
138
|
+
infiniteScrollThreshold = input(200, /* @ts-ignore */
|
|
139
|
+
...(ngDevMode ? [{ debugName: "infiniteScrollThreshold" }] : /* istanbul ignore next */ []));
|
|
140
|
+
/** Where table-owned filter controls are displayed. */
|
|
141
|
+
filterDisplay = input('toolbar', /* @ts-ignore */
|
|
142
|
+
...(ngDevMode ? [{ debugName: "filterDisplay" }] : /* istanbul ignore next */ []));
|
|
143
|
+
/** Whether the built-in global-search field is rendered in the toolbar. */
|
|
144
|
+
showSearch = input(true, { ...(ngDevMode ? { debugName: "showSearch" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
145
|
+
/** Whether global search executes as the user types or only on submit. */
|
|
146
|
+
searchTrigger = input('live', /* @ts-ignore */
|
|
147
|
+
...(ngDevMode ? [{ debugName: "searchTrigger" }] : /* istanbul ignore next */ []));
|
|
148
|
+
/** Delay in milliseconds used by live global search. */
|
|
149
|
+
searchDebounce = input(200, { ...(ngDevMode ? { debugName: "searchDebounce" } : /* istanbul ignore next */ {}), transform: (value) => Math.max(0, numberAttribute(value, 200)) });
|
|
150
|
+
// ---- Outputs ----------------------------------------------------------------
|
|
151
|
+
/** Emits when a row enters edit mode. */
|
|
152
|
+
rowEditStart = output();
|
|
153
|
+
/** Emits when the user confirms edits on a row. */
|
|
154
|
+
rowEditSave = output();
|
|
155
|
+
/** Emits when the user cancels editing a row. */
|
|
156
|
+
rowEditCancel = output();
|
|
157
|
+
/** Emits when the user clicks "Add row". Consumer is responsible for adding the row. */
|
|
158
|
+
rowAdd = output();
|
|
159
|
+
/** Emits when the user clicks a data row. */
|
|
160
|
+
rowClick = output();
|
|
161
|
+
/** Emits when the row selection changes. */
|
|
162
|
+
selectionChange = output();
|
|
163
|
+
/**
|
|
164
|
+
* Emits when the user activates the error block's retry control (the default
|
|
165
|
+
* "Retry" button, or `retry()` from a custom `mlvDataTableError` template).
|
|
166
|
+
* The consumer owns the refetch and is responsible for clearing {@link error}.
|
|
167
|
+
*/
|
|
168
|
+
retry = output();
|
|
169
|
+
/**
|
|
170
|
+
* Emits after a pointer or keyboard interaction commits a column width, or
|
|
171
|
+
* after a reset removes a user width override.
|
|
172
|
+
*/
|
|
173
|
+
columnResize = output();
|
|
174
|
+
/**
|
|
175
|
+
* Emits in `paginationMode: 'infinite'` when the user scrolls near the bottom
|
|
176
|
+
* of the scroll container. The payload carries the next page number,
|
|
177
|
+
* current per-page size, and remaining scroll distance.
|
|
178
|
+
*/
|
|
179
|
+
loadMore = output();
|
|
180
|
+
/** Two-way bindable set of selected rows. */
|
|
181
|
+
selectedRows = model(new Set(), /* @ts-ignore */
|
|
182
|
+
...(ngDevMode ? [{ debugName: "selectedRows" }] : /* istanbul ignore next */ []));
|
|
183
|
+
/** Applied global-search query. Submit-mode drafts are committed here on search. */
|
|
184
|
+
searchQuery = model('', /* @ts-ignore */
|
|
185
|
+
...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
|
|
186
|
+
// ---- Content children -------------------------------------------------------
|
|
187
|
+
cellTemplates = contentChildren(MlvDataTableCell, { ...(ngDevMode ? { debugName: "cellTemplates" } : /* istanbul ignore next */ {}), descendants: true });
|
|
188
|
+
editCellTemplates = contentChildren(MlvDataTableEditCell, { ...(ngDevMode ? { debugName: "editCellTemplates" } : /* istanbul ignore next */ {}), descendants: true });
|
|
189
|
+
noDataTemplate = contentChildren(MlvDataTableNoData, { ...(ngDevMode ? { debugName: "noDataTemplate" } : /* istanbul ignore next */ {}), descendants: true });
|
|
190
|
+
errorTemplate = contentChildren(MlvDataTableError, { ...(ngDevMode ? { debugName: "errorTemplate" } : /* istanbul ignore next */ {}), descendants: true });
|
|
191
|
+
footerTemplate = contentChildren(MlvDataTableFooter, { ...(ngDevMode ? { debugName: "footerTemplate" } : /* istanbul ignore next */ {}), descendants: true });
|
|
192
|
+
// ---- View children ----------------------------------------------------------
|
|
193
|
+
tableWrapperRef = viewChild('tableWrapper', /* @ts-ignore */
|
|
194
|
+
...(ngDevMode ? [{ debugName: "tableWrapperRef" }] : /* istanbul ignore next */ []));
|
|
195
|
+
tableRef = viewChild('tableEl', /* @ts-ignore */
|
|
196
|
+
...(ngDevMode ? [{ debugName: "tableRef" }] : /* istanbul ignore next */ []));
|
|
197
|
+
/**
|
|
198
|
+
* @private Rendered resize separators. Used after rendering to measure
|
|
199
|
+
* non-pixel CSS widths for accurate initial ARIA values.
|
|
200
|
+
*/
|
|
201
|
+
_resizeHandleRefs = viewChildren('resizeHandle', /* @ts-ignore */
|
|
202
|
+
...(ngDevMode ? [{ debugName: "_resizeHandleRefs" }] : /* istanbul ignore next */ []));
|
|
203
|
+
/**
|
|
204
|
+
* @private Toolbar "Filters" trigger button. Focus is returned here when the
|
|
205
|
+
* filter dialog popup closes, matching the overlay focus-restore pattern.
|
|
206
|
+
*/
|
|
207
|
+
_filterTriggerRef = viewChild('filterBtn', { ...(ngDevMode ? { debugName: "_filterTriggerRef" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
208
|
+
/** @private CDK virtual-scroll viewport (only present in virtual-scroll mode). */
|
|
209
|
+
_virtualViewportRef = viewChild(CdkVirtualScrollViewport, /* @ts-ignore */
|
|
210
|
+
...(ngDevMode ? [{ debugName: "_virtualViewportRef" }] : /* istanbul ignore next */ []));
|
|
211
|
+
// ---- Services ---------------------------------------------------------------
|
|
212
|
+
/** @private DestroyRef used for observable cleanup. */
|
|
213
|
+
_destroyRef = inject(DestroyRef);
|
|
214
|
+
/** @private Shared resize-observer service used to track the host width. */
|
|
215
|
+
_resizeService = inject(MlvResizeObserverService);
|
|
216
|
+
/** @private Owning document for pointer fallback listeners and DOM queries. */
|
|
217
|
+
_document = inject(DOCUMENT);
|
|
218
|
+
/** @private Host element ref, observed for width and used for row focus queries. */
|
|
219
|
+
_el = inject(ElementRef);
|
|
220
|
+
/** @private Injector used to schedule `afterNextRender` from imperative handlers. */
|
|
221
|
+
_injector = inject(Injector);
|
|
222
|
+
/** @private Angular zone — column-resize drag listeners run outside it so pointer moves don't trigger a full CD tick per event (see {@link onResizeStart}). */
|
|
223
|
+
_ngZone = inject(NgZone);
|
|
224
|
+
/** @private Resolver for ICU parameterized i18n strings. */
|
|
225
|
+
_resolver = inject(MlvI18nResolverService);
|
|
226
|
+
/**
|
|
227
|
+
* @protected Flat-rows index of the data row that currently owns the roving
|
|
228
|
+
* `tabindex="0"`. All other data rows are `tabindex="-1"`, giving the row grid
|
|
229
|
+
* a single tab stop. Clamped in a constructor effect when `flatRows` changes.
|
|
230
|
+
*/
|
|
231
|
+
_focusedRowIndex = signal(0, /* @ts-ignore */
|
|
232
|
+
...(ngDevMode ? [{ debugName: "_focusedRowIndex" }] : /* istanbul ignore next */ []));
|
|
233
|
+
// ---- Internal state ---------------------------------------------------------
|
|
234
|
+
containerWidth = signal(0, /* @ts-ignore */
|
|
235
|
+
...(ngDevMode ? [{ debugName: "containerWidth" }] : /* istanbul ignore next */ []));
|
|
236
|
+
activeFilters = model([], /* @ts-ignore */
|
|
237
|
+
...(ngDevMode ? [{ debugName: "activeFilters" }] : /* istanbul ignore next */ []));
|
|
238
|
+
/** @protected Search text currently shown in the toolbar, including submit-mode drafts. */
|
|
239
|
+
_searchDraft = signal('', /* @ts-ignore */
|
|
240
|
+
...(ngDevMode ? [{ debugName: "_searchDraft" }] : /* istanbul ignore next */ []));
|
|
241
|
+
currentSort = signal(null, /* @ts-ignore */
|
|
242
|
+
...(ngDevMode ? [{ debugName: "currentSort" }] : /* istanbul ignore next */ []));
|
|
243
|
+
currentPage = signal(1, /* @ts-ignore */
|
|
244
|
+
...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
|
|
245
|
+
currentPerPage = signal(10, /* @ts-ignore */
|
|
246
|
+
...(ngDevMode ? [{ debugName: "currentPerPage" }] : /* istanbul ignore next */ []));
|
|
247
|
+
/** @private Set of currently expanded tree rows (by original row reference). */
|
|
248
|
+
_expanded = signal(new Set(), /* @ts-ignore */
|
|
249
|
+
...(ngDevMode ? [{ debugName: "_expanded" }] : /* istanbul ignore next */ []));
|
|
250
|
+
/** @private User-resized column widths keyed by column key (px). Committed once per completed pointer drag. */
|
|
251
|
+
_columnWidths = signal(new Map(), /* @ts-ignore */
|
|
252
|
+
...(ngDevMode ? [{ debugName: "_columnWidths" }] : /* istanbul ignore next */ []));
|
|
253
|
+
/**
|
|
254
|
+
* @private Live width (px) of the column currently being drag-resized, applied
|
|
255
|
+
* optimistically during the drag before the committed {@link _columnWidths} map is
|
|
256
|
+
* updated on pointer release. `null` when no resize is in progress. Kept separate from
|
|
257
|
+
* {@link _columnWidths} so a move updates a single lightweight signal instead of
|
|
258
|
+
* cloning the whole widths map every frame.
|
|
259
|
+
*/
|
|
260
|
+
_liveResize = signal(null, /* @ts-ignore */
|
|
261
|
+
...(ngDevMode ? [{ debugName: "_liveResize" }] : /* istanbul ignore next */ []));
|
|
262
|
+
/**
|
|
263
|
+
* @private Last DOM-resolved bounds for each resize handle. CSS lengths such
|
|
264
|
+
* as `rem` can be resolved to pixels from the rendered header during an
|
|
265
|
+
* interaction and then reused by the separator's ARIA attributes.
|
|
266
|
+
*/
|
|
267
|
+
_resolvedResizeBounds = signal(new Map(), /* @ts-ignore */
|
|
268
|
+
...(ngDevMode ? [{ debugName: "_resolvedResizeBounds" }] : /* istanbul ignore next */ []));
|
|
269
|
+
/**
|
|
270
|
+
* @private Rendered header widths keyed by column. This supplies accurate
|
|
271
|
+
* separator values when a column's declared width uses `rem`, `%`, or layout.
|
|
272
|
+
*/
|
|
273
|
+
_measuredColumnWidths = signal(new Map(), /* @ts-ignore */
|
|
274
|
+
...(ngDevMode ? [{ debugName: "_measuredColumnWidths" }] : /* istanbul ignore next */ []));
|
|
275
|
+
/** @private Rendered widths of the optional selection and actions columns. */
|
|
276
|
+
_measuredAuxiliaryWidths = signal({}, /* @ts-ignore */
|
|
277
|
+
...(ngDevMode ? [{ debugName: "_measuredAuxiliaryWidths" }] : /* istanbul ignore next */ []));
|
|
278
|
+
/** @protected Native vertical-scrollbar width inside the virtual viewport. */
|
|
279
|
+
_virtualScrollbarGutter = signal(0, /* @ts-ignore */
|
|
280
|
+
...(ngDevMode ? [{ debugName: "_virtualScrollbarGutter" }] : /* istanbul ignore next */ []));
|
|
281
|
+
/**
|
|
282
|
+
* @protected Inert outer scroll width that reproduces the virtual viewport's
|
|
283
|
+
* native horizontal range without changing any split table's layout.
|
|
284
|
+
*/
|
|
285
|
+
_virtualHorizontalScrollExtent = signal(0, /* @ts-ignore */
|
|
286
|
+
...(ngDevMode ? [{ debugName: "_virtualHorizontalScrollExtent" }] : /* istanbul ignore next */ []));
|
|
287
|
+
/** @private Whether table geometry is currently frozen to explicit pixel widths. */
|
|
288
|
+
_columnLayoutFrozen = computed(() => this._liveResize() !== null || this._columnWidths().size > 0, /* @ts-ignore */
|
|
289
|
+
...(ngDevMode ? [{ debugName: "_columnLayoutFrozen" }] : /* istanbul ignore next */ []));
|
|
290
|
+
/**
|
|
291
|
+
* @private Exact measured utility-column styles used once data columns are
|
|
292
|
+
* frozen. Keeping selection/actions cells and their `<col>` elements on the
|
|
293
|
+
* same widths prevents fixed table layout from redistributing data columns.
|
|
294
|
+
*/
|
|
295
|
+
_auxiliaryColumnStyles = computed(() => {
|
|
296
|
+
const measured = this._measuredAuxiliaryWidths();
|
|
297
|
+
if (!this._columnLayoutFrozen()) {
|
|
298
|
+
return { selection: null, actions: null };
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
selection: this._fixedColumnWidthStyles(measured.selection),
|
|
302
|
+
actions: this._fixedColumnWidthStyles(measured.actions),
|
|
303
|
+
};
|
|
304
|
+
}, /* @ts-ignore */
|
|
305
|
+
...(ngDevMode ? [{ debugName: "_auxiliaryColumnStyles" }] : /* istanbul ignore next */ []));
|
|
306
|
+
/**
|
|
307
|
+
* @private Per-row cache of the cell-template `ngTemplateOutletContext`, keyed by the
|
|
308
|
+
* flattened row object identity (see {@link getCellContext}). A `WeakMap` so entries
|
|
309
|
+
* for rows dropped by a `flatRows()` recompute are reclaimed automatically.
|
|
310
|
+
*/
|
|
311
|
+
_cellContextCache = new WeakMap();
|
|
312
|
+
/** @private Stable one-column arrays used by repeated header filter editors. */
|
|
313
|
+
_columnFilterLists = new Map();
|
|
314
|
+
/** @private Editing state moved to MlvDataTableEditingService. */
|
|
315
|
+
_editing = inject((MlvDataTableEditingService));
|
|
316
|
+
/** @private Column visibility state moved to MlvDataTableColumnVisibilityService. */
|
|
317
|
+
_visibility = inject(MlvDataTableColumnVisibilityService);
|
|
318
|
+
/** @private Pin override state moved to MlvDataTablePinningService. */
|
|
319
|
+
_pinning = inject(MlvDataTablePinningService);
|
|
320
|
+
// ---- Derived state ----------------------------------------------------------
|
|
321
|
+
effectiveDataSource = computed(() => {
|
|
322
|
+
const d = this.data();
|
|
323
|
+
if (d instanceof MlvDataSource) {
|
|
324
|
+
return d;
|
|
325
|
+
}
|
|
326
|
+
const rows = Array.isArray(d) ? d : [];
|
|
327
|
+
return new MlvArrayDataSource(rows);
|
|
328
|
+
}, /* @ts-ignore */
|
|
329
|
+
...(ngDevMode ? [{ debugName: "effectiveDataSource" }] : /* istanbul ignore next */ []));
|
|
330
|
+
displayRows = computed(() => {
|
|
331
|
+
const ds = this.effectiveDataSource();
|
|
332
|
+
return ds.connect()();
|
|
333
|
+
}, /* @ts-ignore */
|
|
334
|
+
...(ngDevMode ? [{ debugName: "displayRows" }] : /* istanbul ignore next */ []));
|
|
335
|
+
flatRows = computed(() => {
|
|
336
|
+
const rows = this.displayRows();
|
|
337
|
+
const expanded = this._expanded();
|
|
338
|
+
return flattenRows(rows, expanded);
|
|
339
|
+
}, /* @ts-ignore */
|
|
340
|
+
...(ngDevMode ? [{ debugName: "flatRows" }] : /* istanbul ignore next */ []));
|
|
341
|
+
totalItems = computed(() => this.effectiveDataSource().totalItems(), /* @ts-ignore */
|
|
342
|
+
...(ngDevMode ? [{ debugName: "totalItems" }] : /* istanbul ignore next */ []));
|
|
343
|
+
shownItems = computed(() => this.flatRows().length, /* @ts-ignore */
|
|
344
|
+
...(ngDevMode ? [{ debugName: "shownItems" }] : /* istanbul ignore next */ []));
|
|
345
|
+
visibleColumns = computed(() => {
|
|
346
|
+
const cols = this.columns();
|
|
347
|
+
const containerW = this.containerWidth();
|
|
348
|
+
const widths = this._columnWidths();
|
|
349
|
+
const live = this._liveResize();
|
|
350
|
+
const measured = this._measuredColumnWidths();
|
|
351
|
+
const freezeLayout = this._columnLayoutFrozen();
|
|
352
|
+
const pinOverrides = this._pinning.overrides();
|
|
353
|
+
const hidden = this._visibility.hiddenColumns();
|
|
354
|
+
return cols
|
|
355
|
+
.map((col) => {
|
|
356
|
+
const visible = isColumnVisible(col, containerW);
|
|
357
|
+
// During a drag the live width wins over the committed map value so the
|
|
358
|
+
// column tracks the pointer before the map is updated on pointer release.
|
|
359
|
+
const w = live && live.key === col.key ? live.width : widths.get(col.key);
|
|
360
|
+
const override = pinOverrides.get(col.key);
|
|
361
|
+
return {
|
|
362
|
+
...col,
|
|
363
|
+
pinned: override ? override.pinned : col.pinned,
|
|
364
|
+
pinSide: override ? override.pinSide : col.pinSide,
|
|
365
|
+
_currentWidth: w ??
|
|
366
|
+
(freezeLayout
|
|
367
|
+
? (measured.get(col.key) ?? parsePixelWidth(col.width))
|
|
368
|
+
: undefined),
|
|
369
|
+
_visible: visible,
|
|
370
|
+
};
|
|
371
|
+
})
|
|
372
|
+
.filter((c) => c._visible !== false && !hidden.has(c.key))
|
|
373
|
+
.sort((a, b) => {
|
|
374
|
+
const order = (col) => {
|
|
375
|
+
if (col.pinned && col.pinSide !== 'right')
|
|
376
|
+
return 0;
|
|
377
|
+
if (!col.pinned)
|
|
378
|
+
return 1;
|
|
379
|
+
return 2; // pinned right
|
|
380
|
+
};
|
|
381
|
+
return order(a) - order(b);
|
|
382
|
+
});
|
|
383
|
+
}, /* @ts-ignore */
|
|
384
|
+
...(ngDevMode ? [{ debugName: "visibleColumns" }] : /* istanbul ignore next */ []));
|
|
385
|
+
pinnedLeftColumns = computed(() => this.visibleColumns().filter((c) => c.pinned && c.pinSide !== 'right'), /* @ts-ignore */
|
|
386
|
+
...(ngDevMode ? [{ debugName: "pinnedLeftColumns" }] : /* istanbul ignore next */ []));
|
|
387
|
+
pinnedRightColumns = computed(() => this.visibleColumns().filter((c) => c.pinned && c.pinSide === 'right'), /* @ts-ignore */
|
|
388
|
+
...(ngDevMode ? [{ debugName: "pinnedRightColumns" }] : /* istanbul ignore next */ []));
|
|
389
|
+
columnOffsets = computed(() => {
|
|
390
|
+
const map = new Map();
|
|
391
|
+
const leftWidths = [];
|
|
392
|
+
for (const col of this.pinnedLeftColumns()) {
|
|
393
|
+
map.set(col.key + '_left', this._sumCssLengths(leftWidths));
|
|
394
|
+
leftWidths.push(this._columnOffsetWidth(col));
|
|
395
|
+
}
|
|
396
|
+
const rightWidths = [];
|
|
397
|
+
const rightCols = [...this.pinnedRightColumns()].reverse();
|
|
398
|
+
for (const col of rightCols) {
|
|
399
|
+
map.set(col.key + '_right', this._sumCssLengths(rightWidths));
|
|
400
|
+
rightWidths.push(this._columnOffsetWidth(col));
|
|
401
|
+
}
|
|
402
|
+
return map;
|
|
403
|
+
}, /* @ts-ignore */
|
|
404
|
+
...(ngDevMode ? [{ debugName: "columnOffsets" }] : /* istanbul ignore next */ []));
|
|
405
|
+
/**
|
|
406
|
+
* Exact table width used while at least one column has a live or committed
|
|
407
|
+
* user override. Freezing every measured column and the table to the same
|
|
408
|
+
* pixel sum prevents `width: 100%` table layout from redistributing the
|
|
409
|
+
* committed width. All virtual split tables and their viewport consume this
|
|
410
|
+
* same value.
|
|
411
|
+
*/
|
|
412
|
+
resizedTableWidth = computed(() => {
|
|
413
|
+
if (!this._columnLayoutFrozen())
|
|
414
|
+
return null;
|
|
415
|
+
const measured = this._measuredColumnWidths();
|
|
416
|
+
const auxiliary = this._measuredAuxiliaryWidths();
|
|
417
|
+
let total = 0;
|
|
418
|
+
for (const col of this.visibleColumns()) {
|
|
419
|
+
total +=
|
|
420
|
+
col._currentWidth ??
|
|
421
|
+
measured.get(col.key) ??
|
|
422
|
+
parsePixelWidth(col.width) ??
|
|
423
|
+
DEFAULT_COLUMN_WIDTH;
|
|
424
|
+
}
|
|
425
|
+
if (this.selectable()) {
|
|
426
|
+
total += auxiliary.selection ?? DEFAULT_SELECTION_COLUMN_WIDTH;
|
|
427
|
+
}
|
|
428
|
+
if (this.editable()) {
|
|
429
|
+
total += auxiliary.actions ?? DEFAULT_ACTIONS_COLUMN_WIDTH;
|
|
430
|
+
}
|
|
431
|
+
return this._roundColumnWidth(total);
|
|
432
|
+
}, /* @ts-ignore */
|
|
433
|
+
...(ngDevMode ? [{ debugName: "resizedTableWidth" }] : /* istanbul ignore next */ []));
|
|
434
|
+
/**
|
|
435
|
+
* @private Memoized inline `[style]` object per visible column, keyed by column key.
|
|
436
|
+
* Rebuilt only when {@link visibleColumns} or {@link columnOffsets} change (width /
|
|
437
|
+
* pin / visibility / responsive / live-resize changes). Cell `[style]` bindings read
|
|
438
|
+
* this map through {@link getCellStyle} so they receive a *stable* object reference
|
|
439
|
+
* across change-detection cycles instead of a freshly allocated `Record` per cell per
|
|
440
|
+
* CD; the header and body cells of a column share one object.
|
|
441
|
+
*/
|
|
442
|
+
_columnStyles = computed(() => {
|
|
443
|
+
const offsets = this.columnOffsets();
|
|
444
|
+
const map = new Map();
|
|
445
|
+
for (const col of this.visibleColumns()) {
|
|
446
|
+
map.set(col.key, columnCellStyles(col, offsets));
|
|
447
|
+
}
|
|
448
|
+
return map;
|
|
449
|
+
}, /* @ts-ignore */
|
|
450
|
+
...(ngDevMode ? [{ debugName: "_columnStyles" }] : /* istanbul ignore next */ []));
|
|
451
|
+
/**
|
|
452
|
+
* @private Display cell templates resolved to a `Map<columnKey, TemplateRef>`, rebuilt
|
|
453
|
+
* only when the projected {@link cellTemplates} set changes. Replaces a per-cell
|
|
454
|
+
* `Array.find()` on every CD ({@link getTemplate}). First registration wins for
|
|
455
|
+
* duplicate keys, preserving the previous `find()` (first-match) semantics.
|
|
456
|
+
*/
|
|
457
|
+
_cellTemplateMap = computed(() => {
|
|
458
|
+
const map = new Map();
|
|
459
|
+
for (const t of this.cellTemplates()) {
|
|
460
|
+
const key = t.columnKey();
|
|
461
|
+
if (!map.has(key))
|
|
462
|
+
map.set(key, t.templateRef);
|
|
463
|
+
}
|
|
464
|
+
return map;
|
|
465
|
+
}, /* @ts-ignore */
|
|
466
|
+
...(ngDevMode ? [{ debugName: "_cellTemplateMap" }] : /* istanbul ignore next */ []));
|
|
467
|
+
/**
|
|
468
|
+
* @private Edit-mode cell templates resolved to a `Map<columnKey, TemplateRef>`,
|
|
469
|
+
* rebuilt only when the projected {@link editCellTemplates} set changes. Replaces a
|
|
470
|
+
* per-cell `Array.find()` on every CD ({@link getEditTemplate}). First registration
|
|
471
|
+
* wins for duplicate keys.
|
|
472
|
+
*/
|
|
473
|
+
_editCellTemplateMap = computed(() => {
|
|
474
|
+
const map = new Map();
|
|
475
|
+
for (const t of this.editCellTemplates()) {
|
|
476
|
+
const key = t.columnKey();
|
|
477
|
+
if (!map.has(key))
|
|
478
|
+
map.set(key, t.templateRef);
|
|
479
|
+
}
|
|
480
|
+
return map;
|
|
481
|
+
}, /* @ts-ignore */
|
|
482
|
+
...(ngDevMode ? [{ debugName: "_editCellTemplateMap" }] : /* istanbul ignore next */ []));
|
|
483
|
+
/**
|
|
484
|
+
* @protected Whether the experimental aria cell-level grid navigation is
|
|
485
|
+
* active. Gated to non-virtual mode: `virtualScroll` recycles rows out of the
|
|
486
|
+
* DOM, which the aria grid's DOM-registered row/cell collection cannot track,
|
|
487
|
+
* so cell navigation yields to the stable row-roving path when virtual scroll
|
|
488
|
+
* is on.
|
|
489
|
+
*/
|
|
490
|
+
_cellNav = computed(() => this.cellNavigation() && !this.virtualScroll(), /* @ts-ignore */
|
|
491
|
+
...(ngDevMode ? [{ debugName: "_cellNav" }] : /* istanbul ignore next */ []));
|
|
492
|
+
/**
|
|
493
|
+
* Whether the error block replaces the table body. An in-flight refetch wins:
|
|
494
|
+
* while `loading` is truthy the previous error is suppressed so the loader is
|
|
495
|
+
* not competing with a stale failure message.
|
|
496
|
+
*/
|
|
497
|
+
showError = computed(() => {
|
|
498
|
+
const error = this.error();
|
|
499
|
+
if (this.loading())
|
|
500
|
+
return false;
|
|
501
|
+
return typeof error === 'string' ? error.trim().length > 0 : error === true;
|
|
502
|
+
}, /* @ts-ignore */
|
|
503
|
+
...(ngDevMode ? [{ debugName: "showError" }] : /* istanbul ignore next */ []));
|
|
504
|
+
/**
|
|
505
|
+
* Message rendered by the error block: the `error` string when one was
|
|
506
|
+
* supplied, otherwise the localized `errorMessage` default.
|
|
507
|
+
*/
|
|
508
|
+
errorMessage = computed(() => {
|
|
509
|
+
const error = this.error();
|
|
510
|
+
return typeof error === 'string' && error.trim().length > 0
|
|
511
|
+
? error
|
|
512
|
+
: this._i18n().errorMessage;
|
|
513
|
+
}, /* @ts-ignore */
|
|
514
|
+
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
515
|
+
/**
|
|
516
|
+
* @private Stable `retry()` callback handed to the `mlvDataTableError`
|
|
517
|
+
* template context, so the context object identity only changes with the
|
|
518
|
+
* message.
|
|
519
|
+
*/
|
|
520
|
+
_emitRetry = () => this.onRetry();
|
|
521
|
+
/**
|
|
522
|
+
* @protected `ngTemplateOutletContext` for a projected `mlvDataTableError`
|
|
523
|
+
* template.
|
|
524
|
+
*/
|
|
525
|
+
_errorContext = computed(() => ({
|
|
526
|
+
$implicit: this.errorMessage(),
|
|
527
|
+
retry: this._emitRetry,
|
|
528
|
+
}), /* @ts-ignore */
|
|
529
|
+
...(ngDevMode ? [{ debugName: "_errorContext" }] : /* istanbul ignore next */ []));
|
|
530
|
+
/**
|
|
531
|
+
* @private Per-column raw-value → label lookup, memoized from
|
|
532
|
+
* `visibleColumns()`. Falls back to `filterConfig.options` when a column
|
|
533
|
+
* declares no explicit `valueLabels`. Keys are stringified raw values.
|
|
534
|
+
*/
|
|
535
|
+
_columnValueLabels = computed(() => {
|
|
536
|
+
const map = new Map();
|
|
537
|
+
for (const col of this.visibleColumns()) {
|
|
538
|
+
const source = col.valueLabels ?? col.filterConfig?.options;
|
|
539
|
+
if (!source)
|
|
540
|
+
continue;
|
|
541
|
+
const labels = new Map();
|
|
542
|
+
if (Array.isArray(source)) {
|
|
543
|
+
for (const option of source) {
|
|
544
|
+
labels.set(String(option.value), option.label);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
for (const [value, label] of Object.entries(source)) {
|
|
549
|
+
labels.set(value, label);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (labels.size > 0)
|
|
553
|
+
map.set(col.key, labels);
|
|
554
|
+
}
|
|
555
|
+
return map;
|
|
556
|
+
}, /* @ts-ignore */
|
|
557
|
+
...(ngDevMode ? [{ debugName: "_columnValueLabels" }] : /* istanbul ignore next */ []));
|
|
558
|
+
hasFilters = computed(() => this.activeFilters().length > 0, /* @ts-ignore */
|
|
559
|
+
...(ngDevMode ? [{ debugName: "hasFilters" }] : /* istanbul ignore next */ []));
|
|
560
|
+
/** Columns included in the global query, independent of responsive visibility. */
|
|
561
|
+
searchableColumns = computed(() => this.columns().filter((column) => column.searchable), /* @ts-ignore */
|
|
562
|
+
...(ngDevMode ? [{ debugName: "searchableColumns" }] : /* istanbul ignore next */ []));
|
|
563
|
+
/** Stable key list passed to the data source for OR-across-columns search. */
|
|
564
|
+
searchableColumnKeys = computed(() => this.searchableColumns().map((column) => column.key), /* @ts-ignore */
|
|
565
|
+
...(ngDevMode ? [{ debugName: "searchableColumnKeys" }] : /* istanbul ignore next */ []));
|
|
566
|
+
/** Whether a global search field belongs in the toolbar. */
|
|
567
|
+
hasSearchableColumns = computed(() => this.searchableColumns().length > 0, /* @ts-ignore */
|
|
568
|
+
...(ngDevMode ? [{ debugName: "hasSearchableColumns" }] : /* istanbul ignore next */ []));
|
|
569
|
+
/** Whether any configured column supports filtering. */
|
|
570
|
+
hasFilterableColumns = computed(() => this.columns().some((column) => column.filterable), /* @ts-ignore */
|
|
571
|
+
...(ngDevMode ? [{ debugName: "hasFilterableColumns" }] : /* istanbul ignore next */ []));
|
|
572
|
+
/** Localized active-filter summary announced when the committed set changes. */
|
|
573
|
+
activeFilterLabel = computed(() => this._resolver.resolve(this._i18n(), 'activeFilterCount', { count: this.activeFilters().length }), /* @ts-ignore */
|
|
574
|
+
...(ngDevMode ? [{ debugName: "activeFilterLabel" }] : /* istanbul ignore next */ []));
|
|
575
|
+
/** Total number of rendered columns (visible + selection checkbox + actions). */
|
|
576
|
+
totalColumnCount = computed(() => {
|
|
577
|
+
let count = this.visibleColumns().length;
|
|
578
|
+
if (this.selectable())
|
|
579
|
+
count++;
|
|
580
|
+
if (this.editable())
|
|
581
|
+
count++;
|
|
582
|
+
return count;
|
|
583
|
+
}, /* @ts-ignore */
|
|
584
|
+
...(ngDevMode ? [{ debugName: "totalColumnCount" }] : /* istanbul ignore next */ []));
|
|
585
|
+
/**
|
|
586
|
+
* Pixel/CSS height applied to the `cdk-virtual-scroll-viewport` when `virtualScroll` is on.
|
|
587
|
+
* Falls back to `'24rem'` if `maxHeight` is not provided. The viewport requires an explicit
|
|
588
|
+
* height because `cdk-virtual-scroll-viewport` does not derive its size from its content.
|
|
589
|
+
*/
|
|
590
|
+
virtualViewportHeight = computed(() => this.maxHeight() ?? '24rem', /* @ts-ignore */
|
|
591
|
+
...(ngDevMode ? [{ debugName: "virtualViewportHeight" }] : /* istanbul ignore next */ []));
|
|
592
|
+
/** Whether any column has `hideable: true`. */
|
|
593
|
+
hasHideableColumns = computed(() => this.columns().some((c) => c.hideable), /* @ts-ignore */
|
|
594
|
+
...(ngDevMode ? [{ debugName: "hasHideableColumns" }] : /* istanbul ignore next */ []));
|
|
595
|
+
/** Whether the toolbar has a visible control or table-owned status to render. */
|
|
596
|
+
hasToolbarContent = computed(() => (this.showSearch() && this.hasSearchableColumns()) ||
|
|
597
|
+
(this.filterDisplay() !== 'none' && this.hasFilters()) ||
|
|
598
|
+
(this.editable() && this.addRow()) ||
|
|
599
|
+
this.hasHideableColumns() ||
|
|
600
|
+
(this.filterDisplay() === 'toolbar' && this.hasFilterableColumns()), /* @ts-ignore */
|
|
601
|
+
...(ngDevMode ? [{ debugName: "hasToolbarContent" }] : /* istanbul ignore next */ []));
|
|
602
|
+
/** All columns (for column visibility popup). */
|
|
603
|
+
allColumns = computed(() => this.columns(), /* @ts-ignore */
|
|
604
|
+
...(ngDevMode ? [{ debugName: "allColumns" }] : /* istanbul ignore next */ []));
|
|
605
|
+
/** Whether all visible rows are selected. */
|
|
606
|
+
allRowsSelected = computed(() => {
|
|
607
|
+
const rows = this.flatRows();
|
|
608
|
+
const selected = this.selectedRows();
|
|
609
|
+
return rows.length > 0 && rows.every((r) => selected.has(this._ref(r)));
|
|
610
|
+
}, /* @ts-ignore */
|
|
611
|
+
...(ngDevMode ? [{ debugName: "allRowsSelected" }] : /* istanbul ignore next */ []));
|
|
612
|
+
/** Whether some (but not all) rows are selected. */
|
|
613
|
+
someRowsSelected = computed(() => {
|
|
614
|
+
const rows = this.flatRows();
|
|
615
|
+
const selected = this.selectedRows();
|
|
616
|
+
const count = rows.filter((r) => selected.has(this._ref(r))).length;
|
|
617
|
+
return count > 0 && count < rows.length;
|
|
618
|
+
}, /* @ts-ignore */
|
|
619
|
+
...(ngDevMode ? [{ debugName: "someRowsSelected" }] : /* istanbul ignore next */ []));
|
|
620
|
+
/** Whether the table has multi-row column groups defined. */
|
|
621
|
+
hasColumnGroups = computed(() => this.columnGroups().length > 0, /* @ts-ignore */
|
|
622
|
+
...(ngDevMode ? [{ debugName: "hasColumnGroups" }] : /* istanbul ignore next */ []));
|
|
623
|
+
/**
|
|
624
|
+
* Computes the group header row layout.
|
|
625
|
+
* Columns covered by a group get colspan in the group row.
|
|
626
|
+
* Standalone columns (not in any group) get rowspan="2" in the main header row.
|
|
627
|
+
*/
|
|
628
|
+
headerLayout = computed(() => {
|
|
629
|
+
const groups = this.columnGroups();
|
|
630
|
+
const visible = this.visibleColumns();
|
|
631
|
+
if (groups.length === 0)
|
|
632
|
+
return null;
|
|
633
|
+
const groupedKeys = new Set(groups.flatMap((g) => g.columns));
|
|
634
|
+
// Build group header cells
|
|
635
|
+
const groupCells = [];
|
|
636
|
+
const standaloneKeys = new Set();
|
|
637
|
+
// Track which visible columns belong to which group
|
|
638
|
+
let colIndex = 0;
|
|
639
|
+
while (colIndex < visible.length) {
|
|
640
|
+
const col = visible[colIndex];
|
|
641
|
+
const group = groups.find((g) => g.columns.includes(col.key));
|
|
642
|
+
if (group) {
|
|
643
|
+
// Count how many consecutive visible columns belong to this group
|
|
644
|
+
const span = visible.filter((c) => group.columns.includes(c.key)).length;
|
|
645
|
+
groupCells.push({ type: 'group', group, colspan: span });
|
|
646
|
+
// Skip all columns in this group
|
|
647
|
+
colIndex += span;
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
standaloneKeys.add(col.key);
|
|
651
|
+
colIndex++;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return { groupCells, standaloneKeys, groupedKeys };
|
|
655
|
+
}, /* @ts-ignore */
|
|
656
|
+
...(ngDevMode ? [{ debugName: "headerLayout" }] : /* istanbul ignore next */ []));
|
|
657
|
+
// ---- Lifecycle --------------------------------------------------------------
|
|
658
|
+
constructor() {
|
|
659
|
+
// Detach any in-flight column-resize drag listeners if the table is destroyed
|
|
660
|
+
// mid-drag (e.g. a route change while the user is resizing a column) so the dead
|
|
661
|
+
// component's document listeners can't keep firing.
|
|
662
|
+
this._destroyRef.onDestroy(() => this._cleanupResize());
|
|
663
|
+
effect(() => {
|
|
664
|
+
const ds = this.effectiveDataSource();
|
|
665
|
+
ds.setSort(this.currentSort());
|
|
666
|
+
});
|
|
667
|
+
effect(() => {
|
|
668
|
+
const ds = this.effectiveDataSource();
|
|
669
|
+
const filters = this.activeFilters();
|
|
670
|
+
ds.setFilters(filters);
|
|
671
|
+
untracked(() => {
|
|
672
|
+
if (this.currentPage() !== 1)
|
|
673
|
+
this.currentPage.set(1);
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
effect(() => {
|
|
677
|
+
const ds = this.effectiveDataSource();
|
|
678
|
+
const query = this.searchQuery();
|
|
679
|
+
const keys = this.searchableColumnKeys();
|
|
680
|
+
ds.setSearch(query.trim() && keys.length ? { query, keys } : null);
|
|
681
|
+
untracked(() => {
|
|
682
|
+
if (this._searchDraft() !== query)
|
|
683
|
+
this._searchDraft.set(query);
|
|
684
|
+
if (this.currentPage() !== 1)
|
|
685
|
+
this.currentPage.set(1);
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
effect(() => {
|
|
689
|
+
const ds = this.effectiveDataSource();
|
|
690
|
+
// In infinite or virtual-scroll mode the consumer hands over the entire
|
|
691
|
+
// row set up front, so the internal data source must stay on page 1 at
|
|
692
|
+
// all times — pagination is disabled in both modes.
|
|
693
|
+
const unpaged = this.paginationMode() === 'infinite' || this.virtualScroll();
|
|
694
|
+
ds.setPage(unpaged ? 1 : this.currentPage());
|
|
695
|
+
});
|
|
696
|
+
effect(() => {
|
|
697
|
+
const ds = this.effectiveDataSource();
|
|
698
|
+
// In infinite or virtual-scroll mode disable internal per-page slicing
|
|
699
|
+
// so every row the consumer has provided gets rendered. Virtual scroll
|
|
700
|
+
// then renders only the visible slice of that full list via CDK.
|
|
701
|
+
const unpaged = this.paginationMode() === 'infinite' || this.virtualScroll();
|
|
702
|
+
ds.setPerPage(unpaged ? Number.MAX_SAFE_INTEGER : this.currentPerPage());
|
|
703
|
+
});
|
|
704
|
+
// Keep the roving row index in range as the visible rows change
|
|
705
|
+
// (filtering, sorting, paging, tree expand/collapse, data replacement).
|
|
706
|
+
effect(() => {
|
|
707
|
+
const count = this.flatRows().length;
|
|
708
|
+
const current = untracked(this._focusedRowIndex);
|
|
709
|
+
if (count === 0) {
|
|
710
|
+
if (current !== 0)
|
|
711
|
+
this._focusedRowIndex.set(0);
|
|
712
|
+
}
|
|
713
|
+
else if (current > count - 1) {
|
|
714
|
+
this._focusedRowIndex.set(count - 1);
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
effect(() => {
|
|
718
|
+
const handles = this._resizeHandleRefs();
|
|
719
|
+
if (!handles.length)
|
|
720
|
+
return;
|
|
721
|
+
afterNextRender(() => this._measureHeaderWidths(), {
|
|
722
|
+
injector: this._injector,
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
effect((onCleanup) => {
|
|
726
|
+
if (!this.virtualScroll())
|
|
727
|
+
return;
|
|
728
|
+
const wrapper = this.tableWrapperRef()?.nativeElement;
|
|
729
|
+
const viewport = this._virtualViewportRef()?.elementRef.nativeElement;
|
|
730
|
+
if (!wrapper || !viewport)
|
|
731
|
+
return;
|
|
732
|
+
let synchronizing = false;
|
|
733
|
+
const measureVirtualGeometry = () => {
|
|
734
|
+
this._updateVirtualScrollGeometry(wrapper, viewport);
|
|
735
|
+
};
|
|
736
|
+
const syncFromWrapper = () => {
|
|
737
|
+
if (synchronizing || viewport.scrollLeft === wrapper.scrollLeft)
|
|
738
|
+
return;
|
|
739
|
+
synchronizing = true;
|
|
740
|
+
try {
|
|
741
|
+
viewport.scrollLeft = wrapper.scrollLeft;
|
|
742
|
+
}
|
|
743
|
+
finally {
|
|
744
|
+
synchronizing = false;
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
const syncFromViewport = () => {
|
|
748
|
+
if (synchronizing || wrapper.scrollLeft === viewport.scrollLeft)
|
|
749
|
+
return;
|
|
750
|
+
synchronizing = true;
|
|
751
|
+
try {
|
|
752
|
+
wrapper.scrollLeft = viewport.scrollLeft;
|
|
753
|
+
const acceptedScrollLeft = wrapper.scrollLeft;
|
|
754
|
+
if (viewport.scrollLeft !== acceptedScrollLeft) {
|
|
755
|
+
viewport.scrollLeft = acceptedScrollLeft;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
finally {
|
|
759
|
+
synchronizing = false;
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
this._ngZone.runOutsideAngular(() => {
|
|
763
|
+
wrapper.addEventListener('scroll', syncFromWrapper, {
|
|
764
|
+
passive: true,
|
|
765
|
+
});
|
|
766
|
+
viewport.addEventListener('scroll', syncFromViewport, {
|
|
767
|
+
passive: true,
|
|
768
|
+
});
|
|
769
|
+
measureVirtualGeometry();
|
|
770
|
+
syncFromWrapper();
|
|
771
|
+
});
|
|
772
|
+
const viewportResize = this._resizeService
|
|
773
|
+
.observe(viewport)
|
|
774
|
+
.subscribe(() => {
|
|
775
|
+
measureVirtualGeometry();
|
|
776
|
+
syncFromWrapper();
|
|
777
|
+
});
|
|
778
|
+
onCleanup(() => {
|
|
779
|
+
wrapper.removeEventListener('scroll', syncFromWrapper);
|
|
780
|
+
viewport.removeEventListener('scroll', syncFromViewport);
|
|
781
|
+
viewportResize.unsubscribe();
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
effect(() => {
|
|
785
|
+
if (!this.virtualScroll())
|
|
786
|
+
return;
|
|
787
|
+
this.resizedTableWidth();
|
|
788
|
+
this.visibleColumns();
|
|
789
|
+
this._virtualScrollbarGutter();
|
|
790
|
+
afterNextRender(() => this._syncVirtualHorizontalScroll(), {
|
|
791
|
+
injector: this._injector,
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
afterNextRender(() => {
|
|
795
|
+
const el = this._el.nativeElement;
|
|
796
|
+
this._resizeService
|
|
797
|
+
.observe(el)
|
|
798
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
799
|
+
.subscribe((entries) => {
|
|
800
|
+
const width = entries[0]?.contentRect?.width ?? el.offsetWidth;
|
|
801
|
+
this.containerWidth.set(width);
|
|
802
|
+
afterNextRender(() => this._measureHeaderWidths(), {
|
|
803
|
+
injector: this._injector,
|
|
804
|
+
});
|
|
805
|
+
});
|
|
806
|
+
this.containerWidth.set(el.offsetWidth);
|
|
807
|
+
this._measureHeaderWidths();
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
// ---- Template helpers -------------------------------------------------------
|
|
811
|
+
/** Resolves the custom display template for a column key (if any). */
|
|
812
|
+
getTemplate(key) {
|
|
813
|
+
return this._cellTemplateMap().get(key) ?? null;
|
|
814
|
+
}
|
|
815
|
+
getNoDataTemplate() {
|
|
816
|
+
const list = this.noDataTemplate();
|
|
817
|
+
return list.length > 0 ? list[0].templateRef : null;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* `ngTemplateOutletContext` for a data cell's custom/edit template. Returns a
|
|
821
|
+
* *stable* per-row object so the outlet's context does not churn (fresh object +
|
|
822
|
+
* `NgTemplateOutlet.ngOnChanges`) every change-detection cycle. `flatRows()` rebuilds
|
|
823
|
+
* its row objects on every data / sort / filter / expand change, so a cached entry's
|
|
824
|
+
* `index` always matches that row's current position and stale entries (whose row
|
|
825
|
+
* objects are no longer referenced) are garbage-collected.
|
|
826
|
+
*/
|
|
827
|
+
getCellContext(row, index) {
|
|
828
|
+
let ctx = this._cellContextCache.get(row);
|
|
829
|
+
if (!ctx) {
|
|
830
|
+
ctx = { $implicit: row, row, index };
|
|
831
|
+
this._cellContextCache.set(row, ctx);
|
|
832
|
+
}
|
|
833
|
+
return ctx;
|
|
834
|
+
}
|
|
835
|
+
getCellValue(row, key) {
|
|
836
|
+
return row[key];
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Value rendered by the **default** cell renderer. Maps the raw value through
|
|
840
|
+
* the column's `valueLabels` (or its `filterConfig.options` fallback) when a
|
|
841
|
+
* label exists; otherwise returns the raw value unchanged. Custom
|
|
842
|
+
* `mlvDataTableCell` templates bypass this entirely.
|
|
843
|
+
*/
|
|
844
|
+
getCellDisplayValue(row, col) {
|
|
845
|
+
const raw = this.getCellValue(row, col.key);
|
|
846
|
+
if (raw === null || raw === undefined)
|
|
847
|
+
return raw;
|
|
848
|
+
const labels = this._columnValueLabels().get(col.key);
|
|
849
|
+
if (!labels)
|
|
850
|
+
return raw;
|
|
851
|
+
return labels.get(String(raw)) ?? raw;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Resolves the semantic tone for one default-rendered cell. Returns `null`
|
|
855
|
+
* when the column declares no `tone` (or its resolver opted this row out), in
|
|
856
|
+
* which case the cell renders as plain text instead of a badge.
|
|
857
|
+
*/
|
|
858
|
+
getCellTone(row, col) {
|
|
859
|
+
const tone = col.tone;
|
|
860
|
+
if (!tone)
|
|
861
|
+
return null;
|
|
862
|
+
if (typeof tone === 'function') {
|
|
863
|
+
return tone(row) ?? null;
|
|
864
|
+
}
|
|
865
|
+
return tone;
|
|
866
|
+
}
|
|
867
|
+
/** Retrieves the projected error template, if the consumer supplied one. */
|
|
868
|
+
getErrorTemplate() {
|
|
869
|
+
const list = this.errorTemplate();
|
|
870
|
+
return list.length > 0 ? list[0].templateRef : null;
|
|
871
|
+
}
|
|
872
|
+
/** Emits `retry` so the consumer can refetch the failed request. */
|
|
873
|
+
onRetry() {
|
|
874
|
+
this.retry.emit();
|
|
875
|
+
}
|
|
876
|
+
/** Resolves the edit-mode template for a column key (if any). */
|
|
877
|
+
getEditTemplate(key) {
|
|
878
|
+
return this._editCellTemplateMap().get(key) ?? null;
|
|
879
|
+
}
|
|
880
|
+
/** Whether a given row index is currently being edited. */
|
|
881
|
+
isEditing(index) {
|
|
882
|
+
return this._editing.isEditing(index);
|
|
883
|
+
}
|
|
884
|
+
/** Enter edit mode for a row. Clones the row for rollback. */
|
|
885
|
+
startEdit(row, index) {
|
|
886
|
+
this._editing.start(row, index);
|
|
887
|
+
this.rowEditStart.emit({ row, index });
|
|
888
|
+
}
|
|
889
|
+
/** Confirm edits for a row. Emits save event with original and modified row. */
|
|
890
|
+
saveEdit(row, index) {
|
|
891
|
+
const originalRow = this._editing.save(row, index);
|
|
892
|
+
this.rowEditSave.emit({ row, index, originalRow });
|
|
893
|
+
}
|
|
894
|
+
/** Cancel editing. Emits cancel event. */
|
|
895
|
+
cancelEdit(row, index) {
|
|
896
|
+
this._editing.cancel(index);
|
|
897
|
+
this.rowEditCancel.emit({ row, index });
|
|
898
|
+
}
|
|
899
|
+
/** Emit add-row event. Consumer is responsible for appending the new row. */
|
|
900
|
+
onAddRow() {
|
|
901
|
+
this.rowAdd.emit();
|
|
902
|
+
}
|
|
903
|
+
// ---- Row click ---------------------------------------------------------------
|
|
904
|
+
/** Handle click on a data row. */
|
|
905
|
+
onRowClick(row, index, event) {
|
|
906
|
+
this._focusedRowIndex.set(index);
|
|
907
|
+
this.rowClick.emit({ row, index, event });
|
|
908
|
+
}
|
|
909
|
+
// ---- Row keyboard navigation (grid row-focus model) --------------------------
|
|
910
|
+
/**
|
|
911
|
+
* @protected Roving tabindex for a data row: `0` for the focused row, `-1`
|
|
912
|
+
* otherwise, so the row grid exposes a single tab stop.
|
|
913
|
+
*/
|
|
914
|
+
_rowTabIndex(index) {
|
|
915
|
+
return index === this._focusedRowIndex() ? 0 : -1;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* @protected Sync the roving row index when a row receives focus (via Tab or
|
|
919
|
+
* a click that focuses the `<tr>`).
|
|
920
|
+
*/
|
|
921
|
+
_onRowFocus(index) {
|
|
922
|
+
this._focusedRowIndex.set(index);
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* @protected Row-level keyboard navigation for the `role="grid"` table.
|
|
926
|
+
* Active only when the `<tr>` itself holds focus — keys originating from an
|
|
927
|
+
* interactive cell widget (checkbox, button, edit input) are left alone.
|
|
928
|
+
*
|
|
929
|
+
* - `ArrowUp` / `ArrowDown` — move row focus by one (clamped)
|
|
930
|
+
* - `Home` / `End` — jump to the first / last row
|
|
931
|
+
* - `Space` / `Enter` — toggle selection of the focused row when selectable
|
|
932
|
+
*/
|
|
933
|
+
_onRowKeydown(index, row, event) {
|
|
934
|
+
if (event.target !== event.currentTarget)
|
|
935
|
+
return;
|
|
936
|
+
const lastIndex = this.flatRows().length - 1;
|
|
937
|
+
switch (event.key) {
|
|
938
|
+
case 'ArrowDown':
|
|
939
|
+
event.preventDefault();
|
|
940
|
+
this._moveRowFocus(Math.min(index + 1, lastIndex));
|
|
941
|
+
break;
|
|
942
|
+
case 'ArrowUp':
|
|
943
|
+
event.preventDefault();
|
|
944
|
+
this._moveRowFocus(Math.max(index - 1, 0));
|
|
945
|
+
break;
|
|
946
|
+
case 'Home':
|
|
947
|
+
event.preventDefault();
|
|
948
|
+
this._moveRowFocus(0);
|
|
949
|
+
break;
|
|
950
|
+
case 'End':
|
|
951
|
+
event.preventDefault();
|
|
952
|
+
this._moveRowFocus(lastIndex);
|
|
953
|
+
break;
|
|
954
|
+
case ' ':
|
|
955
|
+
case 'Enter':
|
|
956
|
+
if (this.selectable()) {
|
|
957
|
+
event.preventDefault();
|
|
958
|
+
this.toggleRowSelection(row);
|
|
959
|
+
}
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* @private Move the roving tabindex and DOM focus to the row at `targetIndex`.
|
|
965
|
+
* In virtual-scroll mode the target may be recycled out of the DOM, so it is
|
|
966
|
+
* scrolled into view first and focused after the next render.
|
|
967
|
+
*/
|
|
968
|
+
_moveRowFocus(targetIndex) {
|
|
969
|
+
if (targetIndex < 0)
|
|
970
|
+
return;
|
|
971
|
+
this._focusedRowIndex.set(targetIndex);
|
|
972
|
+
if (this.virtualScroll()) {
|
|
973
|
+
this._virtualViewportRef()?.scrollToIndex(targetIndex, 'smooth');
|
|
974
|
+
}
|
|
975
|
+
afterNextRender(() => this._focusRowElement(targetIndex), {
|
|
976
|
+
injector: this._injector,
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
/** @private Focus the rendered `<tr>` for the given flat-rows index, if present. */
|
|
980
|
+
_focusRowElement(index) {
|
|
981
|
+
const host = this._el.nativeElement;
|
|
982
|
+
const rowEl = host.querySelector(`[data-row-index="${index}"]`);
|
|
983
|
+
rowEl?.focus();
|
|
984
|
+
}
|
|
985
|
+
// ---- Infinite scroll ---------------------------------------------------------
|
|
986
|
+
/**
|
|
987
|
+
* Handler wired to `MlvInfiniteScroll.loadMore` when `paginationMode === 'infinite'`.
|
|
988
|
+
* Increments the tracked page counter and emits a table-level `MlvLoadMoreEvent`
|
|
989
|
+
* whose `page` field tells the consumer which page to fetch next.
|
|
990
|
+
*/
|
|
991
|
+
onInfiniteScrollLoadMore(trigger) {
|
|
992
|
+
const nextPage = this.currentPage() + 1;
|
|
993
|
+
this.currentPage.set(nextPage);
|
|
994
|
+
this.loadMore.emit({
|
|
995
|
+
page: nextPage,
|
|
996
|
+
perPage: this.currentPerPage(),
|
|
997
|
+
distance: trigger.distance,
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
// ---- Row selection -----------------------------------------------------------
|
|
1001
|
+
/** Whether a specific row is currently selected. */
|
|
1002
|
+
isRowSelected(row) {
|
|
1003
|
+
return this.selectedRows().has(this._ref(row));
|
|
1004
|
+
}
|
|
1005
|
+
/** Toggle selection for a single row. */
|
|
1006
|
+
toggleRowSelection(row) {
|
|
1007
|
+
const ref = this._ref(row);
|
|
1008
|
+
const current = this.selectedRows();
|
|
1009
|
+
const next = new Set(current);
|
|
1010
|
+
const mode = this.selectable();
|
|
1011
|
+
if (mode === 'single') {
|
|
1012
|
+
if (next.has(ref)) {
|
|
1013
|
+
next.clear();
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
next.clear();
|
|
1017
|
+
next.add(ref);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
else if (mode === 'multi') {
|
|
1021
|
+
if (next.has(ref)) {
|
|
1022
|
+
next.delete(ref);
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
next.add(ref);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
this.selectedRows.set(next);
|
|
1029
|
+
this.selectionChange.emit({ selectedRows: next, row: ref });
|
|
1030
|
+
}
|
|
1031
|
+
/** Toggle select-all / deselect-all. */
|
|
1032
|
+
toggleSelectAll() {
|
|
1033
|
+
const rows = this.flatRows();
|
|
1034
|
+
const allSelected = this.allRowsSelected();
|
|
1035
|
+
const next = new Set();
|
|
1036
|
+
if (!allSelected) {
|
|
1037
|
+
for (const row of rows) {
|
|
1038
|
+
next.add(this._ref(row));
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
this.selectedRows.set(next);
|
|
1042
|
+
this.selectionChange.emit({ selectedRows: next });
|
|
1043
|
+
}
|
|
1044
|
+
// ---- Column visibility -------------------------------------------------------
|
|
1045
|
+
/** Whether a column is currently hidden by the user. */
|
|
1046
|
+
isColumnHidden(key) {
|
|
1047
|
+
return this._visibility.isHidden(key);
|
|
1048
|
+
}
|
|
1049
|
+
/** Toggle visibility of a hideable column. */
|
|
1050
|
+
toggleColumnVisibility(key) {
|
|
1051
|
+
this._visibility.toggle(key);
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* @protected Returns focus to the toolbar "Filters" trigger when the filter
|
|
1055
|
+
* dialog popup closes (Apply / Clear / Escape / backdrop), so keyboard focus
|
|
1056
|
+
* is never stranded on a destroyed overlay control.
|
|
1057
|
+
*/
|
|
1058
|
+
_onFiltersClosed() {
|
|
1059
|
+
this._filterTriggerRef()?.nativeElement.focus();
|
|
1060
|
+
}
|
|
1061
|
+
/** Commits a search-field event and resets table pagination. */
|
|
1062
|
+
onSearch(query) {
|
|
1063
|
+
this.searchQuery.set(query);
|
|
1064
|
+
this.currentPage.set(1);
|
|
1065
|
+
}
|
|
1066
|
+
/** Whether a column currently has a committed filter. */
|
|
1067
|
+
isColumnFiltered(key) {
|
|
1068
|
+
return this.activeFilters().some((filter) => filter.key === key);
|
|
1069
|
+
}
|
|
1070
|
+
/** Accessible label for a column filter trigger. */
|
|
1071
|
+
getFilterLabel(col) {
|
|
1072
|
+
return this._resolver.resolve(this._i18n(), this.isColumnFiltered(col.key) ? 'filterColumnActive' : 'filterColumn', { column: col.title ?? col.key });
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Merges a scoped editor result against the table's current committed state.
|
|
1076
|
+
* The table remains authoritative here because an overlay may hold an older
|
|
1077
|
+
* input snapshot while another column is committed.
|
|
1078
|
+
*/
|
|
1079
|
+
onColumnFiltersChange(key, scopedFilters) {
|
|
1080
|
+
this.activeFilters.update((current) => [
|
|
1081
|
+
...current.filter((filter) => filter.key !== key),
|
|
1082
|
+
...scopedFilters
|
|
1083
|
+
.filter((filter) => filter.key === key)
|
|
1084
|
+
.map((filter) => ({
|
|
1085
|
+
...filter,
|
|
1086
|
+
value: Array.isArray(filter.value) ? [...filter.value] : filter.value,
|
|
1087
|
+
})),
|
|
1088
|
+
]);
|
|
1089
|
+
}
|
|
1090
|
+
/** Single-column list consumed by the scoped filter editor. */
|
|
1091
|
+
getColumnFilterColumns(col) {
|
|
1092
|
+
const cached = this._columnFilterLists.get(col.key);
|
|
1093
|
+
if (cached?.[0] === col)
|
|
1094
|
+
return cached;
|
|
1095
|
+
const next = [col];
|
|
1096
|
+
this._columnFilterLists.set(col.key, next);
|
|
1097
|
+
return next;
|
|
1098
|
+
}
|
|
1099
|
+
/** Restores focus to the header filter trigger for a popup that just closed. */
|
|
1100
|
+
focusColumnFilterTrigger(key) {
|
|
1101
|
+
const host = this._el.nativeElement;
|
|
1102
|
+
const triggers = host.querySelectorAll('.mlv-data-table__column-filter');
|
|
1103
|
+
for (const trigger of triggers) {
|
|
1104
|
+
if (trigger.dataset['columnKey'] === key) {
|
|
1105
|
+
trigger.focus();
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
// ---- Footer template ----------------------------------------------------------
|
|
1111
|
+
/** Retrieves the footer summary template. */
|
|
1112
|
+
getFooterTemplate() {
|
|
1113
|
+
const list = this.footerTemplate();
|
|
1114
|
+
return list.length > 0 ? list[0].templateRef : null;
|
|
1115
|
+
}
|
|
1116
|
+
// ---- Pin/Unpin ---------------------------------------------------------------
|
|
1117
|
+
/**
|
|
1118
|
+
* Returns the list of sides the user may pin a column to, based on `col.pinnable`.
|
|
1119
|
+
* - `true` → `['left', 'right']`
|
|
1120
|
+
* - `'left'` / `'right'` → single-element list
|
|
1121
|
+
* - `MlvPinSide[]` → the list as provided (duplicates preserved by caller)
|
|
1122
|
+
* - falsy → `[]` (not user-pinnable)
|
|
1123
|
+
*/
|
|
1124
|
+
getPinnableSides(col) {
|
|
1125
|
+
return this._pinning.getPinnableSides(col);
|
|
1126
|
+
}
|
|
1127
|
+
/** Pin a column to the given side, or unpin if it is already pinned. */
|
|
1128
|
+
togglePin(col, side = 'left') {
|
|
1129
|
+
this._pinning.togglePin(col, side);
|
|
1130
|
+
}
|
|
1131
|
+
/** Pin a column to a specific side, overriding any current pin state. */
|
|
1132
|
+
pinTo(col, side) {
|
|
1133
|
+
this._pinning.pinTo(col, side);
|
|
1134
|
+
}
|
|
1135
|
+
/** Explicitly unpin a column, regardless of its current side. */
|
|
1136
|
+
unpin(col) {
|
|
1137
|
+
this._pinning.unpin(col);
|
|
1138
|
+
}
|
|
1139
|
+
/** Whether a column is currently pinned (accounting for overrides). */
|
|
1140
|
+
isColumnPinned(col) {
|
|
1141
|
+
return this._pinning.isPinned(col);
|
|
1142
|
+
}
|
|
1143
|
+
/** Returns the current pin side for a column (considering overrides), or `null` if it is not pinned. */
|
|
1144
|
+
getColumnPinSide(col) {
|
|
1145
|
+
return this._pinning.getPinSide(col);
|
|
1146
|
+
}
|
|
1147
|
+
isRowExpanded(row) {
|
|
1148
|
+
return this._expanded().has(this._ref(row));
|
|
1149
|
+
}
|
|
1150
|
+
hasChildren(row) {
|
|
1151
|
+
const original = this._ref(row);
|
|
1152
|
+
return (Array.isArray(original['_mlvChildren']) &&
|
|
1153
|
+
original['_mlvChildren'].length > 0);
|
|
1154
|
+
}
|
|
1155
|
+
getDepth(row) {
|
|
1156
|
+
return row['_mlvDepth'] ?? 0;
|
|
1157
|
+
}
|
|
1158
|
+
toggleExpand(row, event) {
|
|
1159
|
+
event.stopPropagation();
|
|
1160
|
+
const original = this._ref(row);
|
|
1161
|
+
const current = this._expanded();
|
|
1162
|
+
const next = new Set(current);
|
|
1163
|
+
if (next.has(original)) {
|
|
1164
|
+
next.delete(original);
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
next.add(original);
|
|
1168
|
+
}
|
|
1169
|
+
this._expanded.set(next);
|
|
1170
|
+
}
|
|
1171
|
+
// ---- Sorting ----------------------------------------------------------------
|
|
1172
|
+
onSortClick(col) {
|
|
1173
|
+
if (!col.sortable)
|
|
1174
|
+
return;
|
|
1175
|
+
const current = this.currentSort();
|
|
1176
|
+
if (!current || current.key !== col.key) {
|
|
1177
|
+
this.currentSort.set({ key: col.key, direction: 'asc' });
|
|
1178
|
+
}
|
|
1179
|
+
else if (current.direction === 'asc') {
|
|
1180
|
+
this.currentSort.set({ key: col.key, direction: 'desc' });
|
|
1181
|
+
}
|
|
1182
|
+
else {
|
|
1183
|
+
this.currentSort.set(null);
|
|
1184
|
+
}
|
|
1185
|
+
this.currentPage.set(1);
|
|
1186
|
+
}
|
|
1187
|
+
getSortDirection(col) {
|
|
1188
|
+
const s = this.currentSort();
|
|
1189
|
+
return s && s.key === col.key ? s.direction : null;
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Accessible label for a sortable column header button. Resolves the
|
|
1193
|
+
* `sortByColumn` ICU string ("Sort by {column}") — describing intent rather
|
|
1194
|
+
* than a mouse-specific "click to sort" instruction.
|
|
1195
|
+
*/
|
|
1196
|
+
getSortLabel(col) {
|
|
1197
|
+
return this._resolver.resolve(this._i18n(), 'sortByColumn', { column: col.title ?? col.key });
|
|
1198
|
+
}
|
|
1199
|
+
// ---- Column styles ----------------------------------------------------------
|
|
1200
|
+
getColStyle(col) {
|
|
1201
|
+
return columnWidthStyles(col);
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Returns the value to apply to a `<col>` element's `width` style.
|
|
1205
|
+
* Used by the shared `<colgroup>` template to keep column widths in sync between the
|
|
1206
|
+
* three split tables in virtual scroll mode (header / body / footer).
|
|
1207
|
+
*/
|
|
1208
|
+
getColWidth(col) {
|
|
1209
|
+
if (col._currentWidth !== undefined)
|
|
1210
|
+
return col._currentWidth + 'px';
|
|
1211
|
+
if (col.width)
|
|
1212
|
+
return col.width;
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
/** Exact frozen width styles for a selection or actions utility column. */
|
|
1216
|
+
getAuxiliaryColumnStyle(kind) {
|
|
1217
|
+
return this._auxiliaryColumnStyles()[kind];
|
|
1218
|
+
}
|
|
1219
|
+
/** Exact frozen `<col>` width for a selection or actions utility column. */
|
|
1220
|
+
getAuxiliaryColumnWidth(kind) {
|
|
1221
|
+
return this._auxiliaryColumnStyles()[kind]?.['width'] ?? null;
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* trackBy used by `*cdkVirtualFor` in virtual scroll mode. Falls back to the row index
|
|
1225
|
+
* for plain row references and to `_mlvRef` identity for tree rows so expanded children
|
|
1226
|
+
* are reconciled correctly when the user scrolls.
|
|
1227
|
+
*/
|
|
1228
|
+
trackByRow = (index, row) => {
|
|
1229
|
+
return row['_mlvRef'] ?? index;
|
|
1230
|
+
};
|
|
1231
|
+
/**
|
|
1232
|
+
* Inline `[style]` object (width / min-width, plus sticky offsets for pinned columns)
|
|
1233
|
+
* for a column's header and body cells. Reads the memoized {@link _columnStyles} map
|
|
1234
|
+
* so the returned reference is stable across change-detection cycles (no per-cell
|
|
1235
|
+
* allocation). Every visible column is present in the map; the on-the-fly fallback is
|
|
1236
|
+
* a defensive path for a column outside the current visible set.
|
|
1237
|
+
*/
|
|
1238
|
+
getCellStyle(col) {
|
|
1239
|
+
return (this._columnStyles().get(col.key) ??
|
|
1240
|
+
columnCellStyles(col, this.columnOffsets()));
|
|
1241
|
+
}
|
|
1242
|
+
getAlignClass(col) {
|
|
1243
|
+
return col.align ? `mlv-data-table__cell--align-${col.align}` : '';
|
|
1244
|
+
}
|
|
1245
|
+
// ---- Column resize ----------------------------------------------------------
|
|
1246
|
+
/**
|
|
1247
|
+
* @private In-flight pointer resize state. The originating pointer id prevents
|
|
1248
|
+
* secondary touches or unrelated pointers from moving or ending the drag.
|
|
1249
|
+
*/
|
|
1250
|
+
_resizeState = null;
|
|
1251
|
+
/**
|
|
1252
|
+
* @private rAF handle coalescing live-width signal writes to one per frame
|
|
1253
|
+
* during a pointer drag.
|
|
1254
|
+
*/
|
|
1255
|
+
_resizeRafId = 0;
|
|
1256
|
+
/**
|
|
1257
|
+
* @private Bound pointer-move handler retained so add/removeEventListener use
|
|
1258
|
+
* the same function reference.
|
|
1259
|
+
*/
|
|
1260
|
+
_onResizeMoveBound = (event) => {
|
|
1261
|
+
this._onResizeMove(event);
|
|
1262
|
+
};
|
|
1263
|
+
/**
|
|
1264
|
+
* @private Bound pointer-up handler retained so add/removeEventListener use
|
|
1265
|
+
* the same function reference.
|
|
1266
|
+
*/
|
|
1267
|
+
_onResizeEndBound = (event) => {
|
|
1268
|
+
this._onResizeEnd(event);
|
|
1269
|
+
};
|
|
1270
|
+
/**
|
|
1271
|
+
* @private Bound pointer-cancel handler. Cancellation restores the previously
|
|
1272
|
+
* committed width and never emits a commit event.
|
|
1273
|
+
*/
|
|
1274
|
+
_onResizeCancelBound = (event) => {
|
|
1275
|
+
this._onResizeCancel(event);
|
|
1276
|
+
};
|
|
1277
|
+
/**
|
|
1278
|
+
* @private Cancels an active drag if the browser unexpectedly revokes the
|
|
1279
|
+
* initiating handle's pointer capture.
|
|
1280
|
+
*/
|
|
1281
|
+
_onResizeLostCaptureBound = (event) => {
|
|
1282
|
+
this._onResizeLostCapture(event);
|
|
1283
|
+
};
|
|
1284
|
+
/**
|
|
1285
|
+
* Starts a column resize with Pointer Events and captures the initiating
|
|
1286
|
+
* pointer on the separator. The rendered header width is measured instead of
|
|
1287
|
+
* trusting the declared CSS width, so `rem`, `%`, and flex/table layout widths
|
|
1288
|
+
* begin without a jump.
|
|
1289
|
+
*/
|
|
1290
|
+
onResizeStart(col, event) {
|
|
1291
|
+
if (event.button !== 0 || this._resizeState)
|
|
1292
|
+
return;
|
|
1293
|
+
const target = event.currentTarget;
|
|
1294
|
+
if (!(target instanceof HTMLElement))
|
|
1295
|
+
return;
|
|
1296
|
+
event.preventDefault();
|
|
1297
|
+
this._measureHeaderWidths();
|
|
1298
|
+
const bounds = this._resolveColumnResizeBounds(col, target);
|
|
1299
|
+
this._cacheColumnResizeBounds(col.key, bounds);
|
|
1300
|
+
const startWidth = this._clampColumnWidth(this._measureHeaderWidth(col, target), bounds);
|
|
1301
|
+
this._resizeState = {
|
|
1302
|
+
key: col.key,
|
|
1303
|
+
pointerId: event.pointerId,
|
|
1304
|
+
target,
|
|
1305
|
+
startX: event.clientX,
|
|
1306
|
+
startWidth,
|
|
1307
|
+
latestWidth: startWidth,
|
|
1308
|
+
moved: false,
|
|
1309
|
+
...bounds,
|
|
1310
|
+
};
|
|
1311
|
+
this._ngZone.runOutsideAngular(() => {
|
|
1312
|
+
this._document.addEventListener('pointermove', this._onResizeMoveBound);
|
|
1313
|
+
this._document.addEventListener('pointerup', this._onResizeEndBound);
|
|
1314
|
+
this._document.addEventListener('pointercancel', this._onResizeCancelBound);
|
|
1315
|
+
target.addEventListener('lostpointercapture', this._onResizeLostCaptureBound);
|
|
1316
|
+
if (typeof target.setPointerCapture === 'function') {
|
|
1317
|
+
try {
|
|
1318
|
+
target.setPointerCapture(event.pointerId);
|
|
1319
|
+
}
|
|
1320
|
+
catch {
|
|
1321
|
+
// Document listeners preserve drag/end behavior when capture is
|
|
1322
|
+
// unavailable, rejected, or exercised by a synthetic event.
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Handles keyboard resizing for the focusable separator. ArrowLeft/ArrowRight
|
|
1329
|
+
* use an 8px step (32px with Shift); Home/End jump to the resolved bounds.
|
|
1330
|
+
*/
|
|
1331
|
+
onColumnResizeKeydown(col, event) {
|
|
1332
|
+
if (event.key === 'Enter') {
|
|
1333
|
+
event.preventDefault();
|
|
1334
|
+
this.resetColumnWidth(col.key);
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
const supported = event.key === 'ArrowLeft' ||
|
|
1338
|
+
event.key === 'ArrowRight' ||
|
|
1339
|
+
event.key === 'Home' ||
|
|
1340
|
+
event.key === 'End';
|
|
1341
|
+
if (!supported)
|
|
1342
|
+
return;
|
|
1343
|
+
const target = event.currentTarget instanceof HTMLElement
|
|
1344
|
+
? event.currentTarget
|
|
1345
|
+
: undefined;
|
|
1346
|
+
this._measureHeaderWidths();
|
|
1347
|
+
const bounds = this._resolveColumnResizeBounds(col, target);
|
|
1348
|
+
this._cacheColumnResizeBounds(col.key, bounds);
|
|
1349
|
+
const currentWidth = this._clampColumnWidth(this._measureHeaderWidth(col, target), bounds);
|
|
1350
|
+
const step = event.shiftKey
|
|
1351
|
+
? COLUMN_RESIZE_KEYBOARD_LARGE_STEP
|
|
1352
|
+
: COLUMN_RESIZE_KEYBOARD_STEP;
|
|
1353
|
+
let nextWidth = currentWidth;
|
|
1354
|
+
if (event.key === 'Home')
|
|
1355
|
+
nextWidth = bounds.min;
|
|
1356
|
+
if (event.key === 'End')
|
|
1357
|
+
nextWidth = bounds.max;
|
|
1358
|
+
if (event.key === 'ArrowLeft')
|
|
1359
|
+
nextWidth = currentWidth - step;
|
|
1360
|
+
if (event.key === 'ArrowRight')
|
|
1361
|
+
nextWidth = currentWidth + step;
|
|
1362
|
+
event.preventDefault();
|
|
1363
|
+
this._commitColumnWidth(col.key, this._clampColumnWidth(nextWidth, bounds), 'keyboard');
|
|
1364
|
+
}
|
|
1365
|
+
/** Localized accessible name for a column's resize separator. */
|
|
1366
|
+
getColumnResizeLabel(col) {
|
|
1367
|
+
return this._resolver.resolve(this._i18n(), 'resizeColumn', { column: col.title ?? col.key });
|
|
1368
|
+
}
|
|
1369
|
+
/** Current separator value in CSS pixels for ARIA. */
|
|
1370
|
+
getColumnResizeValue(col) {
|
|
1371
|
+
const bounds = this._columnResizeBounds(col);
|
|
1372
|
+
const active = this._resizeState?.key === col.key
|
|
1373
|
+
? this._resizeState.latestWidth
|
|
1374
|
+
: undefined;
|
|
1375
|
+
const measured = this._measuredColumnWidths().get(col.key);
|
|
1376
|
+
return this._normalizeColumnWidth(active ??
|
|
1377
|
+
col._currentWidth ??
|
|
1378
|
+
measured ??
|
|
1379
|
+
parsePixelWidth(col.width) ??
|
|
1380
|
+
DEFAULT_COLUMN_WIDTH, bounds);
|
|
1381
|
+
}
|
|
1382
|
+
/** Minimum separator value in CSS pixels for ARIA and Home. */
|
|
1383
|
+
getColumnResizeMin(col) {
|
|
1384
|
+
return this._roundColumnWidth(this._columnResizeBounds(col).min);
|
|
1385
|
+
}
|
|
1386
|
+
/** Maximum separator value in CSS pixels for ARIA and End. */
|
|
1387
|
+
getColumnResizeMax(col) {
|
|
1388
|
+
return this._roundColumnWidth(this._columnResizeBounds(col).max);
|
|
1389
|
+
}
|
|
1390
|
+
/** Localized text alternative that makes the separator's pixel unit explicit. */
|
|
1391
|
+
getColumnResizeValueText(col) {
|
|
1392
|
+
return this._resolver.resolve(this._i18n(), 'columnWidthPixels', { width: this.getColumnResizeValue(col) });
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Removes committed user widths. Passing a key resets that column and emits a
|
|
1396
|
+
* reset even when no override exists (useful for clearing persisted state);
|
|
1397
|
+
* omitting it resets every currently overridden column.
|
|
1398
|
+
*/
|
|
1399
|
+
resetColumnWidth(key) {
|
|
1400
|
+
const active = this._resizeState;
|
|
1401
|
+
if (active && (key === undefined || active.key === key)) {
|
|
1402
|
+
this._cleanupResize();
|
|
1403
|
+
this._liveResize.set(null);
|
|
1404
|
+
}
|
|
1405
|
+
const widths = this._columnWidths();
|
|
1406
|
+
if (key !== undefined) {
|
|
1407
|
+
if (!this.columns().some((column) => column.key === key))
|
|
1408
|
+
return;
|
|
1409
|
+
if (widths.has(key)) {
|
|
1410
|
+
const updated = new Map(widths);
|
|
1411
|
+
updated.delete(key);
|
|
1412
|
+
this._columnWidths.set(updated);
|
|
1413
|
+
}
|
|
1414
|
+
this.columnResize.emit({ key, width: null, source: 'reset' });
|
|
1415
|
+
afterNextRender(() => this._measureHeaderWidths(), {
|
|
1416
|
+
injector: this._injector,
|
|
1417
|
+
});
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
const resetKeys = [...widths.keys()];
|
|
1421
|
+
if (!resetKeys.length)
|
|
1422
|
+
return;
|
|
1423
|
+
this._columnWidths.set(new Map());
|
|
1424
|
+
for (const resetKey of resetKeys) {
|
|
1425
|
+
this.columnResize.emit({
|
|
1426
|
+
key: resetKey,
|
|
1427
|
+
width: null,
|
|
1428
|
+
source: 'reset',
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
afterNextRender(() => this._measureHeaderWidths(), {
|
|
1432
|
+
injector: this._injector,
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* @private Pointer-move handler (outside Angular). It synchronously updates
|
|
1437
|
+
* the latest bounded value, then publishes at most one live signal update per
|
|
1438
|
+
* animation frame.
|
|
1439
|
+
*/
|
|
1440
|
+
_onResizeMove(event) {
|
|
1441
|
+
const state = this._resizeState;
|
|
1442
|
+
if (!state || event.pointerId !== state.pointerId)
|
|
1443
|
+
return;
|
|
1444
|
+
const delta = event.clientX - state.startX;
|
|
1445
|
+
if (delta !== 0)
|
|
1446
|
+
state.moved = true;
|
|
1447
|
+
state.latestWidth = this._clampColumnWidth(state.startWidth + delta, state);
|
|
1448
|
+
if (!state.moved)
|
|
1449
|
+
return;
|
|
1450
|
+
if (this._resizeRafId)
|
|
1451
|
+
return;
|
|
1452
|
+
this._resizeRafId = requestAnimationFrame(() => {
|
|
1453
|
+
this._resizeRafId = 0;
|
|
1454
|
+
const s = this._resizeState;
|
|
1455
|
+
if (!s || s.pointerId !== event.pointerId)
|
|
1456
|
+
return;
|
|
1457
|
+
this._ngZone.run(() => this._liveResize.set({ key: s.key, width: s.latestWidth }));
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* @private Pointer-up handler that commits exactly once for the initiating
|
|
1462
|
+
* pointer. A press/release without movement remains a no-op, which keeps a
|
|
1463
|
+
* double-click reset from producing intermediate resize commits.
|
|
1464
|
+
*/
|
|
1465
|
+
_onResizeEnd(event) {
|
|
1466
|
+
const state = this._resizeState;
|
|
1467
|
+
if (!state || event.pointerId !== state.pointerId)
|
|
1468
|
+
return;
|
|
1469
|
+
this._cleanupResize();
|
|
1470
|
+
if (!state.moved) {
|
|
1471
|
+
this._ngZone.run(() => this._liveResize.set(null));
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
this._commitColumnWidth(state.key, state.latestWidth, 'pointer');
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* @private Cancels only the matching active pointer, dropping the optimistic
|
|
1478
|
+
* live width while preserving the last committed map entry.
|
|
1479
|
+
*/
|
|
1480
|
+
_onResizeCancel(event) {
|
|
1481
|
+
const state = this._resizeState;
|
|
1482
|
+
if (!state || event.pointerId !== state.pointerId)
|
|
1483
|
+
return;
|
|
1484
|
+
this._cleanupResize();
|
|
1485
|
+
this._ngZone.run(() => this._liveResize.set(null));
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* @private Treats unexpected capture loss as cancellation. Normal cleanup
|
|
1489
|
+
* removes this listener before explicitly releasing capture, so pointer-up
|
|
1490
|
+
* commits cannot be mistaken for cancellation.
|
|
1491
|
+
*/
|
|
1492
|
+
_onResizeLostCapture(event) {
|
|
1493
|
+
const state = this._resizeState;
|
|
1494
|
+
if (!state || event.pointerId !== state.pointerId)
|
|
1495
|
+
return;
|
|
1496
|
+
this._cleanupResize();
|
|
1497
|
+
this._ngZone.run(() => this._liveResize.set(null));
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* @private Commits a bounded pixel width with one map clone and emits the
|
|
1501
|
+
* public persistence event in the same Angular-zone turn.
|
|
1502
|
+
*/
|
|
1503
|
+
_commitColumnWidth(key, width, source) {
|
|
1504
|
+
const col = this.columns().find((column) => column.key === key);
|
|
1505
|
+
const bounds = col
|
|
1506
|
+
? this._columnResizeBounds(col)
|
|
1507
|
+
: {
|
|
1508
|
+
min: DEFAULT_COLUMN_MIN_WIDTH,
|
|
1509
|
+
max: DEFAULT_COLUMN_MAX_WIDTH,
|
|
1510
|
+
};
|
|
1511
|
+
const committedWidth = this._normalizeColumnWidth(width, bounds);
|
|
1512
|
+
this._ngZone.run(() => {
|
|
1513
|
+
const updated = new Map(this._columnWidths());
|
|
1514
|
+
updated.set(key, committedWidth);
|
|
1515
|
+
this._columnWidths.set(updated);
|
|
1516
|
+
this._liveResize.set(null);
|
|
1517
|
+
this.columnResize.emit({ key, width: committedWidth, source });
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* @private Returns the latest DOM-resolved bounds when available, otherwise
|
|
1522
|
+
* resolves strict pixel metadata with safe finite defaults.
|
|
1523
|
+
*/
|
|
1524
|
+
_columnResizeBounds(col) {
|
|
1525
|
+
return (this._resolvedResizeBounds().get(col.key) ??
|
|
1526
|
+
this._resolveColumnResizeBounds(col));
|
|
1527
|
+
}
|
|
1528
|
+
/** @private Stores DOM-resolved bounds with a signal-safe map replacement. */
|
|
1529
|
+
_cacheColumnResizeBounds(key, bounds) {
|
|
1530
|
+
this._resolvedResizeBounds.update((current) => {
|
|
1531
|
+
const updated = new Map(current);
|
|
1532
|
+
updated.set(key, bounds);
|
|
1533
|
+
return updated;
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* @private Resolves resize bounds. Non-pixel metadata is read back from the
|
|
1538
|
+
* rendered header's computed style where the browser exposes a pixel value.
|
|
1539
|
+
*/
|
|
1540
|
+
_resolveColumnResizeBounds(col, target) {
|
|
1541
|
+
const headerCell = target?.closest('th');
|
|
1542
|
+
let min = this._resolveColumnBound(col.minWidth, headerCell);
|
|
1543
|
+
let max = this._resolveColumnBound(col.maxWidth, headerCell);
|
|
1544
|
+
if (headerCell &&
|
|
1545
|
+
((col.minWidth && min === undefined) ||
|
|
1546
|
+
(col.maxWidth && max === undefined))) {
|
|
1547
|
+
const computedStyle = headerCell.ownerDocument.defaultView?.getComputedStyle(headerCell);
|
|
1548
|
+
if (computedStyle && col.minWidth && min === undefined) {
|
|
1549
|
+
min = parsePixelWidth(computedStyle.minWidth);
|
|
1550
|
+
}
|
|
1551
|
+
if (computedStyle && col.maxWidth && max === undefined) {
|
|
1552
|
+
max = parsePixelWidth(computedStyle.maxWidth);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
const resolvedMin = this._roundColumnWidth(min ?? DEFAULT_COLUMN_MIN_WIDTH);
|
|
1556
|
+
const resolvedMax = this._roundColumnWidth(Math.max(resolvedMin, max ?? DEFAULT_COLUMN_MAX_WIDTH));
|
|
1557
|
+
return { min: resolvedMin, max: resolvedMax };
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* @private Resolves a declared bound to CSS pixels. Complete pixel values are
|
|
1561
|
+
* direct; complete percentages use the rendered header table width, matching
|
|
1562
|
+
* the containing table against which cell percentages are laid out.
|
|
1563
|
+
*/
|
|
1564
|
+
_resolveColumnBound(value, headerCell) {
|
|
1565
|
+
const pixels = parsePixelWidth(value);
|
|
1566
|
+
if (pixels !== undefined)
|
|
1567
|
+
return pixels;
|
|
1568
|
+
const percentage = this._parseCssPercentage(value);
|
|
1569
|
+
if (percentage === undefined || !headerCell)
|
|
1570
|
+
return undefined;
|
|
1571
|
+
const table = headerCell.closest('table');
|
|
1572
|
+
const tableRectWidth = table?.getBoundingClientRect().width ?? 0;
|
|
1573
|
+
const referenceWidth = tableRectWidth > 0 ? tableRectWidth : (table?.clientWidth ?? 0);
|
|
1574
|
+
if (!Number.isFinite(referenceWidth) || referenceWidth <= 0) {
|
|
1575
|
+
return undefined;
|
|
1576
|
+
}
|
|
1577
|
+
return this._roundColumnWidth(referenceWidth * percentage);
|
|
1578
|
+
}
|
|
1579
|
+
/** @private Parses a complete, non-negative CSS percentage as a fraction. */
|
|
1580
|
+
_parseCssPercentage(value) {
|
|
1581
|
+
if (!value)
|
|
1582
|
+
return undefined;
|
|
1583
|
+
const match = value.trim().match(/^(?:\d+(?:\.\d+)?|\.\d+)%$/);
|
|
1584
|
+
if (!match)
|
|
1585
|
+
return undefined;
|
|
1586
|
+
const parsed = Number.parseFloat(match[0]);
|
|
1587
|
+
return Number.isFinite(parsed) ? parsed / 100 : undefined;
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* @private Re-applies outer horizontal position after virtual DOM/geometry
|
|
1591
|
+
* changes that do not themselves emit another wrapper scroll event.
|
|
1592
|
+
*/
|
|
1593
|
+
_syncVirtualHorizontalScroll() {
|
|
1594
|
+
const wrapper = this.tableWrapperRef()?.nativeElement;
|
|
1595
|
+
const viewport = this._virtualViewportRef()?.elementRef.nativeElement;
|
|
1596
|
+
if (!wrapper || !viewport)
|
|
1597
|
+
return;
|
|
1598
|
+
if (viewport.scrollLeft !== wrapper.scrollLeft) {
|
|
1599
|
+
viewport.scrollLeft = wrapper.scrollLeft;
|
|
1600
|
+
}
|
|
1601
|
+
this._updateVirtualScrollGeometry(wrapper, viewport);
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* @private Publishes both the native vertical-scrollbar width and an inert
|
|
1605
|
+
* outer extent whose maximum scroll position exactly matches the viewport.
|
|
1606
|
+
*/
|
|
1607
|
+
_updateVirtualScrollGeometry(wrapper, viewport) {
|
|
1608
|
+
const gutter = Math.max(0, viewport.offsetWidth - viewport.clientWidth);
|
|
1609
|
+
const horizontalRange = Math.max(0, viewport.scrollWidth - viewport.clientWidth);
|
|
1610
|
+
const extent = wrapper.clientWidth + horizontalRange;
|
|
1611
|
+
const gutterChanged = gutter !== untracked(this._virtualScrollbarGutter);
|
|
1612
|
+
const extentChanged = extent !== untracked(this._virtualHorizontalScrollExtent);
|
|
1613
|
+
if (!gutterChanged && !extentChanged)
|
|
1614
|
+
return;
|
|
1615
|
+
this._ngZone.run(() => {
|
|
1616
|
+
if (gutterChanged)
|
|
1617
|
+
this._virtualScrollbarGutter.set(gutter);
|
|
1618
|
+
if (extentChanged)
|
|
1619
|
+
this._virtualHorizontalScrollExtent.set(extent);
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* @private Measures the actual rendered header cell width and falls back to
|
|
1624
|
+
* the current strict-pixel state only in non-layout environments such as
|
|
1625
|
+
* server rendering and jsdom.
|
|
1626
|
+
*/
|
|
1627
|
+
_measureHeaderWidth(col, target) {
|
|
1628
|
+
const headerCell = target?.closest('th');
|
|
1629
|
+
const rectWidth = headerCell?.getBoundingClientRect().width ?? 0;
|
|
1630
|
+
if (Number.isFinite(rectWidth) && rectWidth > 0)
|
|
1631
|
+
return rectWidth;
|
|
1632
|
+
const offsetWidth = headerCell?.offsetWidth ?? 0;
|
|
1633
|
+
if (offsetWidth > 0)
|
|
1634
|
+
return offsetWidth;
|
|
1635
|
+
return (col._currentWidth ?? parsePixelWidth(col.width) ?? DEFAULT_COLUMN_WIDTH);
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* @private Measures the rendered header once per layout change. Before the
|
|
1639
|
+
* first user override these values track responsive/table layout; while the
|
|
1640
|
+
* table is frozen, existing baselines are preserved so reset restores the
|
|
1641
|
+
* original widths rather than another column's resized layout.
|
|
1642
|
+
*/
|
|
1643
|
+
_measureHeaderWidths() {
|
|
1644
|
+
const host = this._el.nativeElement;
|
|
1645
|
+
const header = host.querySelector('thead');
|
|
1646
|
+
if (!header)
|
|
1647
|
+
return;
|
|
1648
|
+
const preserveBaseline = this._liveResize() !== null || this._columnWidths().size > 0;
|
|
1649
|
+
const measured = preserveBaseline
|
|
1650
|
+
? new Map(this._measuredColumnWidths())
|
|
1651
|
+
: new Map();
|
|
1652
|
+
const resolvedBounds = preserveBaseline
|
|
1653
|
+
? new Map(this._resolvedResizeBounds())
|
|
1654
|
+
: new Map();
|
|
1655
|
+
for (const cell of header.querySelectorAll('th[data-mlv-column-key]')) {
|
|
1656
|
+
const key = cell.getAttribute('data-mlv-column-key');
|
|
1657
|
+
if (!key)
|
|
1658
|
+
continue;
|
|
1659
|
+
const width = cell.getBoundingClientRect().width;
|
|
1660
|
+
if ((!preserveBaseline || !measured.has(key)) &&
|
|
1661
|
+
Number.isFinite(width) &&
|
|
1662
|
+
width > 0) {
|
|
1663
|
+
measured.set(key, this._roundColumnWidth(width));
|
|
1664
|
+
}
|
|
1665
|
+
const column = this.columns().find((candidate) => candidate.key === key);
|
|
1666
|
+
if (column?.resizable) {
|
|
1667
|
+
resolvedBounds.set(key, this._resolveColumnResizeBounds(column, cell));
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
const selectionWidth = header
|
|
1671
|
+
.querySelector('.mlv-data-table__cell--select')
|
|
1672
|
+
?.getBoundingClientRect().width;
|
|
1673
|
+
const actionsWidth = header
|
|
1674
|
+
.querySelector('.mlv-data-table__cell--actions')
|
|
1675
|
+
?.getBoundingClientRect().width;
|
|
1676
|
+
const auxiliary = preserveBaseline
|
|
1677
|
+
? { ...this._measuredAuxiliaryWidths() }
|
|
1678
|
+
: {};
|
|
1679
|
+
if (selectionWidth !== undefined &&
|
|
1680
|
+
Number.isFinite(selectionWidth) &&
|
|
1681
|
+
selectionWidth > 0) {
|
|
1682
|
+
auxiliary.selection = this._roundColumnWidth(selectionWidth);
|
|
1683
|
+
}
|
|
1684
|
+
if (actionsWidth !== undefined &&
|
|
1685
|
+
Number.isFinite(actionsWidth) &&
|
|
1686
|
+
actionsWidth > 0) {
|
|
1687
|
+
auxiliary.actions = this._roundColumnWidth(actionsWidth);
|
|
1688
|
+
}
|
|
1689
|
+
this._measuredColumnWidths.set(measured);
|
|
1690
|
+
this._resolvedResizeBounds.set(resolvedBounds);
|
|
1691
|
+
this._measuredAuxiliaryWidths.set(auxiliary);
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* @private CSS length used by a sticky offset. A rendered measurement is more
|
|
1695
|
+
* accurate than declared CSS under table redistribution; the declaration
|
|
1696
|
+
* remains the pre-render/SSR fallback.
|
|
1697
|
+
*/
|
|
1698
|
+
_columnOffsetWidth(col) {
|
|
1699
|
+
if (col._currentWidth !== undefined)
|
|
1700
|
+
return `${col._currentWidth}px`;
|
|
1701
|
+
const measured = this._measuredColumnWidths().get(col.key);
|
|
1702
|
+
if (measured !== undefined)
|
|
1703
|
+
return `${this._roundColumnWidth(measured)}px`;
|
|
1704
|
+
if (col.width)
|
|
1705
|
+
return col.width;
|
|
1706
|
+
return `${DEFAULT_COLUMN_WIDTH}px`;
|
|
1707
|
+
}
|
|
1708
|
+
/** @private Adds heterogeneous CSS lengths without converting their units. */
|
|
1709
|
+
_sumCssLengths(widths) {
|
|
1710
|
+
if (!widths.length)
|
|
1711
|
+
return '0px';
|
|
1712
|
+
if (widths.length === 1)
|
|
1713
|
+
return widths[0];
|
|
1714
|
+
return `calc(${widths.join(' + ')})`;
|
|
1715
|
+
}
|
|
1716
|
+
/** @private Rounds pixel values to hundredths without changing their bounds. */
|
|
1717
|
+
_roundColumnWidth(width) {
|
|
1718
|
+
return Math.round(width * 100) / 100;
|
|
1719
|
+
}
|
|
1720
|
+
/** @private Creates immutable exact width/min/max styles for a measured column. */
|
|
1721
|
+
_fixedColumnWidthStyles(width) {
|
|
1722
|
+
if (width === undefined || !Number.isFinite(width) || width < 0)
|
|
1723
|
+
return null;
|
|
1724
|
+
const value = `${this._roundColumnWidth(width)}px`;
|
|
1725
|
+
return {
|
|
1726
|
+
width: value,
|
|
1727
|
+
'min-width': value,
|
|
1728
|
+
'max-width': value,
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
/** @private Rounds and clamps a width against normalized finite bounds. */
|
|
1732
|
+
_normalizeColumnWidth(width, bounds) {
|
|
1733
|
+
return this._clampColumnWidth(this._roundColumnWidth(width), bounds);
|
|
1734
|
+
}
|
|
1735
|
+
/** @private Clamps a width to finite normalized resize bounds. */
|
|
1736
|
+
_clampColumnWidth(width, bounds) {
|
|
1737
|
+
const finiteWidth = Number.isFinite(width) ? width : DEFAULT_COLUMN_WIDTH;
|
|
1738
|
+
return Math.min(bounds.max, Math.max(bounds.min, finiteWidth));
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* @private Detaches active pointer listeners, releases capture, cancels a
|
|
1742
|
+
* pending animation frame, and clears the drag state. Idempotent for pointer
|
|
1743
|
+
* up, pointer cancel, reset, and component destruction.
|
|
1744
|
+
*/
|
|
1745
|
+
_cleanupResize() {
|
|
1746
|
+
const state = this._resizeState;
|
|
1747
|
+
if (state) {
|
|
1748
|
+
this._document.removeEventListener('pointermove', this._onResizeMoveBound);
|
|
1749
|
+
this._document.removeEventListener('pointerup', this._onResizeEndBound);
|
|
1750
|
+
this._document.removeEventListener('pointercancel', this._onResizeCancelBound);
|
|
1751
|
+
state.target.removeEventListener('lostpointercapture', this._onResizeLostCaptureBound);
|
|
1752
|
+
const canRelease = typeof state.target.hasPointerCapture !== 'function' ||
|
|
1753
|
+
state.target.hasPointerCapture(state.pointerId);
|
|
1754
|
+
if (canRelease &&
|
|
1755
|
+
typeof state.target.releasePointerCapture === 'function') {
|
|
1756
|
+
try {
|
|
1757
|
+
state.target.releasePointerCapture(state.pointerId);
|
|
1758
|
+
}
|
|
1759
|
+
catch {
|
|
1760
|
+
// Capture may already have been released implicitly by the browser.
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
if (this._resizeRafId) {
|
|
1765
|
+
cancelAnimationFrame(this._resizeRafId);
|
|
1766
|
+
this._resizeRafId = 0;
|
|
1767
|
+
}
|
|
1768
|
+
this._resizeState = null;
|
|
1769
|
+
}
|
|
1770
|
+
// ---- Private helpers --------------------------------------------------------
|
|
1771
|
+
/** Returns the original (pre-spread) row reference for expand tracking. */
|
|
1772
|
+
_ref(row) {
|
|
1773
|
+
return originalRow(row);
|
|
1774
|
+
}
|
|
1775
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvDataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1776
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvDataTable, isStandalone: true, selector: "mlv-data-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, columnGroups: { classPropertyName: "columnGroups", publicName: "columnGroups", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: true, isRequired: false, transformFunction: null }, addRow: { classPropertyName: "addRow", publicName: "addRow", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, actionsHeaderLabel: { classPropertyName: "actionsHeaderLabel", publicName: "actionsHeaderLabel", isSignal: true, isRequired: false, transformFunction: null }, cellNavigation: { classPropertyName: "cellNavigation", publicName: "cellNavigation", isSignal: true, isRequired: false, transformFunction: null }, paginationMode: { classPropertyName: "paginationMode", publicName: "paginationMode", isSignal: true, isRequired: false, transformFunction: null }, hasMore: { classPropertyName: "hasMore", publicName: "hasMore", isSignal: true, isRequired: false, transformFunction: null }, infiniteScrollThreshold: { classPropertyName: "infiniteScrollThreshold", publicName: "infiniteScrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, filterDisplay: { classPropertyName: "filterDisplay", publicName: "filterDisplay", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, searchTrigger: { classPropertyName: "searchTrigger", publicName: "searchTrigger", isSignal: true, isRequired: false, transformFunction: null }, searchDebounce: { classPropertyName: "searchDebounce", publicName: "searchDebounce", isSignal: true, isRequired: false, transformFunction: null }, selectedRows: { classPropertyName: "selectedRows", publicName: "selectedRows", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, activeFilters: { classPropertyName: "activeFilters", publicName: "activeFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowEditStart: "rowEditStart", rowEditSave: "rowEditSave", rowEditCancel: "rowEditCancel", rowAdd: "rowAdd", rowClick: "rowClick", selectionChange: "selectionChange", retry: "retry", columnResize: "columnResize", loadMore: "loadMore", selectedRows: "selectedRowsChange", searchQuery: "searchQueryChange", activeFilters: "activeFiltersChange" }, host: { classAttribute: "mlv-data-table" }, providers: [
|
|
1777
|
+
{ provide: MLV_DENSITY_ELEMENT, useValue: 'data-table' },
|
|
1778
|
+
MlvDataTableEditingService,
|
|
1779
|
+
MlvDataTableColumnVisibilityService,
|
|
1780
|
+
MlvDataTablePinningService,
|
|
1781
|
+
], queries: [{ propertyName: "cellTemplates", predicate: MlvDataTableCell, descendants: true, isSignal: true }, { propertyName: "editCellTemplates", predicate: MlvDataTableEditCell, descendants: true, isSignal: true }, { propertyName: "noDataTemplate", predicate: MlvDataTableNoData, descendants: true, isSignal: true }, { propertyName: "errorTemplate", predicate: MlvDataTableError, descendants: true, isSignal: true }, { propertyName: "footerTemplate", predicate: MlvDataTableFooter, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "tableWrapperRef", first: true, predicate: ["tableWrapper"], descendants: true, isSignal: true }, { propertyName: "tableRef", first: true, predicate: ["tableEl"], descendants: true, isSignal: true }, { propertyName: "_resizeHandleRefs", predicate: ["resizeHandle"], descendants: true, isSignal: true }, { propertyName: "_filterTriggerRef", first: true, predicate: ["filterBtn"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_virtualViewportRef", first: true, predicate: CdkVirtualScrollViewport, descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.MlvDensityDirective, inputs: ["mlvDensity", "mlvDensity"] }], ngImport: i0, template: "@if (hasToolbarContent()) {\n<div class=\"mlv-data-table__toolbar\">\n @if (showSearch() && hasSearchableColumns()) {\n <mlv-search-field\n class=\"mlv-data-table__search\"\n [value]=\"_searchDraft()\"\n (valueChange)=\"_searchDraft.set($event)\"\n [trigger]=\"searchTrigger()\"\n [debounce]=\"searchDebounce()\"\n [loading]=\"loading()\"\n [placeholder]=\"_i18n().searchPlaceholder\"\n [ariaLabel]=\"_i18n().searchLabel\"\n (search)=\"onSearch($event)\"\n />\n } @if (filterDisplay() !== 'none' && hasFilters()) {\n <span class=\"mlv-data-table__filter-badge\" aria-live=\"polite\">\n {{ activeFilterLabel() }}\n </span>\n }\n <div class=\"mlv-data-table__toolbar-actions\">\n @if (editable() && addRow()) {\n <button\n mlvButton\n variant=\"secondary\"\n mlvDensity=\"compact\"\n type=\"button\"\n (click)=\"onAddRow()\"\n [attr.aria-label]=\"_i18n().addNewRow\"\n >\n <svg lucidePlus mlvButtonIcon [size]=\"14\" />\n {{ _i18n().addRow }}\n </button>\n } @if (hasHideableColumns()) {\n <button\n mlvButton\n [variant]=\"columnsPopup.opened() ? 'primary' : 'secondary'\"\n mlvDensity=\"compact\"\n type=\"button\"\n [mlvPopupTrigger]=\"columnsPopup\"\n #columnsTrigger=\"mlvPopupTrigger\"\n [attr.aria-label]=\"_i18n().toggleColumnVisibility\"\n >\n <svg mlvButtonIcon lucideEye [size]=\"14\" />\n {{ _i18n().columns }}\n </button>\n\n <mlv-popup #columnsPopup>\n <div *mlvPopupContent class=\"mlv-data-table__columns-dropdown\">\n @for (col of allColumns(); track col.key) { @if (col.hideable) {\n <div class=\"mlv-data-table__columns-item\">\n <mlv-checkbox\n [checked]=\"!isColumnHidden(col.key)\"\n (checkedChange)=\"toggleColumnVisibility(col.key)\"\n />\n <span>{{ col.title ?? col.key }}</span>\n </div>\n } }\n </div>\n </mlv-popup>\n } @if (filterDisplay() === 'toolbar' && hasFilterableColumns()) {\n <button\n #filterBtn\n mlvButton\n [variant]=\"filtersPopup.opened() ? 'primary' : 'secondary'\"\n mlvDensity=\"compact\"\n type=\"button\"\n [mlvPopupTrigger]=\"filtersPopup\"\n #trigger=\"mlvPopupTrigger\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_i18n().toggleFilters\"\n >\n <svg lucideFilter mlvButtonIcon [size]=\"14\" />\n {{ _i18n().filters }} @if (hasFilters()) {\n <mlv-badge tone=\"primary\" rounded>{{ activeFilters().length }}</mlv-badge>\n }\n </button>\n\n <mlv-popup\n #filtersPopup\n panelRole=\"dialog\"\n [ariaLabel]=\"_i18n().filters\"\n (afterClosed)=\"_onFiltersClosed()\"\n >\n <mlv-dt-filter-dropdown\n *mlvPopupContent\n [columns]=\"columns()\"\n [(activeFilters)]=\"activeFilters\"\n (closed)=\"trigger.close()\"\n />\n </mlv-popup>\n }\n </div>\n</div>\n}\n\n<div\n class=\"mlv-data-table__wrapper\"\n [class.mlv-data-table__wrapper--virtual]=\"virtualScroll()\"\n [style.max-height]=\"virtualScroll() ? null : maxHeight()\"\n [style.--mlv-dt-virtual-scrollbar-gutter]=\"\n virtualScroll() ? _virtualScrollbarGutter() + 'px' : null\n \"\n #tableWrapper\n mlvInfiniteScroll\n [disabled]=\"paginationMode() !== 'infinite' || virtualScroll()\"\n [threshold]=\"infiniteScrollThreshold()\"\n [loading]=\"!!loading()\"\n [hasMore]=\"!!hasMore()\"\n (loadMore)=\"onInfiniteScrollLoadMore($event)\"\n>\n @if (virtualScroll()) {\n <!-- ==================== Virtual scroll mode ==================== -->\n <!-- Header table \u2014 sits naturally at the top of the wrapper, outside the viewport -->\n <table\n class=\"mlv-data-table__table mlv-data-table__table--virtual mlv-data-table__table--virtual-header\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.flex]=\"resizedTableWidth() === null ? null : '0 0 auto'\"\n role=\"grid\"\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <thead class=\"mlv-data-table__head\">\n <ng-container [ngTemplateOutlet]=\"headRowsTpl\" />\n </thead>\n </table>\n\n <!-- CDK virtual scroll viewport \u2014 owns the only vertical scroll surface in this mode.\n Infinite scroll is intentionally disabled in virtual mode: virtual scrolling is the\n rendering optimisation for already-loaded data, and the two strategies are mutually\n exclusive in this component. -->\n <cdk-virtual-scroll-viewport\n class=\"mlv-data-table__virtual-viewport\"\n [itemSize]=\"rowHeight()\"\n [style.height]=\"virtualViewportHeight()\"\n >\n <table\n class=\"mlv-data-table__table mlv-data-table__table--virtual mlv-data-table__table--virtual-body\"\n [class.mlv-data-table__table--striped]=\"striped()\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n role=\"presentation\"\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <tbody class=\"mlv-data-table__body\">\n @if (showError()) {\n <ng-container [ngTemplateOutlet]=\"errorRowTpl\" />\n } @else if (flatRows().length === 0) {\n <ng-container [ngTemplateOutlet]=\"noDataRowTpl\" />\n } @else {\n <tr\n *cdkVirtualFor=\"\n let row of flatRows();\n let i = index;\n trackBy: trackByRow\n \"\n class=\"mlv-data-table__row mlv-data-table__row--data\"\n [class.mlv-data-table__row--expanded]=\"isRowExpanded(row)\"\n [class.mlv-data-table__row--editing]=\"isEditing(i)\"\n [class.mlv-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"rowHeight()\"\n [attr.aria-level]=\"getDepth(row) > 0 ? getDepth(row) + 1 : null\"\n [attr.aria-selected]=\"selectable() ? isRowSelected(row) : null\"\n [attr.tabindex]=\"_rowTabIndex(i)\"\n [attr.data-row-index]=\"i\"\n (click)=\"onRowClick(row, i, $event)\"\n (focus)=\"_onRowFocus(i)\"\n (keydown)=\"_onRowKeydown(i, row, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowCellsTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n </tr>\n }\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n\n <!-- Footer summary table \u2014 only when a footer template is projected -->\n @if (getFooterTemplate(); as footerTpl) {\n <table\n class=\"mlv-data-table__table mlv-data-table__table--virtual mlv-data-table__table--virtual-footer\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.flex]=\"resizedTableWidth() === null ? null : '0 0 auto'\"\n role=\"presentation\"\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <tfoot class=\"mlv-data-table__foot\">\n <tr class=\"mlv-data-table__row mlv-data-table__row--footer\">\n <ng-container\n [ngTemplateOutlet]=\"footerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleColumns() }\"\n />\n </tr>\n </tfoot>\n </table>\n }\n\n <!-- Extends only the outer horizontal scroll range to match the viewport's\n native range, including a classic vertical scrollbar gutter. -->\n <div\n class=\"mlv-data-table__virtual-horizontal-sizer\"\n [style.width.px]=\"_virtualHorizontalScrollExtent()\"\n aria-hidden=\"true\"\n ></div>\n } @else if (_cellNav()) {\n <!-- ========= Experimental cell-level grid navigation (aria ngGrid) ========= -->\n <!--\n Opt-in via [cellNavigation]=\"true\" (non-virtual only). The aria Grid pattern owns\n roving focus, arrow/Home/End navigation across cells, and aria-activedescendant.\n Only the data <tbody> rows/cells are registered as grid rows/cells; header cells\n stay outside grid navigation. Interactive cells (edit inputs, action buttons)\n expose an ngGridCellWidget so Enter pauses grid nav to interact and Escape resumes.\n -->\n <table\n ngGrid\n rowWrap=\"nowrap\"\n colWrap=\"nowrap\"\n class=\"mlv-data-table__table\"\n [class.mlv-data-table__table--striped]=\"striped()\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.table-layout]=\"resizedTableWidth() === null ? null : 'fixed'\"\n role=\"grid\"\n #tableEl\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <thead\n class=\"mlv-data-table__head\"\n [class.mlv-data-table__head--sticky]=\"stickyHeader()\"\n >\n <ng-container [ngTemplateOutlet]=\"headRowsTpl\" />\n </thead>\n\n <tbody class=\"mlv-data-table__body\">\n @if (showError()) {\n <ng-container [ngTemplateOutlet]=\"errorRowTpl\" />\n } @else if (flatRows().length === 0) {\n <ng-container [ngTemplateOutlet]=\"noDataRowTpl\" />\n } @else { @for (row of flatRows(); track $index; let i = $index) {\n <tr\n ngGridRow\n class=\"mlv-data-table__row mlv-data-table__row--data\"\n [class.mlv-data-table__row--expanded]=\"isRowExpanded(row)\"\n [class.mlv-data-table__row--editing]=\"isEditing(i)\"\n [class.mlv-data-table__row--selected]=\"isRowSelected(row)\"\n [attr.aria-level]=\"getDepth(row) > 0 ? getDepth(row) + 1 : null\"\n [attr.aria-selected]=\"selectable() ? isRowSelected(row) : null\"\n [attr.data-row-index]=\"i\"\n (click)=\"onRowClick(row, i, $event)\"\n >\n @if (selectable()) {\n <td\n ngGridCell\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n >\n <mlv-checkbox\n [checked]=\"isRowSelected(row)\"\n [tabbable]=\"false\"\n (checkedChange)=\"toggleRowSelection(row)\"\n (click)=\"$event.stopPropagation()\"\n [ariaLabel]=\"'Select row ' + (i + 1)\"\n />\n </td>\n } @for (col of visibleColumns(); track col.key; let ci = $index) {\n <td\n ngGridCell\n class=\"mlv-data-table__cell mlv-data-table__cell--data\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n >\n @if ( editable() && isEditing(i) && getEditTemplate(col.key); as\n editTpl ) {\n <div\n ngGridCellWidget\n widgetType=\"editable\"\n class=\"mlv-data-table__cell-content\"\n >\n <ng-container\n [ngTemplateOutlet]=\"editTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n </div>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"cellContentTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n index: i,\n col: col,\n ci: ci,\n }\"\n />\n }\n </td>\n } @if (editable()) {\n <td\n ngGridCell\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n >\n <div\n ngGridCellWidget\n widgetType=\"complex\"\n class=\"mlv-data-table__cell-content mlv-data-table__actions\"\n >\n <ng-container\n [ngTemplateOutlet]=\"actionsButtonsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n </div>\n </td>\n }\n </tr>\n } }\n </tbody>\n\n @if (getFooterTemplate(); as footerTpl) {\n <tfoot\n class=\"mlv-data-table__foot\"\n [class.mlv-data-table__foot--sticky]=\"stickyHeader()\"\n >\n <tr class=\"mlv-data-table__row mlv-data-table__row--footer\">\n <ng-container\n [ngTemplateOutlet]=\"footerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleColumns() }\"\n />\n </tr>\n </tfoot>\n }\n </table>\n\n @if (paginationMode() === 'infinite' && loading()) {\n <div\n class=\"mlv-data-table__loading-bottom\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <mlv-loader\n variant=\"circle\"\n [indeterminate]=\"true\"\n [size]=\"24\"\n ariaLabel=\"Loading more rows\"\n />\n </div>\n } } @else {\n <!-- ==================== Standard (non-virtual) mode ==================== -->\n <table\n class=\"mlv-data-table__table\"\n [class.mlv-data-table__table--striped]=\"striped()\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.table-layout]=\"resizedTableWidth() === null ? null : 'fixed'\"\n role=\"grid\"\n #tableEl\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <thead\n class=\"mlv-data-table__head\"\n [class.mlv-data-table__head--sticky]=\"stickyHeader()\"\n >\n <ng-container [ngTemplateOutlet]=\"headRowsTpl\" />\n </thead>\n\n <tbody class=\"mlv-data-table__body\">\n @if (showError()) {\n <ng-container [ngTemplateOutlet]=\"errorRowTpl\" />\n } @else if (flatRows().length === 0) {\n <ng-container [ngTemplateOutlet]=\"noDataRowTpl\" />\n } @else { @for (row of flatRows(); track $index; let i = $index) {\n <tr\n class=\"mlv-data-table__row mlv-data-table__row--data\"\n [class.mlv-data-table__row--expanded]=\"isRowExpanded(row)\"\n [class.mlv-data-table__row--editing]=\"isEditing(i)\"\n [class.mlv-data-table__row--selected]=\"isRowSelected(row)\"\n [attr.aria-level]=\"getDepth(row) > 0 ? getDepth(row) + 1 : null\"\n [attr.aria-selected]=\"selectable() ? isRowSelected(row) : null\"\n [attr.tabindex]=\"_rowTabIndex(i)\"\n [attr.data-row-index]=\"i\"\n (click)=\"onRowClick(row, i, $event)\"\n (focus)=\"_onRowFocus(i)\"\n (keydown)=\"_onRowKeydown(i, row, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowCellsTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n </tr>\n } }\n </tbody>\n\n @if (getFooterTemplate(); as footerTpl) {\n <tfoot\n class=\"mlv-data-table__foot\"\n [class.mlv-data-table__foot--sticky]=\"stickyHeader()\"\n >\n <tr class=\"mlv-data-table__row mlv-data-table__row--footer\">\n <ng-container\n [ngTemplateOutlet]=\"footerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleColumns() }\"\n />\n </tr>\n </tfoot>\n }\n </table>\n\n @if (paginationMode() === 'infinite' && loading()) {\n <div\n class=\"mlv-data-table__loading-bottom\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <mlv-loader\n variant=\"circle\"\n [indeterminate]=\"true\"\n [size]=\"24\"\n ariaLabel=\"Loading more rows\"\n />\n </div>\n } }\n</div>\n\n@if (paginationMode() === 'paged' && !virtualScroll()) {\n<div class=\"mlv-data-table__footer\">\n <mlv-pagination\n [totalItems]=\"totalItems()\"\n [(currentPage)]=\"currentPage\"\n [(itemsPerPage)]=\"currentPerPage\"\n />\n</div>\n} @if (paginationMode() === 'paged' && loading()) {\n<div\n class=\"mlv-data-table__loading-overlay\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n>\n <mlv-loader\n variant=\"circle\"\n [indeterminate]=\"true\"\n [size]=\"48\"\n ariaLabel=\"Loading table data\"\n />\n</div>\n}\n\n<!-- ==================== Reusable templates ==================== -->\n\n<!--\n Shared <colgroup> template \u2014 used by both the standard table and the three split tables in\n virtual mode (header / body / footer). Keeping the colgroup definitions identical and combining\n it with `table-layout: fixed` keeps column widths in sync between the split tables in virtual mode.\n-->\n<ng-template #colgroupTpl>\n <colgroup>\n @if (selectable() === 'multi' || selectable() === 'single') {\n <col\n class=\"mlv-data-table__col--select\"\n [style.width]=\"getAuxiliaryColumnWidth('selection')\"\n />\n } @for (col of visibleColumns(); track col.key) {\n <col [style.width]=\"getColWidth(col)\" />\n } @if (editable()) {\n <col\n class=\"mlv-data-table__col--actions\"\n [style.width]=\"getAuxiliaryColumnWidth('actions')\"\n />\n }\n </colgroup>\n</ng-template>\n\n<!-- Header rows \u2014 emits the same content the original `<thead>` did. -->\n<ng-template #headRowsTpl>\n @if (hasColumnGroups()) {\n <!-- Group header row -->\n <tr class=\"mlv-data-table__row mlv-data-table__row--group-header\">\n @if (selectable() === 'multi') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n rowspan=\"2\"\n >\n <mlv-checkbox\n [checked]=\"allRowsSelected()\"\n [indeterminate]=\"someRowsSelected()\"\n (checkedChange)=\"toggleSelectAll()\"\n [ariaLabel]=\"_i18n().selectAllRows\"\n />\n </th>\n } @else if (selectable() === 'single') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n rowspan=\"2\"\n ></th>\n } @for (col of visibleColumns(); track col.key) { @if (headerLayout(); as\n layout) { @if (layout.standaloneKeys.has(col.key)) {\n <!-- Standalone column spans both header rows -->\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--standalone\"\n [class.mlv-data-table__cell--sortable]=\"col.sortable\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n [attr.data-mlv-column-key]=\"col.key\"\n rowspan=\"2\"\n scope=\"col\"\n [attr.aria-sort]=\"\n col.sortable\n ? getSortDirection(col) === 'asc'\n ? 'ascending'\n : getSortDirection(col) === 'desc'\n ? 'descending'\n : 'none'\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"headerCellInnerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n </th>\n } @else {\n <!-- Check if this column is the first column of a group -->\n @for (group of columnGroups(); track group.title) { @if (group.columns[0]\n === col.key) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--group-header\"\n [class.mlv-data-table__cell--align-left]=\"\n group.align === 'left'\n \"\n [class.mlv-data-table__cell--align-center]=\"\n group.align === 'center' || !group.align\n \"\n [class.mlv-data-table__cell--align-right]=\"\n group.align === 'right'\n \"\n [attr.colspan]=\"group.columns.length\"\n scope=\"colgroup\"\n >\n {{ group.title }}\n </th>\n } } } } } @if (editable()) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n rowspan=\"2\"\n >\n <span class=\"mlv-data-table__visually-hidden\"\n >{{ actionsHeaderLabel() }}</span\n >\n </th>\n }\n </tr>\n <!-- Sub-header row for grouped columns -->\n <tr class=\"mlv-data-table__row mlv-data-table__row--header\">\n @for (col of visibleColumns(); track col.key) { @if (headerLayout(); as\n layout) { @if (layout.groupedKeys.has(col.key)) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header\"\n [class.mlv-data-table__cell--sortable]=\"col.sortable\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n [attr.data-mlv-column-key]=\"col.key\"\n scope=\"col\"\n [attr.aria-sort]=\"\n col.sortable\n ? getSortDirection(col) === 'asc'\n ? 'ascending'\n : getSortDirection(col) === 'desc'\n ? 'descending'\n : 'none'\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"headerCellInnerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n </th>\n } } }\n </tr>\n } @else {\n <!-- Standard single-row header -->\n <tr class=\"mlv-data-table__row mlv-data-table__row--header\">\n @if (selectable() === 'multi') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n >\n <mlv-checkbox\n [checked]=\"allRowsSelected()\"\n [indeterminate]=\"someRowsSelected()\"\n (checkedChange)=\"toggleSelectAll()\"\n [ariaLabel]=\"_i18n().selectAllRows\"\n />\n </th>\n } @else if (selectable() === 'single') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n ></th>\n } @for (col of visibleColumns(); track col.key) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header\"\n [class.mlv-data-table__cell--sortable]=\"col.sortable\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n [attr.data-mlv-column-key]=\"col.key\"\n [attr.aria-sort]=\"\n col.sortable\n ? getSortDirection(col) === 'asc'\n ? 'ascending'\n : getSortDirection(col) === 'desc'\n ? 'descending'\n : 'none'\n : null\n \"\n scope=\"col\"\n >\n <ng-container\n [ngTemplateOutlet]=\"headerCellInnerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n </th>\n } @if (editable()) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n >\n <span class=\"mlv-data-table__visually-hidden\"\n >{{ actionsHeaderLabel() }}</span\n >\n </th>\n }\n </tr>\n }\n</ng-template>\n\n<!--\n Shared inner content for a single header cell. Context: { $implicit: col }.\n Sortable columns wrap the label + sort icon in ONE native <button> (APG grid\n sort pattern) so the <th> stays a non-interactive cell carrying only aria-sort;\n the pin button is a sibling of the sort button, never nested inside it, so no\n interactive control is nested within another (avoids axe `nested-interactive`).\n-->\n<ng-template #headerCellInnerTpl let-col>\n <div class=\"mlv-data-table__cell-inner\">\n @if (col.sortable) {\n <button\n type=\"button\"\n class=\"mlv-data-table__sort-button\"\n [attr.aria-label]=\"getSortLabel(col)\"\n (click)=\"onSortClick(col)\"\n >\n <span class=\"mlv-data-table__header-title\"\n >{{ col.title ?? col.key }}</span\n >\n <span class=\"mlv-data-table__sort-icon\" aria-hidden=\"true\">\n @if (getSortDirection(col) === 'asc') {\n <svg lucideArrowUp />\n } @else if (getSortDirection(col) === 'desc') {\n <svg lucideArrowDown />\n } @else {\n <svg lucideChevronsUpDown class=\"mlv-data-table__sort-icon--inactive\" />\n }\n </span>\n </button>\n } @else {\n <span class=\"mlv-data-table__header-title\">{{ col.title ?? col.key }}</span>\n } @if (col.pinnable) {\n <ng-container\n [ngTemplateOutlet]=\"pinButtonTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n } @if (filterDisplay() === 'column' && col.filterable) {\n <button\n mlvButton\n class=\"mlv-data-table__column-filter\"\n [class.mlv-data-table__column-filter--active]=\"\n isColumnFiltered(col.key)\n \"\n [variant]=\"\n columnFilterPopup.opened() || isColumnFiltered(col.key)\n ? 'primary'\n : 'transparent'\n \"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n [attr.data-column-key]=\"col.key\"\n [mlvPopupTrigger]=\"columnFilterPopup\"\n #columnFilterTrigger=\"mlvPopupTrigger\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"getFilterLabel(col)\"\n >\n <svg lucideFunnel mlvButtonIcon [size]=\"14\" />\n </button>\n\n <mlv-popup\n #columnFilterPopup\n panelRole=\"dialog\"\n [ariaLabel]=\"getFilterLabel(col)\"\n (afterClosed)=\"focusColumnFilterTrigger(col.key)\"\n >\n <mlv-dt-filter-dropdown\n *mlvPopupContent\n [columns]=\"getColumnFilterColumns(col)\"\n [activeFilters]=\"activeFilters()\"\n (activeFiltersChange)=\"onColumnFiltersChange(col.key, $event)\"\n (closed)=\"columnFilterTrigger.close()\"\n />\n </mlv-popup>\n }\n </div>\n @if (col.resizable) {\n <div\n #resizeHandle\n class=\"mlv-data-table__resize-handle\"\n [attr.data-column-key]=\"col.key\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"getColumnResizeLabel(col)\"\n [attr.aria-valuenow]=\"getColumnResizeValue(col)\"\n [attr.aria-valuemin]=\"getColumnResizeMin(col)\"\n [attr.aria-valuemax]=\"getColumnResizeMax(col)\"\n [attr.aria-valuetext]=\"getColumnResizeValueText(col)\"\n (pointerdown)=\"onResizeStart(col, $event)\"\n (keydown)=\"onColumnResizeKeydown(col, $event)\"\n (dblclick)=\"resetColumnWidth(col.key)\"\n ></div>\n }\n</ng-template>\n\n<!-- No-data fallback row, used by both rendering paths. -->\n<ng-template #noDataRowTpl>\n <tr class=\"mlv-data-table__row mlv-data-table__row--no-data\">\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--no-data\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n @if (getNoDataTemplate()) {\n <ng-container [ngTemplateOutlet]=\"getNoDataTemplate()!\" />\n } @else {\n <div class=\"mlv-data-table__no-data-default\">\n <svg lucideTable2 [size]=\"40\" aria-hidden=\"true\" />\n <span>No data available</span>\n </div>\n }\n </td>\n </tr>\n</ng-template>\n\n<!--\n Failed-request row, used by all three rendering paths. Takes precedence over both the\n data rows and #noDataRowTpl so a failed request never reads as an empty result set.\n The region is announced as an alert; the default block is an mlv-empty-state with a\n danger-toned icon and a Retry button wired to the (retry) output.\n-->\n<ng-template #errorRowTpl>\n <tr class=\"mlv-data-table__row mlv-data-table__row--error\">\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--error\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <div class=\"mlv-data-table__error\" role=\"alert\" aria-live=\"assertive\">\n @if (getErrorTemplate(); as errorTpl) {\n <ng-container\n [ngTemplateOutlet]=\"errorTpl\"\n [ngTemplateOutletContext]=\"_errorContext()\"\n />\n } @else {\n <mlv-empty-state>\n <svg\n mlvEmptyStateIcon\n lucideTriangleAlert\n class=\"mlv-data-table__error-icon\"\n [size]=\"40\"\n aria-hidden=\"true\"\n />\n <span mlvEmptyStateTitle>{{ _i18n().errorTitle }}</span>\n <span mlvEmptyStateDescription>{{ errorMessage() }}</span>\n <button\n mlvEmptyStateActions\n mlvButton\n variant=\"secondary\"\n type=\"button\"\n class=\"mlv-data-table__error-retry\"\n (click)=\"onRetry()\"\n >\n {{ _i18n().retry }}\n </button>\n </mlv-empty-state>\n }\n </div>\n </td>\n </tr>\n</ng-template>\n\n<!--\n Per-row data cells. Emits the selection cell, the data cells, and the actions cell.\n The owning <tr> stays at the consumer site (either inside `@for` for the standard mode\n or `*cdkVirtualFor` inside the CDK viewport in virtual mode), so each rendering path can\n control its own row-level host bindings.\n-->\n<ng-template #rowCellsTpl let-row let-i=\"index\">\n @if (selectable()) {\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n >\n <!--\n [tabbable]=\"false\" keeps this checkbox out of the tab order so the row is\n the single tab stop: the row's Space/Enter toggles selection, and the\n checkbox stays operable via the mouse. Avoids a per-row double tab stop.\n -->\n <mlv-checkbox\n [checked]=\"isRowSelected(row)\"\n [tabbable]=\"false\"\n (checkedChange)=\"toggleRowSelection(row)\"\n (click)=\"$event.stopPropagation()\"\n [ariaLabel]=\"'Select row ' + (i + 1)\"\n />\n </td>\n } @for (col of visibleColumns(); track col.key; let ci = $index) {\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--data\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cellContentTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n index: i,\n col: col,\n ci: ci,\n }\"\n />\n </td>\n } @if (editable()) {\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n >\n <div class=\"mlv-data-table__cell-content mlv-data-table__actions\">\n <ng-container\n [ngTemplateOutlet]=\"actionsButtonsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n </div>\n </td>\n }\n</ng-template>\n\n<!--\n Shared edit/save/cancel action buttons for the actions column. Context: { $implicit: row, index: i }.\n-->\n<ng-template #actionsButtonsTpl let-row let-i=\"index\">\n @if (isEditing(i)) {\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n (click)=\"saveEdit(row, i)\"\n [attr.aria-label]=\"_i18n().saveChanges\"\n >\n <svg lucideCheck mlvButtonIcon />\n </button>\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n (click)=\"cancelEdit(row, i)\"\n [attr.aria-label]=\"_i18n().cancelEditing\"\n >\n <svg lucideX mlvButtonIcon />\n </button>\n } @else {\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n (click)=\"startEdit(row, i)\"\n [attr.aria-label]=\"_i18n().editRow\"\n >\n <svg lucidePencil mlvButtonIcon />\n </button>\n }\n</ng-template>\n\n<!--\n Shared inner content of a data cell (indent, expand button, edit/display template, raw value).\n Extracted so both the standard `#rowCellsTpl` `<td>` and the experimental cell-navigation grid\n `<td ngGridCell>` render identical content. Purely presentational \u2014 contains no aria grid\n directives \u2014 so it is safe to project via ngTemplateOutlet across the grid-cell boundary.\n Context: { $implicit: row, index: i, col, ci }.\n-->\n<ng-template #cellContentTpl let-row let-i=\"index\" let-col=\"col\" let-ci=\"ci\">\n <div class=\"mlv-data-table__cell-content\">\n @if (ci === 0 && getDepth(row) > 0) {\n <span\n class=\"mlv-data-table__indent\"\n [style.padding-left.rem]=\"getDepth(row) * 1.25\"\n aria-hidden=\"true\"\n ></span>\n } @if (ci === 0 && hasChildren(row)) {\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n class=\"mlv-data-table__expand-btn\"\n (click)=\"toggleExpand(row, $event)\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n [attr.aria-label]=\"\n isRowExpanded(row) ? _i18n().collapseRow : _i18n().expandRow\n \"\n >\n <svg\n lucideChevronRight\n mlvButtonIcon\n class=\"mlv-data-table__expand-icon\"\n [class.mlv-data-table__expand-icon--open]=\"isRowExpanded(row)\"\n />\n </button>\n } @if (editable() && isEditing(i) && getEditTemplate(col.key); as editTpl) {\n <ng-container\n [ngTemplateOutlet]=\"editTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n } @else if (getTemplate(col.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n } @else {\n <!--\n Default cell renderer. `valueLabels` (or the `filterConfig.options` fallback)\n maps the raw value to a label; `tone` promotes the cell to a muted badge.\n -->\n @if (getCellTone(row, col); as tone) {\n <mlv-badge [tone]=\"tone\" muted\n >{{ getCellDisplayValue(row, col) }}</mlv-badge\n >\n } @else { {{ getCellDisplayValue(row, col) }} } }\n </div>\n</ng-template>\n\n<!-- Reusable pin button template. Rendered via ngTemplateOutlet from every header cell that supports pinning. -->\n<ng-template #pinButtonTpl let-col>\n @if (getPinnableSides(col); as sides) { @if (sides.length > 1) {\n <button\n class=\"mlv-data-table__pin-btn\"\n [class.mlv-data-table__pin-btn--active]=\"isColumnPinned(col)\"\n type=\"button\"\n [mlvPopupTrigger]=\"pinPopup\"\n #pinTrigger=\"mlvPopupTrigger\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"\n isColumnPinned(col) ? _i18n().changePinSide : _i18n().pinColumn\n \"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"pinPopup.opened()\"\n [attr.aria-pressed]=\"isColumnPinned(col)\"\n >\n @if (isColumnPinned(col)) {\n <svg lucidePinOff />\n } @else {\n <svg lucidePin />\n }\n </button>\n <mlv-popup #pinPopup>\n <div *mlvPopupContent class=\"mlv-data-table__pin-popup\" role=\"menu\">\n @for (side of sides; track side) {\n <button\n type=\"button\"\n role=\"menuitemradio\"\n class=\"mlv-data-table__pin-popup-item\"\n [class.mlv-data-table__pin-popup-item--active]=\"\n getColumnPinSide(col) === side\n \"\n [attr.aria-checked]=\"getColumnPinSide(col) === side\"\n (click)=\"\n pinTo(col, side); pinTrigger.close(); $event.stopPropagation()\n \"\n >\n <svg lucidePin [size]=\"14\" />\n @if (side === 'left') {\n <span>Pin left</span>\n } @else {\n <span>Pin right</span>\n }\n </button>\n } @if (isColumnPinned(col)) {\n <button\n type=\"button\"\n role=\"menuitem\"\n class=\"mlv-data-table__pin-popup-item mlv-data-table__pin-popup-item--unpin\"\n (click)=\"unpin(col); pinTrigger.close(); $event.stopPropagation()\"\n >\n <svg lucidePinOff [size]=\"14\" />\n <span>{{ _i18n().unpin }}</span>\n </button>\n }\n </div>\n </mlv-popup>\n } @else {\n <button\n class=\"mlv-data-table__pin-btn\"\n [class.mlv-data-table__pin-btn--active]=\"isColumnPinned(col)\"\n type=\"button\"\n (click)=\"togglePin(col, sides[0]); $event.stopPropagation()\"\n [attr.aria-label]=\"\n isColumnPinned(col) ? _i18n().unpinColumn : _i18n().pinColumn\n \"\n [attr.aria-pressed]=\"isColumnPinned(col)\"\n >\n @if (isColumnPinned(col)) {\n <svg lucidePinOff />\n } @else {\n <svg lucidePin />\n }\n </button>\n } }\n</ng-template>\n", styles: [".mlv-data-table{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-dt-font-size)}.mlv-data-table *{box-sizing:border-box}.mlv-data-table{--mlv-dt-row-height: 3.5rem;--mlv-dt-cell-padding-x: 1.25rem;--mlv-dt-header-height: 3rem;--mlv-dt-font-size: var(--mlv-font-size-m);--mlv-dt-header-font-size: var(--mlv-font-size-m);--mlv-dt-icon-size: 1rem;--mlv-dt-toolbar-gap: .5rem;--mlv-dt-toolbar-padding: .75rem 0;display:flex;flex-direction:column;width:100%;position:relative;color:var(--mlv-text-primary)}[class*=--tight] .mlv-data-table:not([class*=--compact],[class*=--comfortable],[class*=--spacious],[class*=--airy]),.mlv-data-table[class*=--tight]{--mlv-dt-row-height: 2.5rem;--mlv-dt-cell-padding-x: .75rem;--mlv-dt-header-height: 2.25rem;--mlv-dt-font-size: var(--mlv-font-size-s);--mlv-dt-header-font-size: var(--mlv-font-size-s);--mlv-dt-icon-size: .75rem;--mlv-dt-toolbar-gap: .25rem;--mlv-dt-toolbar-padding: .375rem 0}[class*=--compact] .mlv-data-table:not([class*=--tight],[class*=--comfortable],[class*=--spacious],[class*=--airy]),.mlv-data-table[class*=--compact]{--mlv-dt-row-height: 3rem;--mlv-dt-cell-padding-x: 1rem;--mlv-dt-header-height: 2.5rem;--mlv-dt-font-size: var(--mlv-font-size-s);--mlv-dt-header-font-size: var(--mlv-font-size-s);--mlv-dt-icon-size: .875rem;--mlv-dt-toolbar-gap: .375rem;--mlv-dt-toolbar-padding: .5rem 0}[class*=--spacious] .mlv-data-table:not([class*=--tight],[class*=--compact],[class*=--comfortable],[class*=--airy]),.mlv-data-table[class*=--spacious]{--mlv-dt-row-height: 4.25rem;--mlv-dt-cell-padding-x: 1.5rem;--mlv-dt-header-height: 3.25rem;--mlv-dt-font-size: var(--mlv-font-size-l);--mlv-dt-header-font-size: var(--mlv-font-size-l);--mlv-dt-icon-size: 1.125rem;--mlv-dt-toolbar-gap: .75rem;--mlv-dt-toolbar-padding: .75rem 0}[class*=--airy] .mlv-data-table:not([class*=--tight],[class*=--compact],[class*=--comfortable],[class*=--spacious]),.mlv-data-table[class*=--airy]{--mlv-dt-row-height: 5rem;--mlv-dt-cell-padding-x: 1.75rem;--mlv-dt-header-height: 3.75rem;--mlv-dt-font-size: var(--mlv-font-size-l);--mlv-dt-header-font-size: var(--mlv-font-size-l);--mlv-dt-icon-size: 1.25rem;--mlv-dt-toolbar-gap: 1rem;--mlv-dt-toolbar-padding: 1rem 0}.mlv-data-table__toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--mlv-dt-toolbar-gap);padding:var(--mlv-dt-toolbar-padding);position:relative}.mlv-data-table__search{flex:1 1 16rem;width:min(100%,24rem);max-width:24rem}.mlv-data-table__toolbar-actions{display:flex;gap:var(--mlv-dt-toolbar-gap);margin-left:auto}.mlv-data-table__filter-badge{font-size:var(--mlv-dt-font-size);color:var(--mlv-text-secondary)}.mlv-data-table__wrapper{width:100%;overflow-x:auto;overflow-y:auto;position:relative}.mlv-data-table__wrapper--virtual{overflow-y:visible;display:flex;flex-direction:column}.mlv-data-table__virtual-viewport{display:block;width:100%;min-width:0;max-width:100%;flex:0 0 auto;position:sticky;left:0;border-bottom:.0625rem solid var(--mlv-border-subtle)}.mlv-data-table__virtual-viewport.cdk-virtual-scrollable{overflow-x:hidden;overflow-y:auto}.mlv-data-table__virtual-horizontal-sizer{width:0;height:0;min-height:0;flex:0 0 0;align-self:flex-start;pointer-events:none}.mlv-data-table__table--virtual-header,.mlv-data-table__table--virtual-footer{--mlv-dt-pinned-right-correction: var( --mlv-dt-virtual-scrollbar-gutter, 0px )}.mlv-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--mlv-background-base) 70%,transparent);-webkit-backdrop-filter:blur(.0625rem);backdrop-filter:blur(.0625rem);z-index:20;pointer-events:all}.mlv-data-table__loading-bottom{display:flex;align-items:center;justify-content:center;padding:1rem 0;color:var(--mlv-text-secondary)}.mlv-data-table__table{width:100%;border-collapse:collapse;table-layout:auto;font-size:var(--mlv-dt-font-size);color:var(--mlv-text-primary)}.mlv-data-table__table--striped .mlv-data-table__row--data:nth-child(2n){background:var(--mlv-background-sunken)}.mlv-data-table__table--striped .mlv-data-table__row--data:nth-child(2n) .mlv-data-table__cell--pinned{background:var(--mlv-background-sunken)}.mlv-data-table__table--bordered .mlv-data-table__cell{border-right:.0625rem solid var(--mlv-border-subtle)}.mlv-data-table__table--bordered .mlv-data-table__cell:last-child{border-right:none}.mlv-data-table__table--virtual{table-layout:fixed}.mlv-data-table__head--sticky{position:sticky;top:var(--mlv-dt-sticky-top, 0);z-index:3;background:var(--mlv-background-base)}.mlv-data-table__head--sticky .mlv-data-table__cell--pinned{z-index:4}.mlv-data-table__cell{padding:0;border-bottom:.0625rem solid var(--mlv-border-subtle);vertical-align:middle;background:inherit}.mlv-data-table__cell-content{display:flex;align-items:center;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.mlv-data-table__cell--header{font-weight:var(--mlv-font-weight-medium);font-size:var(--mlv-dt-header-font-size);color:color-mix(in srgb,var(--mlv-text-action) 65%,var(--mlv-text-secondary));position:relative;-webkit-user-select:none;user-select:none;border-bottom:.0625rem solid var(--mlv-border-normal)}.mlv-data-table__cell--sortable{cursor:pointer}.mlv-data-table__cell--sortable:hover .mlv-data-table__cell-inner{color:var(--mlv-text-action)}.mlv-data-table__cell--data{padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-row-height);font-size:var(--mlv-dt-font-size)}.mlv-data-table__cell--pinned{background:var(--mlv-background-base);z-index:1;transition:background var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__cell--pinned:after{content:\"\";position:absolute;top:0;bottom:0;width:.5rem;pointer-events:none}.mlv-data-table__cell--pinned-left:after{right:-.5rem;background:linear-gradient(to right,rgba(0,0,0,.04),transparent)}.mlv-data-table__cell--pinned-right:after{left:-.5rem;background:linear-gradient(to left,rgba(0,0,0,.04),transparent)}.mlv-data-table__cell--no-data,.mlv-data-table__cell--error{padding:3rem var(--mlv-dt-cell-padding-x);text-align:center}.mlv-data-table__cell--align-left{text-align:left}.mlv-data-table__cell--align-center{text-align:center}.mlv-data-table__cell--align-right{text-align:right}.mlv-data-table__cell-inner{display:flex;align-items:center;gap:.375rem;padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-header-height);transition:color var(--mlv-duration-normal) var(--mlv-ease-default);border-radius:var(--mlv-radius-s)}.mlv-data-table__sort-button{display:inline-flex;align-items:center;gap:.375rem;flex:1;min-width:0;margin:0;padding:0;border:none;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;border-radius:var(--mlv-radius-s)}.mlv-data-table__sort-button:focus-visible{outline:var(--mlv-stroke-width) solid var(--mlv-border-focus);outline-offset:.125rem}.mlv-data-table__visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}.mlv-data-table__header-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}.mlv-data-table__column-filter{flex:0 0 auto}.mlv-data-table__column-filter--active{color:var(--mlv-text-action)}.mlv-data-table__sort-icon{display:inline-flex;flex-shrink:0;color:var(--mlv-text-action)}.mlv-data-table__sort-icon svg{width:var(--mlv-dt-icon-size);height:var(--mlv-dt-icon-size)}.mlv-data-table__sort-icon--inactive{opacity:0;transition:opacity var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__cell--sortable:hover .mlv-data-table__sort-icon--inactive{opacity:.5}.mlv-data-table__pin-btn{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;padding:0;border:none;background:none;cursor:pointer;color:var(--mlv-text-tertiary);opacity:0;transition:opacity var(--mlv-duration-normal) var(--mlv-ease-default),color var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__pin-btn svg{width:var(--mlv-dt-icon-size);height:var(--mlv-dt-icon-size)}.mlv-data-table__cell--header:hover .mlv-data-table__pin-btn{opacity:.6}.mlv-data-table__pin-btn:hover{opacity:1!important;color:var(--mlv-text-action)}.mlv-data-table__pin-btn--active{opacity:1;color:var(--mlv-text-action)}.mlv-data-table__cell--header:hover .mlv-data-table__pin-btn--active{opacity:1}.mlv-data-table__resize-handle{position:absolute;top:0;right:-.5rem;width:1rem;height:100%;cursor:col-resize;background:transparent;z-index:5;touch-action:none;-webkit-user-select:none;user-select:none}.mlv-data-table__resize-handle:after{content:\"\";position:absolute;top:.75rem;bottom:.75rem;left:50%;width:.0625rem;border-radius:var(--mlv-radius-full);background-color:var(--mlv-text-secondary);transform:translate(-50%);transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),width var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-data-table__resize-handle:hover:after{width:.125rem;background-color:var(--mlv-text-primary)}.mlv-data-table__resize-handle:active:after,.mlv-data-table__resize-handle:focus-visible:after{width:.125rem;background-color:var(--mlv-background-accent-1)}.mlv-data-table__resize-handle:focus-visible{outline:var(--mlv-stroke-width) solid var(--mlv-border-focus);outline-offset:-.125rem}@media(pointer:coarse){.mlv-data-table__resize-handle{right:-.75rem;width:1.5rem}}.mlv-data-table__row{transition:background var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__row--data:hover,.mlv-data-table__row--data:hover .mlv-data-table__cell--pinned,.mlv-data-table__row--expanded,.mlv-data-table__row--expanded .mlv-data-table__cell--pinned{background:var(--mlv-background-sunken)}.mlv-data-table__indent{display:inline-block}.mlv-data-table__expand-btn{margin-right:.25rem;vertical-align:middle;flex-shrink:0}.mlv-data-table__expand-icon{width:var(--mlv-dt-icon-size);height:var(--mlv-dt-icon-size);transition:transform var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__expand-icon--open{transform:rotate(90deg)}.mlv-data-table__no-data-default{display:flex;flex-direction:column;align-items:center;gap:.75rem;padding:3rem 0;color:var(--mlv-text-tertiary)}.mlv-data-table__no-data-default svg{opacity:.2}.mlv-data-table__no-data-default span{font-size:var(--mlv-dt-font-size)}.mlv-data-table__error{display:block;text-align:initial}.mlv-data-table__error-icon{color:var(--mlv-text-negative)}.mlv-data-table__row--group-header .mlv-data-table__cell--group-header{font-weight:var(--mlv-font-weight-medium);font-size:var(--mlv-dt-header-font-size);color:color-mix(in srgb,var(--mlv-text-action) 65%,var(--mlv-text-secondary));text-align:center;padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-header-height);border-bottom:.0625rem solid var(--mlv-border-subtle);vertical-align:middle}.mlv-data-table__row--group-header .mlv-data-table__cell--standalone{vertical-align:middle}.mlv-data-table__cell--actions{width:1%;white-space:nowrap;padding:0 var(--mlv-dt-cell-padding-x)!important}.mlv-data-table__actions{display:flex;align-items:center;gap:.25rem;justify-content:flex-end}.mlv-data-table__row--editing,.mlv-data-table__row--editing .mlv-data-table__cell--pinned{background:var(--mlv-background-sunken)}.mlv-data-table__cell--select{width:2.5rem;min-width:2.5rem;max-width:2.5rem;padding:0 .5rem!important;text-align:center}.mlv-data-table__col--select{width:2.5rem}.mlv-data-table__col--actions{width:5rem}.mlv-data-table__row--selected,.mlv-data-table__row--selected .mlv-data-table__cell--pinned{background:color-mix(in srgb,var(--mlv-background-accent-1) 8%,var(--mlv-background-base))}.mlv-data-table__pin-popup{display:flex;flex-direction:column;gap:.125rem;padding:.25rem;min-width:8rem}.mlv-data-table__pin-popup-item{display:flex;align-items:center;gap:.5rem;padding:.375rem .5rem;border:none;background:none;border-radius:var(--mlv-radius-s);cursor:pointer;font:inherit;font-size:var(--mlv-dt-font-size);color:var(--mlv-text-primary);text-align:left}.mlv-data-table__pin-popup-item svg{color:var(--mlv-text-secondary);flex-shrink:0}.mlv-data-table__pin-popup-item:hover{background:var(--mlv-background-sunken)}.mlv-data-table__pin-popup-item--active{color:var(--mlv-text-action)}.mlv-data-table__pin-popup-item--active svg{color:var(--mlv-text-action)}.mlv-data-table__pin-popup-item--unpin{color:var(--mlv-text-negative);border-top:.0625rem solid var(--mlv-border-subtle);margin-top:.125rem;padding-top:.5rem;border-radius:0 0 var(--mlv-radius-s) var(--mlv-radius-s)}.mlv-data-table__pin-popup-item--unpin svg{color:var(--mlv-text-negative)}.mlv-data-table__pin-popup-item--unpin:hover{background:color-mix(in srgb,var(--mlv-text-negative) 8%,transparent)}.mlv-data-table__columns-dropdown{display:flex;flex-direction:column;gap:.25rem;padding:.5rem;min-width:10rem}.mlv-data-table__columns-item{display:flex;align-items:center;gap:.5rem;padding:.25rem .5rem;border-radius:var(--mlv-radius-s);cursor:pointer;font-size:var(--mlv-dt-font-size)}.mlv-data-table__columns-item:hover{background:var(--mlv-background-sunken)}.mlv-data-table__foot{font-weight:var(--mlv-font-weight-medium);font-size:var(--mlv-dt-font-size);background:var(--mlv-background-base)}.mlv-data-table__foot--sticky{position:sticky;bottom:0;z-index:3}.mlv-data-table__foot .mlv-data-table__cell--pinned,.mlv-data-table__row--footer{background:inherit}.mlv-data-table__row--footer>:where(td,th){box-sizing:border-box;padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-header-height);border-top:.125rem solid var(--mlv-border-normal);border-bottom:0;vertical-align:middle;background:inherit;font-size:inherit;font-weight:inherit}.mlv-data-table__table--bordered .mlv-data-table__row--footer>:where(td,th){border-right:.0625rem solid var(--mlv-border-subtle)}.mlv-data-table__table--bordered .mlv-data-table__row--footer>:where(td,th):last-child{border-right:none}.mlv-data-table__footer{padding-top:.75rem}@media(prefers-reduced-motion:reduce){.mlv-data-table,.mlv-data-table *,.mlv-data-table:before,.mlv-data-table:after,[class^=mlv-data-table__],[class*=\" mlv-data-table__\"],[class^=mlv-data-table--],[class*=\" mlv-data-table--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i2.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i2.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i2.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: MlvPagination, selector: "mlv-pagination", inputs: ["id", "mobile", "totalItems", "itemsPerPage", "perPageOptions", "displayTotalItems", "currentPage", "mlvDensity"], outputs: ["itemsPerPageChange", "currentPageChange"] }, { kind: "component", type: MlvSearchField, selector: "mlv-search-field", inputs: ["value", "trigger", "debounce", "placeholder", "ariaLabel", "disabled", "loading", "showSubmit", "clearable", "commitOnClear", "presentation", "overlay", "role", "ariaControls", "ariaActiveDescendant", "ariaAutocomplete", "ariaExpanded"], outputs: ["valueChange", "search", "navigate", "commit"] }, { kind: "component", type: MlvFilterDropdown, selector: "mlv-dt-filter-dropdown", inputs: ["columns", "activeFilters"], outputs: ["activeFiltersChange", "closed"] }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading"] }, { kind: "component", type: MlvBadge, selector: "mlv-badge", inputs: ["tone", "rounded", "muted"] }, { kind: "component", type: LucideArrowUp, selector: "svg[lucideArrowUp]" }, { kind: "component", type: LucideArrowDown, selector: "svg[lucideArrowDown]" }, { kind: "component", type: LucideChevronsUpDown, selector: "svg[lucideChevronsUpDown]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideFilter, selector: "svg[lucideFunnel], svg[lucideFilter]" }, { kind: "component", type: LucideTable2, selector: "svg[lucideTable2]" }, { kind: "component", type: LucidePencil, selector: "svg[lucidePencil]" }, { kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }, { kind: "component", type: LucidePlus, selector: "svg[lucidePlus]" }, { kind: "component", type: LucidePin, selector: "svg[lucidePin]" }, { kind: "component", type: LucidePinOff, selector: "svg[lucidePinOff]" }, { kind: "component", type: LucideEye, selector: "svg[lucideEye]" }, { kind: "component", type: LucideTriangleAlert, selector: "svg[lucideTriangleAlert], svg[lucideAlertTriangle]" }, { kind: "component", type: MlvEmptyState, selector: "mlv-empty-state" }, { kind: "component", type: MlvLoader, selector: "mlv-loader", inputs: ["variant", "tone", "value", "max", "indeterminate", "size", "strokeWidth", "ariaLabel", "showHint", "color", "trackColor", "glow"] }, { kind: "component", type: MlvCheckbox, selector: "mlv-checkbox", inputs: ["ariaLabelledBy", "checked", "indeterminate", "tabbable"], outputs: ["checkedChange"] }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvInfiniteScroll, selector: "[mlvInfiniteScroll]", inputs: ["threshold", "loading", "hasMore", "disabled", "orientation", "scrollContainer"], outputs: ["loadMore"], exportAs: ["mlvInfiniteScroll"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: Grid, selector: "[ngGrid]", inputs: ["enableSelection", "disabled", "softDisabled", "focusMode", "rowWrap", "colWrap", "multi", "selectionMode", "tabindex"], exportAs: ["ngGrid"] }, { kind: "directive", type: GridRow, selector: "[ngGridRow]", inputs: ["rowIndex"], exportAs: ["ngGridRow"] }, { kind: "directive", type: GridCell, selector: "[ngGridCell]", inputs: ["id", "role", "rowSpan", "colSpan", "rowIndex", "colIndex", "disabled", "selected", "selectable", "tabindex"], outputs: ["selectedChange"], exportAs: ["ngGridCell"] }, { kind: "directive", type: GridCellWidget, selector: "[ngGridCellWidget]", inputs: ["id", "widgetType", "disabled", "focusTarget", "tabindex"], outputs: ["activated", "deactivated"], exportAs: ["ngGridCellWidget"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1782
|
+
}
|
|
1783
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvDataTable, decorators: [{
|
|
1784
|
+
type: Component,
|
|
1785
|
+
args: [{ selector: 'mlv-data-table', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1786
|
+
NgTemplateOutlet,
|
|
1787
|
+
ScrollingModule,
|
|
1788
|
+
MlvPagination,
|
|
1789
|
+
MlvSearchField,
|
|
1790
|
+
MlvFilterDropdown,
|
|
1791
|
+
MlvButton,
|
|
1792
|
+
MlvBadge,
|
|
1793
|
+
LucideArrowUp,
|
|
1794
|
+
LucideArrowDown,
|
|
1795
|
+
LucideChevronsUpDown,
|
|
1796
|
+
LucideChevronRight,
|
|
1797
|
+
LucideFilter,
|
|
1798
|
+
LucideTable2,
|
|
1799
|
+
LucideFunnel,
|
|
1800
|
+
LucidePencil,
|
|
1801
|
+
LucideCheck,
|
|
1802
|
+
LucideX,
|
|
1803
|
+
LucidePlus,
|
|
1804
|
+
LucidePin,
|
|
1805
|
+
LucidePinOff,
|
|
1806
|
+
LucideEye,
|
|
1807
|
+
LucideTriangleAlert,
|
|
1808
|
+
MlvEmptyState,
|
|
1809
|
+
MlvLoader,
|
|
1810
|
+
MlvCheckbox,
|
|
1811
|
+
MlvPopupTrigger,
|
|
1812
|
+
MlvPopup,
|
|
1813
|
+
MlvPopupContent,
|
|
1814
|
+
MlvInfiniteScroll,
|
|
1815
|
+
MlvButtonIcon,
|
|
1816
|
+
Grid,
|
|
1817
|
+
GridRow,
|
|
1818
|
+
GridCell,
|
|
1819
|
+
GridCellWidget,
|
|
1820
|
+
], providers: [
|
|
1821
|
+
{ provide: MLV_DENSITY_ELEMENT, useValue: 'data-table' },
|
|
1822
|
+
MlvDataTableEditingService,
|
|
1823
|
+
MlvDataTableColumnVisibilityService,
|
|
1824
|
+
MlvDataTablePinningService,
|
|
1825
|
+
], hostDirectives: [
|
|
1826
|
+
{
|
|
1827
|
+
directive: MlvDensityDirective,
|
|
1828
|
+
inputs: ['mlvDensity'],
|
|
1829
|
+
},
|
|
1830
|
+
], host: {
|
|
1831
|
+
class: 'mlv-data-table',
|
|
1832
|
+
}, template: "@if (hasToolbarContent()) {\n<div class=\"mlv-data-table__toolbar\">\n @if (showSearch() && hasSearchableColumns()) {\n <mlv-search-field\n class=\"mlv-data-table__search\"\n [value]=\"_searchDraft()\"\n (valueChange)=\"_searchDraft.set($event)\"\n [trigger]=\"searchTrigger()\"\n [debounce]=\"searchDebounce()\"\n [loading]=\"loading()\"\n [placeholder]=\"_i18n().searchPlaceholder\"\n [ariaLabel]=\"_i18n().searchLabel\"\n (search)=\"onSearch($event)\"\n />\n } @if (filterDisplay() !== 'none' && hasFilters()) {\n <span class=\"mlv-data-table__filter-badge\" aria-live=\"polite\">\n {{ activeFilterLabel() }}\n </span>\n }\n <div class=\"mlv-data-table__toolbar-actions\">\n @if (editable() && addRow()) {\n <button\n mlvButton\n variant=\"secondary\"\n mlvDensity=\"compact\"\n type=\"button\"\n (click)=\"onAddRow()\"\n [attr.aria-label]=\"_i18n().addNewRow\"\n >\n <svg lucidePlus mlvButtonIcon [size]=\"14\" />\n {{ _i18n().addRow }}\n </button>\n } @if (hasHideableColumns()) {\n <button\n mlvButton\n [variant]=\"columnsPopup.opened() ? 'primary' : 'secondary'\"\n mlvDensity=\"compact\"\n type=\"button\"\n [mlvPopupTrigger]=\"columnsPopup\"\n #columnsTrigger=\"mlvPopupTrigger\"\n [attr.aria-label]=\"_i18n().toggleColumnVisibility\"\n >\n <svg mlvButtonIcon lucideEye [size]=\"14\" />\n {{ _i18n().columns }}\n </button>\n\n <mlv-popup #columnsPopup>\n <div *mlvPopupContent class=\"mlv-data-table__columns-dropdown\">\n @for (col of allColumns(); track col.key) { @if (col.hideable) {\n <div class=\"mlv-data-table__columns-item\">\n <mlv-checkbox\n [checked]=\"!isColumnHidden(col.key)\"\n (checkedChange)=\"toggleColumnVisibility(col.key)\"\n />\n <span>{{ col.title ?? col.key }}</span>\n </div>\n } }\n </div>\n </mlv-popup>\n } @if (filterDisplay() === 'toolbar' && hasFilterableColumns()) {\n <button\n #filterBtn\n mlvButton\n [variant]=\"filtersPopup.opened() ? 'primary' : 'secondary'\"\n mlvDensity=\"compact\"\n type=\"button\"\n [mlvPopupTrigger]=\"filtersPopup\"\n #trigger=\"mlvPopupTrigger\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_i18n().toggleFilters\"\n >\n <svg lucideFilter mlvButtonIcon [size]=\"14\" />\n {{ _i18n().filters }} @if (hasFilters()) {\n <mlv-badge tone=\"primary\" rounded>{{ activeFilters().length }}</mlv-badge>\n }\n </button>\n\n <mlv-popup\n #filtersPopup\n panelRole=\"dialog\"\n [ariaLabel]=\"_i18n().filters\"\n (afterClosed)=\"_onFiltersClosed()\"\n >\n <mlv-dt-filter-dropdown\n *mlvPopupContent\n [columns]=\"columns()\"\n [(activeFilters)]=\"activeFilters\"\n (closed)=\"trigger.close()\"\n />\n </mlv-popup>\n }\n </div>\n</div>\n}\n\n<div\n class=\"mlv-data-table__wrapper\"\n [class.mlv-data-table__wrapper--virtual]=\"virtualScroll()\"\n [style.max-height]=\"virtualScroll() ? null : maxHeight()\"\n [style.--mlv-dt-virtual-scrollbar-gutter]=\"\n virtualScroll() ? _virtualScrollbarGutter() + 'px' : null\n \"\n #tableWrapper\n mlvInfiniteScroll\n [disabled]=\"paginationMode() !== 'infinite' || virtualScroll()\"\n [threshold]=\"infiniteScrollThreshold()\"\n [loading]=\"!!loading()\"\n [hasMore]=\"!!hasMore()\"\n (loadMore)=\"onInfiniteScrollLoadMore($event)\"\n>\n @if (virtualScroll()) {\n <!-- ==================== Virtual scroll mode ==================== -->\n <!-- Header table \u2014 sits naturally at the top of the wrapper, outside the viewport -->\n <table\n class=\"mlv-data-table__table mlv-data-table__table--virtual mlv-data-table__table--virtual-header\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.flex]=\"resizedTableWidth() === null ? null : '0 0 auto'\"\n role=\"grid\"\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <thead class=\"mlv-data-table__head\">\n <ng-container [ngTemplateOutlet]=\"headRowsTpl\" />\n </thead>\n </table>\n\n <!-- CDK virtual scroll viewport \u2014 owns the only vertical scroll surface in this mode.\n Infinite scroll is intentionally disabled in virtual mode: virtual scrolling is the\n rendering optimisation for already-loaded data, and the two strategies are mutually\n exclusive in this component. -->\n <cdk-virtual-scroll-viewport\n class=\"mlv-data-table__virtual-viewport\"\n [itemSize]=\"rowHeight()\"\n [style.height]=\"virtualViewportHeight()\"\n >\n <table\n class=\"mlv-data-table__table mlv-data-table__table--virtual mlv-data-table__table--virtual-body\"\n [class.mlv-data-table__table--striped]=\"striped()\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n role=\"presentation\"\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <tbody class=\"mlv-data-table__body\">\n @if (showError()) {\n <ng-container [ngTemplateOutlet]=\"errorRowTpl\" />\n } @else if (flatRows().length === 0) {\n <ng-container [ngTemplateOutlet]=\"noDataRowTpl\" />\n } @else {\n <tr\n *cdkVirtualFor=\"\n let row of flatRows();\n let i = index;\n trackBy: trackByRow\n \"\n class=\"mlv-data-table__row mlv-data-table__row--data\"\n [class.mlv-data-table__row--expanded]=\"isRowExpanded(row)\"\n [class.mlv-data-table__row--editing]=\"isEditing(i)\"\n [class.mlv-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"rowHeight()\"\n [attr.aria-level]=\"getDepth(row) > 0 ? getDepth(row) + 1 : null\"\n [attr.aria-selected]=\"selectable() ? isRowSelected(row) : null\"\n [attr.tabindex]=\"_rowTabIndex(i)\"\n [attr.data-row-index]=\"i\"\n (click)=\"onRowClick(row, i, $event)\"\n (focus)=\"_onRowFocus(i)\"\n (keydown)=\"_onRowKeydown(i, row, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowCellsTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n </tr>\n }\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n\n <!-- Footer summary table \u2014 only when a footer template is projected -->\n @if (getFooterTemplate(); as footerTpl) {\n <table\n class=\"mlv-data-table__table mlv-data-table__table--virtual mlv-data-table__table--virtual-footer\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.flex]=\"resizedTableWidth() === null ? null : '0 0 auto'\"\n role=\"presentation\"\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <tfoot class=\"mlv-data-table__foot\">\n <tr class=\"mlv-data-table__row mlv-data-table__row--footer\">\n <ng-container\n [ngTemplateOutlet]=\"footerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleColumns() }\"\n />\n </tr>\n </tfoot>\n </table>\n }\n\n <!-- Extends only the outer horizontal scroll range to match the viewport's\n native range, including a classic vertical scrollbar gutter. -->\n <div\n class=\"mlv-data-table__virtual-horizontal-sizer\"\n [style.width.px]=\"_virtualHorizontalScrollExtent()\"\n aria-hidden=\"true\"\n ></div>\n } @else if (_cellNav()) {\n <!-- ========= Experimental cell-level grid navigation (aria ngGrid) ========= -->\n <!--\n Opt-in via [cellNavigation]=\"true\" (non-virtual only). The aria Grid pattern owns\n roving focus, arrow/Home/End navigation across cells, and aria-activedescendant.\n Only the data <tbody> rows/cells are registered as grid rows/cells; header cells\n stay outside grid navigation. Interactive cells (edit inputs, action buttons)\n expose an ngGridCellWidget so Enter pauses grid nav to interact and Escape resumes.\n -->\n <table\n ngGrid\n rowWrap=\"nowrap\"\n colWrap=\"nowrap\"\n class=\"mlv-data-table__table\"\n [class.mlv-data-table__table--striped]=\"striped()\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.table-layout]=\"resizedTableWidth() === null ? null : 'fixed'\"\n role=\"grid\"\n #tableEl\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <thead\n class=\"mlv-data-table__head\"\n [class.mlv-data-table__head--sticky]=\"stickyHeader()\"\n >\n <ng-container [ngTemplateOutlet]=\"headRowsTpl\" />\n </thead>\n\n <tbody class=\"mlv-data-table__body\">\n @if (showError()) {\n <ng-container [ngTemplateOutlet]=\"errorRowTpl\" />\n } @else if (flatRows().length === 0) {\n <ng-container [ngTemplateOutlet]=\"noDataRowTpl\" />\n } @else { @for (row of flatRows(); track $index; let i = $index) {\n <tr\n ngGridRow\n class=\"mlv-data-table__row mlv-data-table__row--data\"\n [class.mlv-data-table__row--expanded]=\"isRowExpanded(row)\"\n [class.mlv-data-table__row--editing]=\"isEditing(i)\"\n [class.mlv-data-table__row--selected]=\"isRowSelected(row)\"\n [attr.aria-level]=\"getDepth(row) > 0 ? getDepth(row) + 1 : null\"\n [attr.aria-selected]=\"selectable() ? isRowSelected(row) : null\"\n [attr.data-row-index]=\"i\"\n (click)=\"onRowClick(row, i, $event)\"\n >\n @if (selectable()) {\n <td\n ngGridCell\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n >\n <mlv-checkbox\n [checked]=\"isRowSelected(row)\"\n [tabbable]=\"false\"\n (checkedChange)=\"toggleRowSelection(row)\"\n (click)=\"$event.stopPropagation()\"\n [ariaLabel]=\"'Select row ' + (i + 1)\"\n />\n </td>\n } @for (col of visibleColumns(); track col.key; let ci = $index) {\n <td\n ngGridCell\n class=\"mlv-data-table__cell mlv-data-table__cell--data\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n >\n @if ( editable() && isEditing(i) && getEditTemplate(col.key); as\n editTpl ) {\n <div\n ngGridCellWidget\n widgetType=\"editable\"\n class=\"mlv-data-table__cell-content\"\n >\n <ng-container\n [ngTemplateOutlet]=\"editTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n </div>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"cellContentTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n index: i,\n col: col,\n ci: ci,\n }\"\n />\n }\n </td>\n } @if (editable()) {\n <td\n ngGridCell\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n >\n <div\n ngGridCellWidget\n widgetType=\"complex\"\n class=\"mlv-data-table__cell-content mlv-data-table__actions\"\n >\n <ng-container\n [ngTemplateOutlet]=\"actionsButtonsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n </div>\n </td>\n }\n </tr>\n } }\n </tbody>\n\n @if (getFooterTemplate(); as footerTpl) {\n <tfoot\n class=\"mlv-data-table__foot\"\n [class.mlv-data-table__foot--sticky]=\"stickyHeader()\"\n >\n <tr class=\"mlv-data-table__row mlv-data-table__row--footer\">\n <ng-container\n [ngTemplateOutlet]=\"footerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleColumns() }\"\n />\n </tr>\n </tfoot>\n }\n </table>\n\n @if (paginationMode() === 'infinite' && loading()) {\n <div\n class=\"mlv-data-table__loading-bottom\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <mlv-loader\n variant=\"circle\"\n [indeterminate]=\"true\"\n [size]=\"24\"\n ariaLabel=\"Loading more rows\"\n />\n </div>\n } } @else {\n <!-- ==================== Standard (non-virtual) mode ==================== -->\n <table\n class=\"mlv-data-table__table\"\n [class.mlv-data-table__table--striped]=\"striped()\"\n [class.mlv-data-table__table--bordered]=\"bordered()\"\n [style.width.px]=\"resizedTableWidth()\"\n [style.table-layout]=\"resizedTableWidth() === null ? null : 'fixed'\"\n role=\"grid\"\n #tableEl\n >\n <ng-container [ngTemplateOutlet]=\"colgroupTpl\" />\n <thead\n class=\"mlv-data-table__head\"\n [class.mlv-data-table__head--sticky]=\"stickyHeader()\"\n >\n <ng-container [ngTemplateOutlet]=\"headRowsTpl\" />\n </thead>\n\n <tbody class=\"mlv-data-table__body\">\n @if (showError()) {\n <ng-container [ngTemplateOutlet]=\"errorRowTpl\" />\n } @else if (flatRows().length === 0) {\n <ng-container [ngTemplateOutlet]=\"noDataRowTpl\" />\n } @else { @for (row of flatRows(); track $index; let i = $index) {\n <tr\n class=\"mlv-data-table__row mlv-data-table__row--data\"\n [class.mlv-data-table__row--expanded]=\"isRowExpanded(row)\"\n [class.mlv-data-table__row--editing]=\"isEditing(i)\"\n [class.mlv-data-table__row--selected]=\"isRowSelected(row)\"\n [attr.aria-level]=\"getDepth(row) > 0 ? getDepth(row) + 1 : null\"\n [attr.aria-selected]=\"selectable() ? isRowSelected(row) : null\"\n [attr.tabindex]=\"_rowTabIndex(i)\"\n [attr.data-row-index]=\"i\"\n (click)=\"onRowClick(row, i, $event)\"\n (focus)=\"_onRowFocus(i)\"\n (keydown)=\"_onRowKeydown(i, row, $event)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowCellsTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n </tr>\n } }\n </tbody>\n\n @if (getFooterTemplate(); as footerTpl) {\n <tfoot\n class=\"mlv-data-table__foot\"\n [class.mlv-data-table__foot--sticky]=\"stickyHeader()\"\n >\n <tr class=\"mlv-data-table__row mlv-data-table__row--footer\">\n <ng-container\n [ngTemplateOutlet]=\"footerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleColumns() }\"\n />\n </tr>\n </tfoot>\n }\n </table>\n\n @if (paginationMode() === 'infinite' && loading()) {\n <div\n class=\"mlv-data-table__loading-bottom\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n >\n <mlv-loader\n variant=\"circle\"\n [indeterminate]=\"true\"\n [size]=\"24\"\n ariaLabel=\"Loading more rows\"\n />\n </div>\n } }\n</div>\n\n@if (paginationMode() === 'paged' && !virtualScroll()) {\n<div class=\"mlv-data-table__footer\">\n <mlv-pagination\n [totalItems]=\"totalItems()\"\n [(currentPage)]=\"currentPage\"\n [(itemsPerPage)]=\"currentPerPage\"\n />\n</div>\n} @if (paginationMode() === 'paged' && loading()) {\n<div\n class=\"mlv-data-table__loading-overlay\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n>\n <mlv-loader\n variant=\"circle\"\n [indeterminate]=\"true\"\n [size]=\"48\"\n ariaLabel=\"Loading table data\"\n />\n</div>\n}\n\n<!-- ==================== Reusable templates ==================== -->\n\n<!--\n Shared <colgroup> template \u2014 used by both the standard table and the three split tables in\n virtual mode (header / body / footer). Keeping the colgroup definitions identical and combining\n it with `table-layout: fixed` keeps column widths in sync between the split tables in virtual mode.\n-->\n<ng-template #colgroupTpl>\n <colgroup>\n @if (selectable() === 'multi' || selectable() === 'single') {\n <col\n class=\"mlv-data-table__col--select\"\n [style.width]=\"getAuxiliaryColumnWidth('selection')\"\n />\n } @for (col of visibleColumns(); track col.key) {\n <col [style.width]=\"getColWidth(col)\" />\n } @if (editable()) {\n <col\n class=\"mlv-data-table__col--actions\"\n [style.width]=\"getAuxiliaryColumnWidth('actions')\"\n />\n }\n </colgroup>\n</ng-template>\n\n<!-- Header rows \u2014 emits the same content the original `<thead>` did. -->\n<ng-template #headRowsTpl>\n @if (hasColumnGroups()) {\n <!-- Group header row -->\n <tr class=\"mlv-data-table__row mlv-data-table__row--group-header\">\n @if (selectable() === 'multi') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n rowspan=\"2\"\n >\n <mlv-checkbox\n [checked]=\"allRowsSelected()\"\n [indeterminate]=\"someRowsSelected()\"\n (checkedChange)=\"toggleSelectAll()\"\n [ariaLabel]=\"_i18n().selectAllRows\"\n />\n </th>\n } @else if (selectable() === 'single') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n rowspan=\"2\"\n ></th>\n } @for (col of visibleColumns(); track col.key) { @if (headerLayout(); as\n layout) { @if (layout.standaloneKeys.has(col.key)) {\n <!-- Standalone column spans both header rows -->\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--standalone\"\n [class.mlv-data-table__cell--sortable]=\"col.sortable\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n [attr.data-mlv-column-key]=\"col.key\"\n rowspan=\"2\"\n scope=\"col\"\n [attr.aria-sort]=\"\n col.sortable\n ? getSortDirection(col) === 'asc'\n ? 'ascending'\n : getSortDirection(col) === 'desc'\n ? 'descending'\n : 'none'\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"headerCellInnerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n </th>\n } @else {\n <!-- Check if this column is the first column of a group -->\n @for (group of columnGroups(); track group.title) { @if (group.columns[0]\n === col.key) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--group-header\"\n [class.mlv-data-table__cell--align-left]=\"\n group.align === 'left'\n \"\n [class.mlv-data-table__cell--align-center]=\"\n group.align === 'center' || !group.align\n \"\n [class.mlv-data-table__cell--align-right]=\"\n group.align === 'right'\n \"\n [attr.colspan]=\"group.columns.length\"\n scope=\"colgroup\"\n >\n {{ group.title }}\n </th>\n } } } } } @if (editable()) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n rowspan=\"2\"\n >\n <span class=\"mlv-data-table__visually-hidden\"\n >{{ actionsHeaderLabel() }}</span\n >\n </th>\n }\n </tr>\n <!-- Sub-header row for grouped columns -->\n <tr class=\"mlv-data-table__row mlv-data-table__row--header\">\n @for (col of visibleColumns(); track col.key) { @if (headerLayout(); as\n layout) { @if (layout.groupedKeys.has(col.key)) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header\"\n [class.mlv-data-table__cell--sortable]=\"col.sortable\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n [attr.data-mlv-column-key]=\"col.key\"\n scope=\"col\"\n [attr.aria-sort]=\"\n col.sortable\n ? getSortDirection(col) === 'asc'\n ? 'ascending'\n : getSortDirection(col) === 'desc'\n ? 'descending'\n : 'none'\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"headerCellInnerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n </th>\n } } }\n </tr>\n } @else {\n <!-- Standard single-row header -->\n <tr class=\"mlv-data-table__row mlv-data-table__row--header\">\n @if (selectable() === 'multi') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n >\n <mlv-checkbox\n [checked]=\"allRowsSelected()\"\n [indeterminate]=\"someRowsSelected()\"\n (checkedChange)=\"toggleSelectAll()\"\n [ariaLabel]=\"_i18n().selectAllRows\"\n />\n </th>\n } @else if (selectable() === 'single') {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n ></th>\n } @for (col of visibleColumns(); track col.key) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header\"\n [class.mlv-data-table__cell--sortable]=\"col.sortable\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n [attr.data-mlv-column-key]=\"col.key\"\n [attr.aria-sort]=\"\n col.sortable\n ? getSortDirection(col) === 'asc'\n ? 'ascending'\n : getSortDirection(col) === 'desc'\n ? 'descending'\n : 'none'\n : null\n \"\n scope=\"col\"\n >\n <ng-container\n [ngTemplateOutlet]=\"headerCellInnerTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n </th>\n } @if (editable()) {\n <th\n class=\"mlv-data-table__cell mlv-data-table__cell--header mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n >\n <span class=\"mlv-data-table__visually-hidden\"\n >{{ actionsHeaderLabel() }}</span\n >\n </th>\n }\n </tr>\n }\n</ng-template>\n\n<!--\n Shared inner content for a single header cell. Context: { $implicit: col }.\n Sortable columns wrap the label + sort icon in ONE native <button> (APG grid\n sort pattern) so the <th> stays a non-interactive cell carrying only aria-sort;\n the pin button is a sibling of the sort button, never nested inside it, so no\n interactive control is nested within another (avoids axe `nested-interactive`).\n-->\n<ng-template #headerCellInnerTpl let-col>\n <div class=\"mlv-data-table__cell-inner\">\n @if (col.sortable) {\n <button\n type=\"button\"\n class=\"mlv-data-table__sort-button\"\n [attr.aria-label]=\"getSortLabel(col)\"\n (click)=\"onSortClick(col)\"\n >\n <span class=\"mlv-data-table__header-title\"\n >{{ col.title ?? col.key }}</span\n >\n <span class=\"mlv-data-table__sort-icon\" aria-hidden=\"true\">\n @if (getSortDirection(col) === 'asc') {\n <svg lucideArrowUp />\n } @else if (getSortDirection(col) === 'desc') {\n <svg lucideArrowDown />\n } @else {\n <svg lucideChevronsUpDown class=\"mlv-data-table__sort-icon--inactive\" />\n }\n </span>\n </button>\n } @else {\n <span class=\"mlv-data-table__header-title\">{{ col.title ?? col.key }}</span>\n } @if (col.pinnable) {\n <ng-container\n [ngTemplateOutlet]=\"pinButtonTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col }\"\n />\n } @if (filterDisplay() === 'column' && col.filterable) {\n <button\n mlvButton\n class=\"mlv-data-table__column-filter\"\n [class.mlv-data-table__column-filter--active]=\"\n isColumnFiltered(col.key)\n \"\n [variant]=\"\n columnFilterPopup.opened() || isColumnFiltered(col.key)\n ? 'primary'\n : 'transparent'\n \"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n [attr.data-column-key]=\"col.key\"\n [mlvPopupTrigger]=\"columnFilterPopup\"\n #columnFilterTrigger=\"mlvPopupTrigger\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"getFilterLabel(col)\"\n >\n <svg lucideFunnel mlvButtonIcon [size]=\"14\" />\n </button>\n\n <mlv-popup\n #columnFilterPopup\n panelRole=\"dialog\"\n [ariaLabel]=\"getFilterLabel(col)\"\n (afterClosed)=\"focusColumnFilterTrigger(col.key)\"\n >\n <mlv-dt-filter-dropdown\n *mlvPopupContent\n [columns]=\"getColumnFilterColumns(col)\"\n [activeFilters]=\"activeFilters()\"\n (activeFiltersChange)=\"onColumnFiltersChange(col.key, $event)\"\n (closed)=\"columnFilterTrigger.close()\"\n />\n </mlv-popup>\n }\n </div>\n @if (col.resizable) {\n <div\n #resizeHandle\n class=\"mlv-data-table__resize-handle\"\n [attr.data-column-key]=\"col.key\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"getColumnResizeLabel(col)\"\n [attr.aria-valuenow]=\"getColumnResizeValue(col)\"\n [attr.aria-valuemin]=\"getColumnResizeMin(col)\"\n [attr.aria-valuemax]=\"getColumnResizeMax(col)\"\n [attr.aria-valuetext]=\"getColumnResizeValueText(col)\"\n (pointerdown)=\"onResizeStart(col, $event)\"\n (keydown)=\"onColumnResizeKeydown(col, $event)\"\n (dblclick)=\"resetColumnWidth(col.key)\"\n ></div>\n }\n</ng-template>\n\n<!-- No-data fallback row, used by both rendering paths. -->\n<ng-template #noDataRowTpl>\n <tr class=\"mlv-data-table__row mlv-data-table__row--no-data\">\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--no-data\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n @if (getNoDataTemplate()) {\n <ng-container [ngTemplateOutlet]=\"getNoDataTemplate()!\" />\n } @else {\n <div class=\"mlv-data-table__no-data-default\">\n <svg lucideTable2 [size]=\"40\" aria-hidden=\"true\" />\n <span>No data available</span>\n </div>\n }\n </td>\n </tr>\n</ng-template>\n\n<!--\n Failed-request row, used by all three rendering paths. Takes precedence over both the\n data rows and #noDataRowTpl so a failed request never reads as an empty result set.\n The region is announced as an alert; the default block is an mlv-empty-state with a\n danger-toned icon and a Retry button wired to the (retry) output.\n-->\n<ng-template #errorRowTpl>\n <tr class=\"mlv-data-table__row mlv-data-table__row--error\">\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--error\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <div class=\"mlv-data-table__error\" role=\"alert\" aria-live=\"assertive\">\n @if (getErrorTemplate(); as errorTpl) {\n <ng-container\n [ngTemplateOutlet]=\"errorTpl\"\n [ngTemplateOutletContext]=\"_errorContext()\"\n />\n } @else {\n <mlv-empty-state>\n <svg\n mlvEmptyStateIcon\n lucideTriangleAlert\n class=\"mlv-data-table__error-icon\"\n [size]=\"40\"\n aria-hidden=\"true\"\n />\n <span mlvEmptyStateTitle>{{ _i18n().errorTitle }}</span>\n <span mlvEmptyStateDescription>{{ errorMessage() }}</span>\n <button\n mlvEmptyStateActions\n mlvButton\n variant=\"secondary\"\n type=\"button\"\n class=\"mlv-data-table__error-retry\"\n (click)=\"onRetry()\"\n >\n {{ _i18n().retry }}\n </button>\n </mlv-empty-state>\n }\n </div>\n </td>\n </tr>\n</ng-template>\n\n<!--\n Per-row data cells. Emits the selection cell, the data cells, and the actions cell.\n The owning <tr> stays at the consumer site (either inside `@for` for the standard mode\n or `*cdkVirtualFor` inside the CDK viewport in virtual mode), so each rendering path can\n control its own row-level host bindings.\n-->\n<ng-template #rowCellsTpl let-row let-i=\"index\">\n @if (selectable()) {\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--select\"\n [style]=\"getAuxiliaryColumnStyle('selection')\"\n >\n <!--\n [tabbable]=\"false\" keeps this checkbox out of the tab order so the row is\n the single tab stop: the row's Space/Enter toggles selection, and the\n checkbox stays operable via the mouse. Avoids a per-row double tab stop.\n -->\n <mlv-checkbox\n [checked]=\"isRowSelected(row)\"\n [tabbable]=\"false\"\n (checkedChange)=\"toggleRowSelection(row)\"\n (click)=\"$event.stopPropagation()\"\n [ariaLabel]=\"'Select row ' + (i + 1)\"\n />\n </td>\n } @for (col of visibleColumns(); track col.key; let ci = $index) {\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--data\"\n [class.mlv-data-table__cell--pinned]=\"col.pinned\"\n [class.mlv-data-table__cell--pinned-left]=\"\n col.pinned && col.pinSide !== 'right'\n \"\n [class.mlv-data-table__cell--pinned-right]=\"\n col.pinned && col.pinSide === 'right'\n \"\n [class]=\"getAlignClass(col)\"\n [style]=\"getCellStyle(col)\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cellContentTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n index: i,\n col: col,\n ci: ci,\n }\"\n />\n </td>\n } @if (editable()) {\n <td\n class=\"mlv-data-table__cell mlv-data-table__cell--data mlv-data-table__cell--actions\"\n [style]=\"getAuxiliaryColumnStyle('actions')\"\n >\n <div class=\"mlv-data-table__cell-content mlv-data-table__actions\">\n <ng-container\n [ngTemplateOutlet]=\"actionsButtonsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n </div>\n </td>\n }\n</ng-template>\n\n<!--\n Shared edit/save/cancel action buttons for the actions column. Context: { $implicit: row, index: i }.\n-->\n<ng-template #actionsButtonsTpl let-row let-i=\"index\">\n @if (isEditing(i)) {\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n (click)=\"saveEdit(row, i)\"\n [attr.aria-label]=\"_i18n().saveChanges\"\n >\n <svg lucideCheck mlvButtonIcon />\n </button>\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n (click)=\"cancelEdit(row, i)\"\n [attr.aria-label]=\"_i18n().cancelEditing\"\n >\n <svg lucideX mlvButtonIcon />\n </button>\n } @else {\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n (click)=\"startEdit(row, i)\"\n [attr.aria-label]=\"_i18n().editRow\"\n >\n <svg lucidePencil mlvButtonIcon />\n </button>\n }\n</ng-template>\n\n<!--\n Shared inner content of a data cell (indent, expand button, edit/display template, raw value).\n Extracted so both the standard `#rowCellsTpl` `<td>` and the experimental cell-navigation grid\n `<td ngGridCell>` render identical content. Purely presentational \u2014 contains no aria grid\n directives \u2014 so it is safe to project via ngTemplateOutlet across the grid-cell boundary.\n Context: { $implicit: row, index: i, col, ci }.\n-->\n<ng-template #cellContentTpl let-row let-i=\"index\" let-col=\"col\" let-ci=\"ci\">\n <div class=\"mlv-data-table__cell-content\">\n @if (ci === 0 && getDepth(row) > 0) {\n <span\n class=\"mlv-data-table__indent\"\n [style.padding-left.rem]=\"getDepth(row) * 1.25\"\n aria-hidden=\"true\"\n ></span>\n } @if (ci === 0 && hasChildren(row)) {\n <button\n mlvButton\n variant=\"transparent\"\n shape=\"square\"\n mlvDensity=\"tight\"\n type=\"button\"\n class=\"mlv-data-table__expand-btn\"\n (click)=\"toggleExpand(row, $event)\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n [attr.aria-label]=\"\n isRowExpanded(row) ? _i18n().collapseRow : _i18n().expandRow\n \"\n >\n <svg\n lucideChevronRight\n mlvButtonIcon\n class=\"mlv-data-table__expand-icon\"\n [class.mlv-data-table__expand-icon--open]=\"isRowExpanded(row)\"\n />\n </button>\n } @if (editable() && isEditing(i) && getEditTemplate(col.key); as editTpl) {\n <ng-container\n [ngTemplateOutlet]=\"editTpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n } @else if (getTemplate(col.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"getCellContext(row, i)\"\n />\n } @else {\n <!--\n Default cell renderer. `valueLabels` (or the `filterConfig.options` fallback)\n maps the raw value to a label; `tone` promotes the cell to a muted badge.\n -->\n @if (getCellTone(row, col); as tone) {\n <mlv-badge [tone]=\"tone\" muted\n >{{ getCellDisplayValue(row, col) }}</mlv-badge\n >\n } @else { {{ getCellDisplayValue(row, col) }} } }\n </div>\n</ng-template>\n\n<!-- Reusable pin button template. Rendered via ngTemplateOutlet from every header cell that supports pinning. -->\n<ng-template #pinButtonTpl let-col>\n @if (getPinnableSides(col); as sides) { @if (sides.length > 1) {\n <button\n class=\"mlv-data-table__pin-btn\"\n [class.mlv-data-table__pin-btn--active]=\"isColumnPinned(col)\"\n type=\"button\"\n [mlvPopupTrigger]=\"pinPopup\"\n #pinTrigger=\"mlvPopupTrigger\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"\n isColumnPinned(col) ? _i18n().changePinSide : _i18n().pinColumn\n \"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"pinPopup.opened()\"\n [attr.aria-pressed]=\"isColumnPinned(col)\"\n >\n @if (isColumnPinned(col)) {\n <svg lucidePinOff />\n } @else {\n <svg lucidePin />\n }\n </button>\n <mlv-popup #pinPopup>\n <div *mlvPopupContent class=\"mlv-data-table__pin-popup\" role=\"menu\">\n @for (side of sides; track side) {\n <button\n type=\"button\"\n role=\"menuitemradio\"\n class=\"mlv-data-table__pin-popup-item\"\n [class.mlv-data-table__pin-popup-item--active]=\"\n getColumnPinSide(col) === side\n \"\n [attr.aria-checked]=\"getColumnPinSide(col) === side\"\n (click)=\"\n pinTo(col, side); pinTrigger.close(); $event.stopPropagation()\n \"\n >\n <svg lucidePin [size]=\"14\" />\n @if (side === 'left') {\n <span>Pin left</span>\n } @else {\n <span>Pin right</span>\n }\n </button>\n } @if (isColumnPinned(col)) {\n <button\n type=\"button\"\n role=\"menuitem\"\n class=\"mlv-data-table__pin-popup-item mlv-data-table__pin-popup-item--unpin\"\n (click)=\"unpin(col); pinTrigger.close(); $event.stopPropagation()\"\n >\n <svg lucidePinOff [size]=\"14\" />\n <span>{{ _i18n().unpin }}</span>\n </button>\n }\n </div>\n </mlv-popup>\n } @else {\n <button\n class=\"mlv-data-table__pin-btn\"\n [class.mlv-data-table__pin-btn--active]=\"isColumnPinned(col)\"\n type=\"button\"\n (click)=\"togglePin(col, sides[0]); $event.stopPropagation()\"\n [attr.aria-label]=\"\n isColumnPinned(col) ? _i18n().unpinColumn : _i18n().pinColumn\n \"\n [attr.aria-pressed]=\"isColumnPinned(col)\"\n >\n @if (isColumnPinned(col)) {\n <svg lucidePinOff />\n } @else {\n <svg lucidePin />\n }\n </button>\n } }\n</ng-template>\n", styles: [".mlv-data-table{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-dt-font-size)}.mlv-data-table *{box-sizing:border-box}.mlv-data-table{--mlv-dt-row-height: 3.5rem;--mlv-dt-cell-padding-x: 1.25rem;--mlv-dt-header-height: 3rem;--mlv-dt-font-size: var(--mlv-font-size-m);--mlv-dt-header-font-size: var(--mlv-font-size-m);--mlv-dt-icon-size: 1rem;--mlv-dt-toolbar-gap: .5rem;--mlv-dt-toolbar-padding: .75rem 0;display:flex;flex-direction:column;width:100%;position:relative;color:var(--mlv-text-primary)}[class*=--tight] .mlv-data-table:not([class*=--compact],[class*=--comfortable],[class*=--spacious],[class*=--airy]),.mlv-data-table[class*=--tight]{--mlv-dt-row-height: 2.5rem;--mlv-dt-cell-padding-x: .75rem;--mlv-dt-header-height: 2.25rem;--mlv-dt-font-size: var(--mlv-font-size-s);--mlv-dt-header-font-size: var(--mlv-font-size-s);--mlv-dt-icon-size: .75rem;--mlv-dt-toolbar-gap: .25rem;--mlv-dt-toolbar-padding: .375rem 0}[class*=--compact] .mlv-data-table:not([class*=--tight],[class*=--comfortable],[class*=--spacious],[class*=--airy]),.mlv-data-table[class*=--compact]{--mlv-dt-row-height: 3rem;--mlv-dt-cell-padding-x: 1rem;--mlv-dt-header-height: 2.5rem;--mlv-dt-font-size: var(--mlv-font-size-s);--mlv-dt-header-font-size: var(--mlv-font-size-s);--mlv-dt-icon-size: .875rem;--mlv-dt-toolbar-gap: .375rem;--mlv-dt-toolbar-padding: .5rem 0}[class*=--spacious] .mlv-data-table:not([class*=--tight],[class*=--compact],[class*=--comfortable],[class*=--airy]),.mlv-data-table[class*=--spacious]{--mlv-dt-row-height: 4.25rem;--mlv-dt-cell-padding-x: 1.5rem;--mlv-dt-header-height: 3.25rem;--mlv-dt-font-size: var(--mlv-font-size-l);--mlv-dt-header-font-size: var(--mlv-font-size-l);--mlv-dt-icon-size: 1.125rem;--mlv-dt-toolbar-gap: .75rem;--mlv-dt-toolbar-padding: .75rem 0}[class*=--airy] .mlv-data-table:not([class*=--tight],[class*=--compact],[class*=--comfortable],[class*=--spacious]),.mlv-data-table[class*=--airy]{--mlv-dt-row-height: 5rem;--mlv-dt-cell-padding-x: 1.75rem;--mlv-dt-header-height: 3.75rem;--mlv-dt-font-size: var(--mlv-font-size-l);--mlv-dt-header-font-size: var(--mlv-font-size-l);--mlv-dt-icon-size: 1.25rem;--mlv-dt-toolbar-gap: 1rem;--mlv-dt-toolbar-padding: 1rem 0}.mlv-data-table__toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--mlv-dt-toolbar-gap);padding:var(--mlv-dt-toolbar-padding);position:relative}.mlv-data-table__search{flex:1 1 16rem;width:min(100%,24rem);max-width:24rem}.mlv-data-table__toolbar-actions{display:flex;gap:var(--mlv-dt-toolbar-gap);margin-left:auto}.mlv-data-table__filter-badge{font-size:var(--mlv-dt-font-size);color:var(--mlv-text-secondary)}.mlv-data-table__wrapper{width:100%;overflow-x:auto;overflow-y:auto;position:relative}.mlv-data-table__wrapper--virtual{overflow-y:visible;display:flex;flex-direction:column}.mlv-data-table__virtual-viewport{display:block;width:100%;min-width:0;max-width:100%;flex:0 0 auto;position:sticky;left:0;border-bottom:.0625rem solid var(--mlv-border-subtle)}.mlv-data-table__virtual-viewport.cdk-virtual-scrollable{overflow-x:hidden;overflow-y:auto}.mlv-data-table__virtual-horizontal-sizer{width:0;height:0;min-height:0;flex:0 0 0;align-self:flex-start;pointer-events:none}.mlv-data-table__table--virtual-header,.mlv-data-table__table--virtual-footer{--mlv-dt-pinned-right-correction: var( --mlv-dt-virtual-scrollbar-gutter, 0px )}.mlv-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--mlv-background-base) 70%,transparent);-webkit-backdrop-filter:blur(.0625rem);backdrop-filter:blur(.0625rem);z-index:20;pointer-events:all}.mlv-data-table__loading-bottom{display:flex;align-items:center;justify-content:center;padding:1rem 0;color:var(--mlv-text-secondary)}.mlv-data-table__table{width:100%;border-collapse:collapse;table-layout:auto;font-size:var(--mlv-dt-font-size);color:var(--mlv-text-primary)}.mlv-data-table__table--striped .mlv-data-table__row--data:nth-child(2n){background:var(--mlv-background-sunken)}.mlv-data-table__table--striped .mlv-data-table__row--data:nth-child(2n) .mlv-data-table__cell--pinned{background:var(--mlv-background-sunken)}.mlv-data-table__table--bordered .mlv-data-table__cell{border-right:.0625rem solid var(--mlv-border-subtle)}.mlv-data-table__table--bordered .mlv-data-table__cell:last-child{border-right:none}.mlv-data-table__table--virtual{table-layout:fixed}.mlv-data-table__head--sticky{position:sticky;top:var(--mlv-dt-sticky-top, 0);z-index:3;background:var(--mlv-background-base)}.mlv-data-table__head--sticky .mlv-data-table__cell--pinned{z-index:4}.mlv-data-table__cell{padding:0;border-bottom:.0625rem solid var(--mlv-border-subtle);vertical-align:middle;background:inherit}.mlv-data-table__cell-content{display:flex;align-items:center;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.mlv-data-table__cell--header{font-weight:var(--mlv-font-weight-medium);font-size:var(--mlv-dt-header-font-size);color:color-mix(in srgb,var(--mlv-text-action) 65%,var(--mlv-text-secondary));position:relative;-webkit-user-select:none;user-select:none;border-bottom:.0625rem solid var(--mlv-border-normal)}.mlv-data-table__cell--sortable{cursor:pointer}.mlv-data-table__cell--sortable:hover .mlv-data-table__cell-inner{color:var(--mlv-text-action)}.mlv-data-table__cell--data{padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-row-height);font-size:var(--mlv-dt-font-size)}.mlv-data-table__cell--pinned{background:var(--mlv-background-base);z-index:1;transition:background var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__cell--pinned:after{content:\"\";position:absolute;top:0;bottom:0;width:.5rem;pointer-events:none}.mlv-data-table__cell--pinned-left:after{right:-.5rem;background:linear-gradient(to right,rgba(0,0,0,.04),transparent)}.mlv-data-table__cell--pinned-right:after{left:-.5rem;background:linear-gradient(to left,rgba(0,0,0,.04),transparent)}.mlv-data-table__cell--no-data,.mlv-data-table__cell--error{padding:3rem var(--mlv-dt-cell-padding-x);text-align:center}.mlv-data-table__cell--align-left{text-align:left}.mlv-data-table__cell--align-center{text-align:center}.mlv-data-table__cell--align-right{text-align:right}.mlv-data-table__cell-inner{display:flex;align-items:center;gap:.375rem;padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-header-height);transition:color var(--mlv-duration-normal) var(--mlv-ease-default);border-radius:var(--mlv-radius-s)}.mlv-data-table__sort-button{display:inline-flex;align-items:center;gap:.375rem;flex:1;min-width:0;margin:0;padding:0;border:none;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;border-radius:var(--mlv-radius-s)}.mlv-data-table__sort-button:focus-visible{outline:var(--mlv-stroke-width) solid var(--mlv-border-focus);outline-offset:.125rem}.mlv-data-table__visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}.mlv-data-table__header-title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}.mlv-data-table__column-filter{flex:0 0 auto}.mlv-data-table__column-filter--active{color:var(--mlv-text-action)}.mlv-data-table__sort-icon{display:inline-flex;flex-shrink:0;color:var(--mlv-text-action)}.mlv-data-table__sort-icon svg{width:var(--mlv-dt-icon-size);height:var(--mlv-dt-icon-size)}.mlv-data-table__sort-icon--inactive{opacity:0;transition:opacity var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__cell--sortable:hover .mlv-data-table__sort-icon--inactive{opacity:.5}.mlv-data-table__pin-btn{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;padding:0;border:none;background:none;cursor:pointer;color:var(--mlv-text-tertiary);opacity:0;transition:opacity var(--mlv-duration-normal) var(--mlv-ease-default),color var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__pin-btn svg{width:var(--mlv-dt-icon-size);height:var(--mlv-dt-icon-size)}.mlv-data-table__cell--header:hover .mlv-data-table__pin-btn{opacity:.6}.mlv-data-table__pin-btn:hover{opacity:1!important;color:var(--mlv-text-action)}.mlv-data-table__pin-btn--active{opacity:1;color:var(--mlv-text-action)}.mlv-data-table__cell--header:hover .mlv-data-table__pin-btn--active{opacity:1}.mlv-data-table__resize-handle{position:absolute;top:0;right:-.5rem;width:1rem;height:100%;cursor:col-resize;background:transparent;z-index:5;touch-action:none;-webkit-user-select:none;user-select:none}.mlv-data-table__resize-handle:after{content:\"\";position:absolute;top:.75rem;bottom:.75rem;left:50%;width:.0625rem;border-radius:var(--mlv-radius-full);background-color:var(--mlv-text-secondary);transform:translate(-50%);transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),width var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-data-table__resize-handle:hover:after{width:.125rem;background-color:var(--mlv-text-primary)}.mlv-data-table__resize-handle:active:after,.mlv-data-table__resize-handle:focus-visible:after{width:.125rem;background-color:var(--mlv-background-accent-1)}.mlv-data-table__resize-handle:focus-visible{outline:var(--mlv-stroke-width) solid var(--mlv-border-focus);outline-offset:-.125rem}@media(pointer:coarse){.mlv-data-table__resize-handle{right:-.75rem;width:1.5rem}}.mlv-data-table__row{transition:background var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__row--data:hover,.mlv-data-table__row--data:hover .mlv-data-table__cell--pinned,.mlv-data-table__row--expanded,.mlv-data-table__row--expanded .mlv-data-table__cell--pinned{background:var(--mlv-background-sunken)}.mlv-data-table__indent{display:inline-block}.mlv-data-table__expand-btn{margin-right:.25rem;vertical-align:middle;flex-shrink:0}.mlv-data-table__expand-icon{width:var(--mlv-dt-icon-size);height:var(--mlv-dt-icon-size);transition:transform var(--mlv-duration-normal) var(--mlv-ease-default)}.mlv-data-table__expand-icon--open{transform:rotate(90deg)}.mlv-data-table__no-data-default{display:flex;flex-direction:column;align-items:center;gap:.75rem;padding:3rem 0;color:var(--mlv-text-tertiary)}.mlv-data-table__no-data-default svg{opacity:.2}.mlv-data-table__no-data-default span{font-size:var(--mlv-dt-font-size)}.mlv-data-table__error{display:block;text-align:initial}.mlv-data-table__error-icon{color:var(--mlv-text-negative)}.mlv-data-table__row--group-header .mlv-data-table__cell--group-header{font-weight:var(--mlv-font-weight-medium);font-size:var(--mlv-dt-header-font-size);color:color-mix(in srgb,var(--mlv-text-action) 65%,var(--mlv-text-secondary));text-align:center;padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-header-height);border-bottom:.0625rem solid var(--mlv-border-subtle);vertical-align:middle}.mlv-data-table__row--group-header .mlv-data-table__cell--standalone{vertical-align:middle}.mlv-data-table__cell--actions{width:1%;white-space:nowrap;padding:0 var(--mlv-dt-cell-padding-x)!important}.mlv-data-table__actions{display:flex;align-items:center;gap:.25rem;justify-content:flex-end}.mlv-data-table__row--editing,.mlv-data-table__row--editing .mlv-data-table__cell--pinned{background:var(--mlv-background-sunken)}.mlv-data-table__cell--select{width:2.5rem;min-width:2.5rem;max-width:2.5rem;padding:0 .5rem!important;text-align:center}.mlv-data-table__col--select{width:2.5rem}.mlv-data-table__col--actions{width:5rem}.mlv-data-table__row--selected,.mlv-data-table__row--selected .mlv-data-table__cell--pinned{background:color-mix(in srgb,var(--mlv-background-accent-1) 8%,var(--mlv-background-base))}.mlv-data-table__pin-popup{display:flex;flex-direction:column;gap:.125rem;padding:.25rem;min-width:8rem}.mlv-data-table__pin-popup-item{display:flex;align-items:center;gap:.5rem;padding:.375rem .5rem;border:none;background:none;border-radius:var(--mlv-radius-s);cursor:pointer;font:inherit;font-size:var(--mlv-dt-font-size);color:var(--mlv-text-primary);text-align:left}.mlv-data-table__pin-popup-item svg{color:var(--mlv-text-secondary);flex-shrink:0}.mlv-data-table__pin-popup-item:hover{background:var(--mlv-background-sunken)}.mlv-data-table__pin-popup-item--active{color:var(--mlv-text-action)}.mlv-data-table__pin-popup-item--active svg{color:var(--mlv-text-action)}.mlv-data-table__pin-popup-item--unpin{color:var(--mlv-text-negative);border-top:.0625rem solid var(--mlv-border-subtle);margin-top:.125rem;padding-top:.5rem;border-radius:0 0 var(--mlv-radius-s) var(--mlv-radius-s)}.mlv-data-table__pin-popup-item--unpin svg{color:var(--mlv-text-negative)}.mlv-data-table__pin-popup-item--unpin:hover{background:color-mix(in srgb,var(--mlv-text-negative) 8%,transparent)}.mlv-data-table__columns-dropdown{display:flex;flex-direction:column;gap:.25rem;padding:.5rem;min-width:10rem}.mlv-data-table__columns-item{display:flex;align-items:center;gap:.5rem;padding:.25rem .5rem;border-radius:var(--mlv-radius-s);cursor:pointer;font-size:var(--mlv-dt-font-size)}.mlv-data-table__columns-item:hover{background:var(--mlv-background-sunken)}.mlv-data-table__foot{font-weight:var(--mlv-font-weight-medium);font-size:var(--mlv-dt-font-size);background:var(--mlv-background-base)}.mlv-data-table__foot--sticky{position:sticky;bottom:0;z-index:3}.mlv-data-table__foot .mlv-data-table__cell--pinned,.mlv-data-table__row--footer{background:inherit}.mlv-data-table__row--footer>:where(td,th){box-sizing:border-box;padding:0 var(--mlv-dt-cell-padding-x);height:var(--mlv-dt-header-height);border-top:.125rem solid var(--mlv-border-normal);border-bottom:0;vertical-align:middle;background:inherit;font-size:inherit;font-weight:inherit}.mlv-data-table__table--bordered .mlv-data-table__row--footer>:where(td,th){border-right:.0625rem solid var(--mlv-border-subtle)}.mlv-data-table__table--bordered .mlv-data-table__row--footer>:where(td,th):last-child{border-right:none}.mlv-data-table__footer{padding-top:.75rem}@media(prefers-reduced-motion:reduce){.mlv-data-table,.mlv-data-table *,.mlv-data-table:before,.mlv-data-table:after,[class^=mlv-data-table__],[class*=\" mlv-data-table__\"],[class^=mlv-data-table--],[class*=\" mlv-data-table--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}\n"] }]
|
|
1833
|
+
}], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], columnGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnGroups", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], editMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "editMode", required: false }] }], addRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "addRow", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], actionsHeaderLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsHeaderLabel", required: false }] }], cellNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellNavigation", required: false }] }], paginationMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationMode", required: false }] }], hasMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMore", required: false }] }], infiniteScrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "infiniteScrollThreshold", required: false }] }], filterDisplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterDisplay", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], searchTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTrigger", required: false }] }], searchDebounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchDebounce", required: false }] }], rowEditStart: [{ type: i0.Output, args: ["rowEditStart"] }], rowEditSave: [{ type: i0.Output, args: ["rowEditSave"] }], rowEditCancel: [{ type: i0.Output, args: ["rowEditCancel"] }], rowAdd: [{ type: i0.Output, args: ["rowAdd"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], retry: [{ type: i0.Output, args: ["retry"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], loadMore: [{ type: i0.Output, args: ["loadMore"] }], selectedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedRows", required: false }] }, { type: i0.Output, args: ["selectedRowsChange"] }], searchQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchQuery", required: false }] }, { type: i0.Output, args: ["searchQueryChange"] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvDataTableCell), { ...{
|
|
1834
|
+
descendants: true,
|
|
1835
|
+
}, isSignal: true }] }], editCellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvDataTableEditCell), { ...{
|
|
1836
|
+
descendants: true,
|
|
1837
|
+
}, isSignal: true }] }], noDataTemplate: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvDataTableNoData), { ...{
|
|
1838
|
+
descendants: true,
|
|
1839
|
+
}, isSignal: true }] }], errorTemplate: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvDataTableError), { ...{
|
|
1840
|
+
descendants: true,
|
|
1841
|
+
}, isSignal: true }] }], footerTemplate: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvDataTableFooter), { ...{
|
|
1842
|
+
descendants: true,
|
|
1843
|
+
}, isSignal: true }] }], tableWrapperRef: [{ type: i0.ViewChild, args: ['tableWrapper', { isSignal: true }] }], tableRef: [{ type: i0.ViewChild, args: ['tableEl', { isSignal: true }] }], _resizeHandleRefs: [{ type: i0.ViewChildren, args: ['resizeHandle', { isSignal: true }] }], _filterTriggerRef: [{ type: i0.ViewChild, args: ['filterBtn', { ...{
|
|
1844
|
+
read: ElementRef,
|
|
1845
|
+
}, isSignal: true }] }], _virtualViewportRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }], activeFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeFilters", required: false }] }, { type: i0.Output, args: ["activeFiltersChange"] }] } });
|
|
1846
|
+
//# sourceMappingURL=data-table.js.map
|