ngwr 12.0.0 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -20
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs +3 -3
- package/fesm2022/ngwr-alert.mjs +3 -3
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs +3 -3
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs +3 -3
- package/fesm2022/ngwr-badge.mjs +6 -6
- package/fesm2022/ngwr-bar-chart.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs +3 -3
- package/fesm2022/ngwr-border-glow.mjs +3 -3
- package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-button.mjs +6 -6
- package/fesm2022/ngwr-calendar-heatmap.mjs +3 -3
- package/fesm2022/ngwr-calendar.mjs +3 -3
- package/fesm2022/ngwr-card.mjs +9 -9
- package/fesm2022/ngwr-carousel.mjs +6 -6
- package/fesm2022/ngwr-cascader.mjs +3 -3
- package/fesm2022/ngwr-checkbox.mjs +6 -6
- package/fesm2022/ngwr-circular-text.mjs +3 -3
- package/fesm2022/ngwr-click-spark.mjs +3 -3
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-color-picker.mjs +6 -6
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +3 -3
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs +3 -3
- package/fesm2022/ngwr-context-menu.mjs +12 -12
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-counter.mjs +6 -6
- package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
- package/fesm2022/ngwr-date-picker.mjs +18 -18
- package/fesm2022/ngwr-date.mjs +3 -3
- package/fesm2022/ngwr-decrypt-text.mjs +3 -3
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-dialog.mjs +15 -15
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-divider.mjs +3 -3
- package/fesm2022/ngwr-donut-chart.mjs +3 -3
- package/fesm2022/ngwr-drag-drop.mjs +6 -6
- package/fesm2022/ngwr-drawer.mjs +18 -18
- package/fesm2022/ngwr-dropdown.mjs +9 -9
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-event-calendar.mjs +6 -6
- package/fesm2022/ngwr-falling-text.mjs +3 -3
- package/fesm2022/ngwr-file-upload.mjs +3 -3
- package/fesm2022/ngwr-form.mjs +9 -9
- package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
- package/fesm2022/ngwr-gauge.mjs +3 -3
- package/fesm2022/ngwr-glitch-text.mjs +3 -3
- package/fesm2022/ngwr-gradient-text.mjs +3 -3
- package/fesm2022/ngwr-hotkey.mjs +6 -6
- package/fesm2022/ngwr-i18n.mjs +9 -9
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-image-cropper.mjs +3 -3
- package/fesm2022/ngwr-input-number.mjs +3 -3
- package/fesm2022/ngwr-input-otp.mjs +3 -3
- package/fesm2022/ngwr-input.mjs +15 -15
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-knob.mjs +3 -3
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-lightbox.mjs +3 -3
- package/fesm2022/ngwr-line-chart.mjs +3 -3
- package/fesm2022/ngwr-list.mjs +6 -6
- package/fesm2022/ngwr-loading-bar.mjs +6 -6
- package/fesm2022/ngwr-markdown.mjs +6 -6
- package/fesm2022/ngwr-marquee.mjs +3 -3
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-mention.mjs +6 -6
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meter-group.mjs +3 -3
- package/fesm2022/ngwr-overlay.mjs +6 -6
- package/fesm2022/ngwr-page-header.mjs +3 -3
- package/fesm2022/ngwr-pagination.mjs +72 -9
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +21 -21
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popover.mjs +6 -6
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs +3 -3
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-rating.mjs +3 -3
- package/fesm2022/ngwr-result.mjs +12 -12
- package/fesm2022/ngwr-rotating-text.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-segmented-testing.mjs +71 -0
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +98 -8
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +9 -9
- package/fesm2022/ngwr-shiny-text.mjs +6 -6
- package/fesm2022/ngwr-sidebar.mjs +3 -3
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs +3 -3
- package/fesm2022/ngwr-sparkline.mjs +3 -3
- package/fesm2022/ngwr-speed-dial.mjs +3 -3
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-split-text.mjs +3 -3
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-spotlight-card.mjs +6 -6
- package/fesm2022/ngwr-squircle.mjs +6 -6
- package/fesm2022/ngwr-star-border.mjs +3 -3
- package/fesm2022/ngwr-statistic.mjs +9 -9
- package/fesm2022/ngwr-stepper.mjs +6 -6
- package/fesm2022/ngwr-storage.mjs +3 -3
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-table.mjs +18 -18
- package/fesm2022/ngwr-tabs.mjs +6 -6
- package/fesm2022/ngwr-textarea.mjs +3 -3
- package/fesm2022/ngwr-theme.mjs +3 -3
- package/fesm2022/ngwr-tilt-card.mjs +6 -6
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-toast.mjs +9 -9
- package/fesm2022/ngwr-toolbar.mjs +3 -3
- package/fesm2022/ngwr-tour.mjs +6 -6
- package/fesm2022/ngwr-transfer.mjs +3 -3
- package/fesm2022/ngwr-tree.mjs +3 -3
- package/fesm2022/ngwr-typewriter.mjs +3 -3
- package/fesm2022/ngwr-typography.mjs +3 -3
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
- package/fesm2022/ngwr-waves.mjs +3 -3
- package/fesm2022/ngwr-window.mjs +12 -12
- package/llms-full.txt +1 -1
- package/llms.txt +24 -2
- package/package.json +1 -1
- package/types/ngwr-pagination.d.ts +11 -0
- package/types/ngwr-segmented-testing.d.ts +44 -0
- package/types/ngwr-segmented.d.ts +86 -4
- package/types/ngwr-version.d.ts +1 -1
package/fesm2022/ngwr-window.mjs
CHANGED
|
@@ -572,10 +572,10 @@ class WrWindow {
|
|
|
572
572
|
this.x_.set(clamp(this.x_(), MIN_ON_VIEWPORT - w, vw - MIN_ON_VIEWPORT));
|
|
573
573
|
this.y_.set(clamp(this.y_(), 0, vh - MIN_ON_VIEWPORT));
|
|
574
574
|
}
|
|
575
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
576
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: WrWindow, isStandalone: true, selector: "wr-window", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, initialX: { classPropertyName: "initialX", publicName: "initialX", isSignal: true, isRequired: false, transformFunction: null }, initialY: { classPropertyName: "initialY", publicName: "initialY", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, initialWidth: { classPropertyName: "initialWidth", publicName: "initialWidth", isSignal: true, isRequired: false, transformFunction: null }, initialHeight: { classPropertyName: "initialHeight", publicName: "initialHeight", isSignal: true, isRequired: false, transformFunction: null }, os: { classPropertyName: "os", publicName: "os", isSignal: true, isRequired: false, transformFunction: null }, chromeSize: { classPropertyName: "chromeSize", publicName: "chromeSize", isSignal: true, isRequired: false, transformFunction: null }, animations: { classPropertyName: "animations", publicName: "animations", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, movable: { classPropertyName: "movable", publicName: "movable", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, keepInViewport: { classPropertyName: "keepInViewport", publicName: "keepInViewport", isSignal: true, isRequired: false, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, dragHandle: { classPropertyName: "dragHandle", publicName: "dragHandle", isSignal: true, isRequired: false, transformFunction: null }, showMinimize: { classPropertyName: "showMinimize", publicName: "showMinimize", isSignal: true, isRequired: false, transformFunction: null }, showMaximize: { classPropertyName: "showMaximize", publicName: "showMaximize", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", state: "stateChange", closed: "closed", moved: "moved", resized: "resized" }, host: { attributes: { "role": "dialog" }, listeners: { "pointerdown": "focusWindow()" }, properties: { "class": "classes()", "style.left.px": "x()", "style.top.px": "y()", "style.width.px": "width()", "style.height.px": "height()", "style.z-index": "z()", "attr.aria-hidden": "!open()", "attr.aria-labelledby": "titleId" } }, ngImport: i0, template: "<!--\n The double-click gate is `!== false`, NOT `maximizeVisible()`, and the\n difference is a window nobody can get out of. The Linux preset hides the\n maximize BUTTON; the gesture is how GNOME maximizes, and it worked on every\n chrome before this input went tri-state. Gating it on button visibility would\n have killed it there \u2014 and `maximized` is reachable on Linux with no button at\n all (a snap to the top edge, `ref.maximize()`, a restored layout), after which\n every exit disappears at once: no restore-down button, no minimize button,\n `startMove` refuses while the state is not `normal`, the resize handles are not\n rendered, and the only key the window listens for is Escape, which closes it.\n An explicit `showMaximize: false` still turns the gesture off everywhere.\n-->\n<div class=\"wr-window__chrome\" (pointerdown)=\"startMove($event)\" (dblclick)=\"showMaximize() !== false && maximize()\">\n <div class=\"wr-window__title\" [attr.id]=\"titleId\">{{ title() }}</div>\n <div class=\"wr-window__title-extra\">\n <ng-content select=\"[wrWindowTitleExtra]\" />\n </div>\n\n <div class=\"wr-window__chrome-actions\">\n @if (resolvedOs() === 'macos') {\n <!-- macOS-style traffic-light cluster: close \u2192 minimize \u2192 maximize.\n Glyphs (\u00D7, \u2212, +) appear on cluster hover. -->\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path\n d=\"M2.2 2.2 L7.8 7.8 M7.8 2.2 L2.2 7.8\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"2\" y1=\"5\" x2=\"8\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <path\n d=\"M3 1 L1 1 L1 3 M7 1 L9 1 L9 3 M1 7 L1 9 L3 9 M9 7 L9 9 L7 9\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n fill=\"none\"\n />\n } @else {\n <path d=\"M2 5 L8 5 M5 2 L5 8\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n }\n </svg>\n </button>\n }\n } @else {\n <!-- Windows / Linux glyph buttons. Linux hides minimize and maximize by\n DEFAULT, not by rule: `minimizeVisible()` resolves the input first, so\n `[showMinimize]=\"true\"` puts the button back \u2014 which is the only way a\n user on Linux can reach `<wr-window-taskbar>` at all. -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"1\" y1=\"8\" x2=\"9\" y2=\"8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n @if (state() === 'maximized') {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <rect x=\"3\" y=\"1\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n } @else {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1\" y=\"1\" width=\"8\" height=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n }\n </button>\n }\n\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n }\n </div>\n</div>\n\n<div class=\"wr-window__body\">\n <ng-content />\n</div>\n\n<div class=\"wr-window__status-bar\">\n <ng-content select=\"[wrWindowStatusBar]\" />\n</div>\n\n@if (resizable() && state() === 'normal') {\n <span class=\"wr-window__handle wr-window__handle--t\" (pointerdown)=\"resizeT($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--r\" (pointerdown)=\"resizeR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--b\" (pointerdown)=\"resizeB($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--l\" (pointerdown)=\"resizeL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tl\" (pointerdown)=\"resizeTL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tr\" (pointerdown)=\"resizeTR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--bl\" (pointerdown)=\"resizeBL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--br\" (pointerdown)=\"resizeBR($event)\"></span>\n}\n", encapsulation: i0.ViewEncapsulation.None });
|
|
575
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindow, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
576
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrWindow, isStandalone: true, selector: "wr-window", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, initialX: { classPropertyName: "initialX", publicName: "initialX", isSignal: true, isRequired: false, transformFunction: null }, initialY: { classPropertyName: "initialY", publicName: "initialY", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, initialWidth: { classPropertyName: "initialWidth", publicName: "initialWidth", isSignal: true, isRequired: false, transformFunction: null }, initialHeight: { classPropertyName: "initialHeight", publicName: "initialHeight", isSignal: true, isRequired: false, transformFunction: null }, os: { classPropertyName: "os", publicName: "os", isSignal: true, isRequired: false, transformFunction: null }, chromeSize: { classPropertyName: "chromeSize", publicName: "chromeSize", isSignal: true, isRequired: false, transformFunction: null }, animations: { classPropertyName: "animations", publicName: "animations", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, movable: { classPropertyName: "movable", publicName: "movable", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, keepInViewport: { classPropertyName: "keepInViewport", publicName: "keepInViewport", isSignal: true, isRequired: false, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, dragHandle: { classPropertyName: "dragHandle", publicName: "dragHandle", isSignal: true, isRequired: false, transformFunction: null }, showMinimize: { classPropertyName: "showMinimize", publicName: "showMinimize", isSignal: true, isRequired: false, transformFunction: null }, showMaximize: { classPropertyName: "showMaximize", publicName: "showMaximize", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", state: "stateChange", closed: "closed", moved: "moved", resized: "resized" }, host: { attributes: { "role": "dialog" }, listeners: { "pointerdown": "focusWindow()" }, properties: { "class": "classes()", "style.left.px": "x()", "style.top.px": "y()", "style.width.px": "width()", "style.height.px": "height()", "style.z-index": "z()", "attr.aria-hidden": "!open()", "attr.aria-labelledby": "titleId" } }, ngImport: i0, template: "<!--\n The double-click gate is `!== false`, NOT `maximizeVisible()`, and the\n difference is a window nobody can get out of. The Linux preset hides the\n maximize BUTTON; the gesture is how GNOME maximizes, and it worked on every\n chrome before this input went tri-state. Gating it on button visibility would\n have killed it there \u2014 and `maximized` is reachable on Linux with no button at\n all (a snap to the top edge, `ref.maximize()`, a restored layout), after which\n every exit disappears at once: no restore-down button, no minimize button,\n `startMove` refuses while the state is not `normal`, the resize handles are not\n rendered, and the only key the window listens for is Escape, which closes it.\n An explicit `showMaximize: false` still turns the gesture off everywhere.\n-->\n<div class=\"wr-window__chrome\" (pointerdown)=\"startMove($event)\" (dblclick)=\"showMaximize() !== false && maximize()\">\n <div class=\"wr-window__title\" [attr.id]=\"titleId\">{{ title() }}</div>\n <div class=\"wr-window__title-extra\">\n <ng-content select=\"[wrWindowTitleExtra]\" />\n </div>\n\n <div class=\"wr-window__chrome-actions\">\n @if (resolvedOs() === 'macos') {\n <!-- macOS-style traffic-light cluster: close \u2192 minimize \u2192 maximize.\n Glyphs (\u00D7, \u2212, +) appear on cluster hover. -->\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path\n d=\"M2.2 2.2 L7.8 7.8 M7.8 2.2 L2.2 7.8\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"2\" y1=\"5\" x2=\"8\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <path\n d=\"M3 1 L1 1 L1 3 M7 1 L9 1 L9 3 M1 7 L1 9 L3 9 M9 7 L9 9 L7 9\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n fill=\"none\"\n />\n } @else {\n <path d=\"M2 5 L8 5 M5 2 L5 8\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n }\n </svg>\n </button>\n }\n } @else {\n <!-- Windows / Linux glyph buttons. Linux hides minimize and maximize by\n DEFAULT, not by rule: `minimizeVisible()` resolves the input first, so\n `[showMinimize]=\"true\"` puts the button back \u2014 which is the only way a\n user on Linux can reach `<wr-window-taskbar>` at all. -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"1\" y1=\"8\" x2=\"9\" y2=\"8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n @if (state() === 'maximized') {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <rect x=\"3\" y=\"1\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n } @else {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1\" y=\"1\" width=\"8\" height=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n }\n </button>\n }\n\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n }\n </div>\n</div>\n\n<div class=\"wr-window__body\">\n <ng-content />\n</div>\n\n<div class=\"wr-window__status-bar\">\n <ng-content select=\"[wrWindowStatusBar]\" />\n</div>\n\n@if (resizable() && state() === 'normal') {\n <span class=\"wr-window__handle wr-window__handle--t\" (pointerdown)=\"resizeT($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--r\" (pointerdown)=\"resizeR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--b\" (pointerdown)=\"resizeB($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--l\" (pointerdown)=\"resizeL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tl\" (pointerdown)=\"resizeTL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tr\" (pointerdown)=\"resizeTR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--bl\" (pointerdown)=\"resizeBL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--br\" (pointerdown)=\"resizeBR($event)\"></span>\n}\n", encapsulation: i0.ViewEncapsulation.None });
|
|
577
577
|
}
|
|
578
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
578
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindow, decorators: [{
|
|
579
579
|
type: Component,
|
|
580
580
|
args: [{ selector: 'wr-window', encapsulation: ViewEncapsulation.None, host: {
|
|
581
581
|
role: 'dialog',
|
|
@@ -746,10 +746,10 @@ class WrWindowContainer {
|
|
|
746
746
|
});
|
|
747
747
|
});
|
|
748
748
|
}
|
|
749
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
750
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
749
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
750
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrWindowContainer, isStandalone: true, selector: "wr-window-container", viewQueries: [{ propertyName: "contentVcr", first: true, predicate: ["content"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "wrWindow", first: true, predicate: WrWindow, descendants: true, isSignal: true }], ngImport: i0, template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'normal'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n", dependencies: [{ kind: "component", type: WrWindow, selector: "wr-window", inputs: ["open", "state", "title", "initialX", "initialY", "size", "initialWidth", "initialHeight", "os", "chromeSize", "animations", "minWidth", "minHeight", "maxWidth", "maxHeight", "movable", "resizable", "keepInViewport", "snap", "dragHandle", "showMinimize", "showMaximize", "showClose"], outputs: ["openChange", "stateChange", "closed", "moved", "resized"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
751
751
|
}
|
|
752
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
752
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowContainer, decorators: [{
|
|
753
753
|
type: Component,
|
|
754
754
|
args: [{ selector: 'wr-window-container', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [WrWindow], template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'normal'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n" }]
|
|
755
755
|
}], ctorParameters: () => [], propDecorators: { contentVcr: [{ type: i0.ViewChild, args: ['content', { ...{ read: ViewContainerRef }, isSignal: true }] }], wrWindow: [{ type: i0.ViewChild, args: [i0.forwardRef(() => WrWindow), { isSignal: true }] }] } });
|
|
@@ -1270,10 +1270,10 @@ class WrWindowManager {
|
|
|
1270
1270
|
clearLayout(name) {
|
|
1271
1271
|
this.storage.remove(this.layoutKey(name));
|
|
1272
1272
|
}
|
|
1273
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1274
|
-
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.
|
|
1273
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowManager, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
1274
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: WrWindowManager });
|
|
1275
1275
|
}
|
|
1276
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1276
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowManager, decorators: [{
|
|
1277
1277
|
type: Service
|
|
1278
1278
|
}] });
|
|
1279
1279
|
|
|
@@ -1336,10 +1336,10 @@ class WrWindowTaskbar {
|
|
|
1336
1336
|
event.stopPropagation();
|
|
1337
1337
|
void ref.close();
|
|
1338
1338
|
}
|
|
1339
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1340
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
1339
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowTaskbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1340
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrWindowTaskbar, isStandalone: true, selector: "wr-window-taskbar", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar" }, properties: { "class": "classes()", "attr.aria-label": "railLabel()" } }, ngImport: i0, template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n <!--\n Two SIBLING buttons in a plain container, not a close button nested\n inside the tab. The close used to be a `role=\"button\"` span with\n `tabindex=\"0\"` inside the tab's own `<button>` \u2014 interactive content\n inside interactive content, which the HTML parser and `nested-interactive`\n both object to. Nothing caught it: a taskbar tab exists only while a\n window is minimized, and the a11y sweep reads prerendered HTML, where the\n rail is not rendered at all.\n -->\n <div class=\"wr-window-taskbar__tab\">\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-restore\"\n [attr.aria-label]=\"restoreLabel({ title: tabTitle(ref) })\"\n (click)=\"restore(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ tabTitle(ref) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close(ref, $event)\"\n >\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n }\n </div>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1341
1341
|
}
|
|
1342
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowTaskbar, decorators: [{
|
|
1343
1343
|
type: Component,
|
|
1344
1344
|
args: [{ selector: 'wr-window-taskbar', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', role: 'toolbar', '[attr.aria-label]': 'railLabel()' }, template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n <!--\n Two SIBLING buttons in a plain container, not a close button nested\n inside the tab. The close used to be a `role=\"button\"` span with\n `tabindex=\"0\"` inside the tab's own `<button>` \u2014 interactive content\n inside interactive content, which the HTML parser and `nested-interactive`\n both object to. Nothing caught it: a taskbar tab exists only while a\n window is minimized, and the a11y sweep reads prerendered HTML, where the\n rail is not rendered at all.\n -->\n <div class=\"wr-window-taskbar__tab\">\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-restore\"\n [attr.aria-label]=\"restoreLabel({ title: tabTitle(ref) })\"\n (click)=\"restore(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ tabTitle(ref) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close(ref, $event)\"\n >\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n }\n </div>\n}\n" }]
|
|
1345
1345
|
}], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }] } });
|
package/llms-full.txt
CHANGED
|
@@ -732,7 +732,7 @@
|
|
|
732
732
|
- import: `import { WrSegmented } from 'ngwr/segmented'`
|
|
733
733
|
- selector: `wr-segmented`
|
|
734
734
|
- exports: WrSegmented, WrSegmentedOption, WrSegmentedSize
|
|
735
|
-
- Single-choice picker rendered as a segmented control.
|
|
735
|
+
- Single-choice picker rendered as a segmented control. A signal-forms native control — it implements `FormValueControl`, so `[formField]` binds straight to its `value` model. `[(value)]` works standalone, and `[(ngModel)]` / reactive forms keep working through Angular's bridge.
|
|
736
736
|
|
|
737
737
|
## ngwr/segmented/testing
|
|
738
738
|
- import: `import { WrSegmentedHarness } from 'ngwr/segmented/testing'`
|
package/llms.txt
CHANGED
|
@@ -132,8 +132,10 @@ before writing your own. Full catalog: https://ngwr.dev/reference/components.
|
|
|
132
132
|
|
|
133
133
|
## Patterns
|
|
134
134
|
|
|
135
|
-
- **Forms** —
|
|
136
|
-
`FormCheckboxControl` for checkbox) — no `ControlValueAccessor` in
|
|
135
|
+
- **Forms** — nineteen value controls are signal-forms native (`FormValueControl`,
|
|
136
|
+
or `FormCheckboxControl` for checkbox/switch) — no `ControlValueAccessor` in the
|
|
137
|
+
library. A count rather than "every": `[wrColorPickerTrigger]` is public, carries
|
|
138
|
+
its own `value` model, and implements neither.
|
|
137
139
|
Bind `[formField]="form.x"`, use the two-way model standalone (`[(value)]`, or
|
|
138
140
|
`[(checked)]` on checkbox/switch), or classic `[(ngModel)]` / reactive forms
|
|
139
141
|
via Angular's bridge.
|
|
@@ -191,6 +193,26 @@ before writing your own. Full catalog: https://ngwr.dev/reference/components.
|
|
|
191
193
|
EXCLUSIVE. `editable` turns on drag and resize; `Alt` + arrows do the same
|
|
192
194
|
from the keyboard.
|
|
193
195
|
|
|
196
|
+
## Deciding whether to use it
|
|
197
|
+
|
|
198
|
+
Three pages exist for the question before installation, and everything on them
|
|
199
|
+
is meant to be checkable rather than taken on trust:
|
|
200
|
+
|
|
201
|
+
- **Why ngwr, and when not to** — the wedge (nineteen value controls are Signal
|
|
202
|
+
Forms controls; no `ControlValueAccessor` in the library), a comparison against
|
|
203
|
+
Material / PrimeNG / NG-ZORRO / Taiga / Spartan, and a per-competitor "choose
|
|
204
|
+
something else if" section: https://ngwr.dev/start/comparison
|
|
205
|
+
- **What gates a change** — eight gates on every PR, three separate
|
|
206
|
+
accessibility sweeps and what each one cannot see, the two nightly baselines
|
|
207
|
+
and what is accepted in them, plus what the test suite does NOT cover:
|
|
208
|
+
https://ngwr.dev/start/quality
|
|
209
|
+
- **A live Signal Forms demo** you can read before installing anything:
|
|
210
|
+
https://ngwr.dev/start/playground
|
|
211
|
+
|
|
212
|
+
The honest shape of the project, since these pages say it too: one maintainer,
|
|
213
|
+
MIT, six majors shipped between 2026-06-12 and 2026-08-20, a codemod for every
|
|
214
|
+
break a codemod can repair, no commercial support.
|
|
215
|
+
|
|
194
216
|
## Resources (machine-readable)
|
|
195
217
|
|
|
196
218
|
- This quick-ref: https://ngwr.dev/llms.txt
|
package/package.json
CHANGED
|
@@ -111,6 +111,17 @@ declare class WrPagination {
|
|
|
111
111
|
protected readonly navLabel: _angular_core.Signal<string>;
|
|
112
112
|
/** Internal: total page count. */
|
|
113
113
|
protected readonly totalPages: _angular_core.Signal<number>;
|
|
114
|
+
/**
|
|
115
|
+
* Internal: where the previous arrow goes. Clamped to the end of the valid
|
|
116
|
+
* range rather than being a plain `currentPage() - 1`, because the page can
|
|
117
|
+
* legitimately sit past that end while `total` is 0 (see the guard effect).
|
|
118
|
+
* From page 5 of one page, `goTo(4)` is refused as out of range, so the arrow
|
|
119
|
+
* was enabled and did nothing — and under `responsive` on a narrow box the
|
|
120
|
+
* numbered cells are hidden by a container query, which left no way back at
|
|
121
|
+
* all. `Math.min` only ever moves the target INWARD, so `goTo`'s own
|
|
122
|
+
* past-the-end refusal keeps working as the backstop it is.
|
|
123
|
+
*/
|
|
124
|
+
protected readonly prevPage: _angular_core.Signal<number>;
|
|
114
125
|
/** Internal: "X–Y of Z" string. */
|
|
115
126
|
protected readonly rangeLabel: _angular_core.Signal<string>;
|
|
116
127
|
/**
|
|
@@ -194,6 +194,17 @@ declare class WrSegmentedOptionHarness extends ComponentHarness {
|
|
|
194
194
|
* - Activation is the browser's own default action on a button, which jsdom does not
|
|
195
195
|
* implement, so {@link select} clicks — see {@link WrSegmentedOptionHarness.select}.
|
|
196
196
|
*
|
|
197
|
+
* **It is a signal-forms control**, which adds two things to read here and one that
|
|
198
|
+
* cannot be read at all. `[formField]` binds the component's own `value` model, and a
|
|
199
|
+
* surrounding `<wr-form-field>` publishes its state onto the GROUP —
|
|
200
|
+
* {@link isInvalid} and {@link getDescriptionText} answer that, on the host rather
|
|
201
|
+
* than per segment, because the strip is one field and a description repeated on
|
|
202
|
+
* every segment would be announced once per Tab. What is NOT here is the field's
|
|
203
|
+
* `touched` flag: the component only emits `touch`, and whether anything listened is
|
|
204
|
+
* a fact about the form, not about this DOM — assert it on the field.
|
|
205
|
+
* {@link blur} is how a spec gets there, and it is the ONLY route: a click does not
|
|
206
|
+
* mark a field touched, and neither does moving from one segment to the next.
|
|
207
|
+
*
|
|
197
208
|
* **There is deliberately no `getValue()` and no selection by value.** `options` is
|
|
198
209
|
* a bound array of `{ value, label, … }` and the value is a generic `T` the component
|
|
199
210
|
* writes nowhere — not on the host, not on a segment. The single exception is an
|
|
@@ -339,6 +350,39 @@ declare class WrSegmentedHarness extends ComponentHarness {
|
|
|
339
350
|
* {@link getTabStopLabels} gives.
|
|
340
351
|
*/
|
|
341
352
|
getFocusedLabel(): Promise<string | null>;
|
|
353
|
+
/**
|
|
354
|
+
* Whether the surrounding `<wr-form-field>` is reporting this control invalid, from
|
|
355
|
+
* `aria-invalid` on the host.
|
|
356
|
+
*
|
|
357
|
+
* `false` for a control with no field around it, and `false` for a field that is
|
|
358
|
+
* invalid but has not shown its copy yet: the component keys the attribute on the
|
|
359
|
+
* MESSAGE existing rather than on the error, because announcing "invalid" while
|
|
360
|
+
* pointing at nothing is worse than staying quiet. So this and
|
|
361
|
+
* {@link getDescriptionText} move together, and a `true` with no description would
|
|
362
|
+
* be the finding.
|
|
363
|
+
*/
|
|
364
|
+
isInvalid(): Promise<boolean>;
|
|
365
|
+
/**
|
|
366
|
+
* The text `aria-describedby` points at — the validation copy a screen reader is
|
|
367
|
+
* given for this control — or `null` when the host describes nothing.
|
|
368
|
+
*
|
|
369
|
+
* Resolved from the document root, because the element named may sit anywhere on
|
|
370
|
+
* the page, and THROWS when the reference resolves to no element: a dangling
|
|
371
|
+
* `aria-describedby` renders exactly like a working one and is announced as
|
|
372
|
+
* nothing, so returning `null` for it would report the bug as the healthy state.
|
|
373
|
+
*/
|
|
374
|
+
getDescriptionText(): Promise<string | null>;
|
|
375
|
+
/**
|
|
376
|
+
* Take focus off the strip entirely — what marks a bound field touched, and the
|
|
377
|
+
* only thing that does.
|
|
378
|
+
*
|
|
379
|
+
* The component emits `touch` from a `focusout` that leaves the host, so a click
|
|
380
|
+
* does not reach it and neither does Tab from one segment to the next: a strip a
|
|
381
|
+
* user tabbed straight past has to end up untouched, which is the case `touched`
|
|
382
|
+
* exists for. Resolves quietly when nothing inside was focused, since that is
|
|
383
|
+
* already the state this asks for.
|
|
384
|
+
*/
|
|
385
|
+
blur(): Promise<void>;
|
|
342
386
|
/**
|
|
343
387
|
* The index the component publishes for the thumb, from the
|
|
344
388
|
* `--wr-segmented-thumb-index` custom property it sets inline.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { FormValueControl } from '@angular/forms/signals';
|
|
2
3
|
import { WrIconName } from 'ngwr/icon';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -31,8 +32,25 @@ interface WrSegmentedOption<T = unknown> {
|
|
|
31
32
|
* Single-choice picker rendered as a segmented control. Two-way binds
|
|
32
33
|
* the selected `value`.
|
|
33
34
|
*
|
|
35
|
+
* A signal-forms native control: it implements `FormValueControl<T | null>`, so
|
|
36
|
+
* `[formField]` binds straight to its `value` model — no
|
|
37
|
+
* `ControlValueAccessor` in between. `[(value)]` works standalone, and classic
|
|
38
|
+
* `[(ngModel)]` / reactive forms keep working through Angular's bridge.
|
|
39
|
+
*
|
|
40
|
+
* It stays a `role="group"` of `aria-pressed` toggle buttons rather than
|
|
41
|
+
* becoming a `radiogroup`: being a form control changes what the value is bound
|
|
42
|
+
* to, not what the widget is, and a radiogroup would owe a roving tab stop and
|
|
43
|
+
* arrow-key selection — a different keyboard contract for every consumer who
|
|
44
|
+
* already ships this. The group is what the field describes (`aria-describedby`
|
|
45
|
+
* / `aria-invalid` land on the host), while the `<label for>` has to name a
|
|
46
|
+
* labelable element, so the id goes on the first segment.
|
|
47
|
+
*
|
|
34
48
|
* @example
|
|
35
49
|
* ```html
|
|
50
|
+
* <!-- signal forms -->
|
|
51
|
+
* <wr-segmented [options]="ranges" [formField]="form.range" />
|
|
52
|
+
*
|
|
53
|
+
* <!-- standalone two-way binding -->
|
|
36
54
|
* <wr-segmented
|
|
37
55
|
* [options]="[
|
|
38
56
|
* { value: 'day', label: 'Day' },
|
|
@@ -46,15 +64,68 @@ interface WrSegmentedOption<T = unknown> {
|
|
|
46
64
|
* @see https://ngwr.dev/reference/components/segmented
|
|
47
65
|
*/
|
|
48
66
|
type WrSegmentedSize = 'sm' | 'md' | 'lg';
|
|
49
|
-
declare class WrSegmented<T = unknown> {
|
|
67
|
+
declare class WrSegmented<T = unknown> implements FormValueControl<T | null> {
|
|
50
68
|
/** The segments to render. */
|
|
51
69
|
readonly options: _angular_core.InputSignal<readonly WrSegmentedOption<T>[]>;
|
|
52
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* The picked segment's `value`, `null` when nothing is selected. Bound by
|
|
72
|
+
* `[formField]`, or two-way via `[(value)]`.
|
|
73
|
+
*/
|
|
53
74
|
readonly value: _angular_core.ModelSignal<T | null>;
|
|
54
|
-
/**
|
|
75
|
+
/** Emitted when focus leaves the strip, so a bound field can mark itself touched. */
|
|
76
|
+
readonly touch: _angular_core.OutputEmitterRef<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Disable the whole control. Bound automatically from the field's disabled
|
|
79
|
+
* state when used with `[formField]`.
|
|
80
|
+
*
|
|
81
|
+
* @default false
|
|
82
|
+
*/
|
|
55
83
|
readonly disabled: _angular_core.InputSignalWithTransform<boolean, any>;
|
|
56
84
|
/** Control size — shares the `--wr-control-*` contract. @default 'md' */
|
|
57
85
|
readonly size: _angular_core.InputSignal<WrSegmentedSize>;
|
|
86
|
+
private readonly host;
|
|
87
|
+
private readonly field;
|
|
88
|
+
/**
|
|
89
|
+
* Id the surrounding `<wr-form-field>`'s `<label for>` points at, stamped on the
|
|
90
|
+
* FIRST segment.
|
|
91
|
+
*
|
|
92
|
+
* The field renders its label before it can see what was projected into it, so the
|
|
93
|
+
* id travels the other way and the control adopts it, exactly as `[wrInput]`,
|
|
94
|
+
* `wr-slider` and `wr-select` do. Without this the `for` named an element that was
|
|
95
|
+
* nowhere in the document: clicking the label did nothing.
|
|
96
|
+
*
|
|
97
|
+
* It cannot go on the host — only a labelable element can be a label's target, and
|
|
98
|
+
* `<wr-segmented>` is not one — and there is no single control to put it on, so it
|
|
99
|
+
* goes where clicking the label should land a user: the first segment, which is
|
|
100
|
+
* also the strip's first tab stop. The same choice `wr-slider` makes with its low
|
|
101
|
+
* thumb.
|
|
102
|
+
*
|
|
103
|
+
* What this does NOT change is that segment's NAME: it is named by its own label,
|
|
104
|
+
* and a `<label for>` cannot outrank text content in the accname order anyway.
|
|
105
|
+
*
|
|
106
|
+
* The one shape this reads wrong is a strip NESTED inside another control that is
|
|
107
|
+
* itself the field — `<wr-color-picker>`'s own format tabs are the only case in
|
|
108
|
+
* the library — where the id lands on a tab instead of on the picker. That is the
|
|
109
|
+
* same exposure `[wrInput]`, `wr-slider` and `wr-select` all carry, and the fix
|
|
110
|
+
* belongs to the outer control (shield its subtree by providing `WR_FORM_FIELD`
|
|
111
|
+
* as `null`), not to a strip that cannot tell whose field it is in.
|
|
112
|
+
*/
|
|
113
|
+
protected readonly controlId: _angular_core.Signal<string | null>;
|
|
114
|
+
/**
|
|
115
|
+
* The field's error message, wired to the GROUP rather than to a segment.
|
|
116
|
+
*
|
|
117
|
+
* A segmented control is one field made of several buttons, so the description
|
|
118
|
+
* belongs to the thing the field describes — repeated on every segment it would be
|
|
119
|
+
* announced once per Tab. `role="group"` supports neither state as a role-specific
|
|
120
|
+
* attribute, but both are ARIA globals, so this is valid where an `aria-checked`
|
|
121
|
+
* would not be.
|
|
122
|
+
*
|
|
123
|
+
* `aria-invalid` is keyed on the message EXISTING rather than on `errorKeys()`,
|
|
124
|
+
* because the field only publishes an id once it is showing something — announcing
|
|
125
|
+
* "invalid" while pointing at nothing is worse than staying quiet.
|
|
126
|
+
*/
|
|
127
|
+
protected readonly describedBy: _angular_core.Signal<string | null>;
|
|
128
|
+
protected readonly ariaInvalid: _angular_core.Signal<"true" | null>;
|
|
58
129
|
/** Index of the selected option, or `-1` when nothing is selected. */
|
|
59
130
|
protected readonly selectedIndex: _angular_core.Signal<number>;
|
|
60
131
|
/** Flips true after the first paint so the thumb only animates user-driven changes, not the initial snap. */
|
|
@@ -86,8 +157,19 @@ declare class WrSegmented<T = unknown> {
|
|
|
86
157
|
protected isSelected(option: WrSegmentedOption<T>): boolean;
|
|
87
158
|
protected select(option: WrSegmentedOption<T>): void;
|
|
88
159
|
protected trackByValue(_: number, option: WrSegmentedOption<T>): unknown;
|
|
160
|
+
/**
|
|
161
|
+
* A bound field learns it may show its validation copy from `touch`, and a click
|
|
162
|
+
* cannot be the source: a strip tabbed into and out of without a pick left the
|
|
163
|
+
* field untouched forever — which is exactly the case `touched` exists for.
|
|
164
|
+
*
|
|
165
|
+
* `wr-knob` and `wr-rating` bind `(blur)` on the one element they own; this
|
|
166
|
+
* component owns one button per option, so it listens for the bubbling `focusout`
|
|
167
|
+
* on its host instead — and ignores the one that only moves focus from one segment
|
|
168
|
+
* to the next, the way `wr-slider` ignores the hop between its two thumbs.
|
|
169
|
+
*/
|
|
170
|
+
protected onFocusOut(event: FocusEvent): void;
|
|
89
171
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrSegmented<any>, never>;
|
|
90
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrSegmented<any>, "wr-segmented", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
|
|
172
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrSegmented<any>, "wr-segmented", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touch": "touch"; }, never, never, true, never>;
|
|
91
173
|
}
|
|
92
174
|
|
|
93
175
|
export { WrSegmented };
|
package/types/ngwr-version.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { InjectionToken } from '@angular/core';
|
|
|
16
16
|
* Distinct from `Version` exported by `@angular/core`, which reports the
|
|
17
17
|
* loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.
|
|
18
18
|
*/
|
|
19
|
-
declare const NGWR_VERSION = "12.
|
|
19
|
+
declare const NGWR_VERSION = "12.1.0";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @license
|