ngwr 12.0.0 → 12.2.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/alert/styles/_index.scss +11 -4
- package/badge/styles/_index.scss +30 -0
- package/badge/styles/_tag.scss +15 -1
- package/descriptions/styles/_index.scss +13 -3
- package/drawer/styles/_index.scss +95 -1
- 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 +24 -18
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- 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 +22 -12
- package/fesm2022/ngwr-select.mjs.map +1 -1
- 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/overlay/styles/_arrow.scss +243 -0
- package/package.json +1 -1
- package/popconfirm/styles/_index.scss +17 -0
- package/popover/styles/_index.scss +49 -207
- package/select/styles/_index.scss +30 -4
- package/sidebar/styles/_index.scss +4 -1
- package/stepper/styles/_index.scss +44 -5
- package/theme/styles/_mixins.scss +10 -0
- package/tree/styles/_index.scss +10 -2
- 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-select.d.ts +12 -2
- 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
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
@use 'sass:list';
|
|
2
|
+
@use 'sass:map';
|
|
3
|
+
|
|
4
|
+
// --- Anchored-panel arrow ----------------------------------------------------
|
|
5
|
+
//
|
|
6
|
+
// A small square rotated 45°, half tucked under the panel so only the tip shows.
|
|
7
|
+
// Shared by every overlay that points AT its trigger: `wr-popover`, its tooltip
|
|
8
|
+
// mode, and `wr-popconfirm`.
|
|
9
|
+
//
|
|
10
|
+
// **Why this lives in `ngwr/overlay` and not in `ngwr/theme`.** `theme/styles`
|
|
11
|
+
// is the token layer plus mixins any element can use — `smooth-br`,
|
|
12
|
+
// `touch-target`, `focus-ring`. This is not one of those. It only means anything
|
|
13
|
+
// against a CDK pane that carries a `--<placement>` modifier written by
|
|
14
|
+
// `wrPopoverPositions()` / a directive's `panelClass`, and the eight-to-twelve
|
|
15
|
+
// placement names it keys off are `WR_POPOVER_POSITIONS`. That is the overlay
|
|
16
|
+
// layer's vocabulary, and `overlay/styles` is already where the cross-overlay
|
|
17
|
+
// presentation lives (`.wr-overlay-sheet`, `.wr-overlay-backdrop`). Promoting it
|
|
18
|
+
// to `ngwr/theme` would advertise it to every component in the catalog as
|
|
19
|
+
// something general, which it is not.
|
|
20
|
+
//
|
|
21
|
+
// It is deliberately NOT `@forward`ed from `overlay/styles/_index.scss`, so
|
|
22
|
+
// `@use 'ngwr/overlay'` gains no new public API and this file emits no CSS of
|
|
23
|
+
// its own — a component `@use`s it by relative path and pays only for the rules
|
|
24
|
+
// its own `anchored-arrow()` call generates.
|
|
25
|
+
|
|
26
|
+
/// Side of the square's box. Half of it shows; the rest is under the panel.
|
|
27
|
+
$size: 6px;
|
|
28
|
+
$half: 3px;
|
|
29
|
+
|
|
30
|
+
/// Distance from the panel corner for the `-start` / `-end` variants.
|
|
31
|
+
$inset: 0.625rem;
|
|
32
|
+
|
|
33
|
+
/// Every placement an anchored panel can take, mapped to
|
|
34
|
+
/// (which panel edge the arrow hangs off, where along that edge it sits).
|
|
35
|
+
///
|
|
36
|
+
/// A `--top` pane means the panel sits ABOVE the trigger, so the arrow hangs off
|
|
37
|
+
/// the panel's BOTTOM edge.
|
|
38
|
+
$placements: (
|
|
39
|
+
top: (
|
|
40
|
+
bottom,
|
|
41
|
+
center,
|
|
42
|
+
),
|
|
43
|
+
top-start: (
|
|
44
|
+
bottom,
|
|
45
|
+
start,
|
|
46
|
+
),
|
|
47
|
+
top-end: (
|
|
48
|
+
bottom,
|
|
49
|
+
end,
|
|
50
|
+
),
|
|
51
|
+
bottom: (
|
|
52
|
+
top,
|
|
53
|
+
center,
|
|
54
|
+
),
|
|
55
|
+
bottom-start: (
|
|
56
|
+
top,
|
|
57
|
+
start,
|
|
58
|
+
),
|
|
59
|
+
bottom-end: (
|
|
60
|
+
top,
|
|
61
|
+
end,
|
|
62
|
+
),
|
|
63
|
+
left: (
|
|
64
|
+
right,
|
|
65
|
+
center,
|
|
66
|
+
),
|
|
67
|
+
left-start: (
|
|
68
|
+
right,
|
|
69
|
+
start,
|
|
70
|
+
),
|
|
71
|
+
left-end: (
|
|
72
|
+
right,
|
|
73
|
+
end,
|
|
74
|
+
),
|
|
75
|
+
right: (
|
|
76
|
+
left,
|
|
77
|
+
center,
|
|
78
|
+
),
|
|
79
|
+
right-start: (
|
|
80
|
+
left,
|
|
81
|
+
start,
|
|
82
|
+
),
|
|
83
|
+
right-end: (
|
|
84
|
+
left,
|
|
85
|
+
end,
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
/// Positions the arrow against `$edge`, offset outwards by `$pull`.
|
|
90
|
+
///
|
|
91
|
+
/// `left` / `right` name the panel edge in LTR terms only. Every side-anchored
|
|
92
|
+
/// and `-start` / `-end` placement is expressed as CDK `originX: start | end` —
|
|
93
|
+
/// plain `top` / `bottom` use `center`, which mirrors to itself — and CDK
|
|
94
|
+
/// resolves start / end against `Directionality`. So under `dir="rtl"` the panel
|
|
95
|
+
/// lands on the mirrored side of the trigger while the modifier class keeps its
|
|
96
|
+
/// LTR name, and the arrow has to travel with the panel. Same for the `-start` /
|
|
97
|
+
/// `-end` alignment along a horizontal edge.
|
|
98
|
+
///
|
|
99
|
+
/// The class names the placement the panel ACTUALLY took, not the requested one,
|
|
100
|
+
/// wherever the caller feeds `withPositions()` a fallback list whose links each
|
|
101
|
+
/// carry their own `panelClass` — so a flip swaps the modifier and this moves the
|
|
102
|
+
/// arrow with it.
|
|
103
|
+
@mixin at($edge, $align, $pull) {
|
|
104
|
+
@if $edge == left {
|
|
105
|
+
inset-inline-start: -$pull;
|
|
106
|
+
} @else if $edge == right {
|
|
107
|
+
inset-inline-end: -$pull;
|
|
108
|
+
} @else {
|
|
109
|
+
#{$edge}: -$pull;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@if $edge == bottom or $edge == top {
|
|
113
|
+
@if $align == center {
|
|
114
|
+
// Both halves of the centring pair go logical together, so the arrow
|
|
115
|
+
// stays on the midpoint whichever way the axis runs.
|
|
116
|
+
inset-inline-start: 50%;
|
|
117
|
+
margin-inline-start: -$half;
|
|
118
|
+
} @else if $align == start {
|
|
119
|
+
inset-inline-start: $inset;
|
|
120
|
+
} @else {
|
|
121
|
+
inset-inline-end: $inset;
|
|
122
|
+
}
|
|
123
|
+
} @else {
|
|
124
|
+
@if $align == center {
|
|
125
|
+
top: 50%;
|
|
126
|
+
margin-top: -$half;
|
|
127
|
+
} @else if $align == start {
|
|
128
|
+
top: $inset;
|
|
129
|
+
} @else {
|
|
130
|
+
bottom: $inset;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// The two faces of the rotated square that stay visible outside the panel.
|
|
136
|
+
///
|
|
137
|
+
/// These stay physical, and it is not an oversight. Which two borders paint is
|
|
138
|
+
/// decided by `transform: rotate(45deg)`, not by reading order: the rotation
|
|
139
|
+
/// turns the right border into the down-right face and the bottom one into the
|
|
140
|
+
/// down-left face, so `right + bottom` is what outlines a tip pointing DOWN.
|
|
141
|
+
/// Swapping only the inline half would paint two faces that are tucked under
|
|
142
|
+
/// the panel. A mirrored arrow needs the rotation mirrored as well — more than
|
|
143
|
+
/// a property rewrite, so it is left for the RTL pass that owns the TS side.
|
|
144
|
+
@mixin faces($edge, $color) {
|
|
145
|
+
@if $edge == bottom {
|
|
146
|
+
// rtl-ok: down-right face of a 45°-rotated square — fixed by the rotation
|
|
147
|
+
border-right: 1px solid $color;
|
|
148
|
+
border-bottom: 1px solid $color;
|
|
149
|
+
} @else if $edge == top {
|
|
150
|
+
// rtl-ok: up-left face of a 45°-rotated square — fixed by the rotation
|
|
151
|
+
border-left: 1px solid $color;
|
|
152
|
+
border-top: 1px solid $color;
|
|
153
|
+
} @else if $edge == right {
|
|
154
|
+
border-top: 1px solid $color;
|
|
155
|
+
// rtl-ok: down-right face of a 45°-rotated square — fixed by the rotation
|
|
156
|
+
border-right: 1px solid $color;
|
|
157
|
+
} @else {
|
|
158
|
+
border-bottom: 1px solid $color;
|
|
159
|
+
// rtl-ok: up-left face of a 45°-rotated square — fixed by the rotation
|
|
160
|
+
border-left: 1px solid $color;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Draws the arrow for an anchored overlay, at every placement it can take.
|
|
165
|
+
///
|
|
166
|
+
/// @param {String} $pane - the CDK pane class the directive sets, without the
|
|
167
|
+
/// modifier: `.wr-popover-overlay`, `.wr-popconfirm-overlay`.
|
|
168
|
+
/// @param {String} $panel - selector for the painted surface INSIDE the pane,
|
|
169
|
+
/// relative to it: `> *` where the pane wraps an arbitrary child,
|
|
170
|
+
/// `> .wr-popconfirm` / `.wr-tooltip` where it has a known one. The arrow is
|
|
171
|
+
/// that element's `::after`, so this is also the element made `position:
|
|
172
|
+
/// relative`.
|
|
173
|
+
/// @param {*} $background - fill for the square; match the panel's own so the
|
|
174
|
+
/// two read as one shape. A `var()` is fine and preferred.
|
|
175
|
+
/// @param {*} $border - the panel's border colour, or `null` for a panel with no
|
|
176
|
+
/// border. When set, the two outward faces are painted so the outline reads as
|
|
177
|
+
/// continuous, and the square is pulled one extra pixel outwards so its own
|
|
178
|
+
/// border lines up flush with the panel's instead of leaving the panel border
|
|
179
|
+
/// showing across the arrow's base. A borderless panel has nothing to line up
|
|
180
|
+
/// with, hence the smaller pull.
|
|
181
|
+
/// @param {Bool} $inert - `true` drops the arrow out of hit-testing. For a
|
|
182
|
+
/// tooltip, whose arrow overlaps the very control the tooltip describes, so a
|
|
183
|
+
/// hittable arrow eats clicks meant for it.
|
|
184
|
+
/// @param {List} $placements-used - which placements to emit, defaulting to all
|
|
185
|
+
/// twelve. A component whose position input offers four should pass four
|
|
186
|
+
/// rather than shipping eight rules that can never match.
|
|
187
|
+
@mixin anchored-arrow($pane, $panel, $background, $border: null, $inert: false, $placements-used: map.keys($placements)) {
|
|
188
|
+
#{$pane} {
|
|
189
|
+
#{$panel} {
|
|
190
|
+
// Containing block for the arrow.
|
|
191
|
+
position: relative;
|
|
192
|
+
|
|
193
|
+
&::after {
|
|
194
|
+
content: '';
|
|
195
|
+
position: absolute;
|
|
196
|
+
width: $size;
|
|
197
|
+
height: $size;
|
|
198
|
+
background: $background;
|
|
199
|
+
transform: rotate(45deg);
|
|
200
|
+
|
|
201
|
+
@if $inert {
|
|
202
|
+
pointer-events: none;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Responsive bottom-sheet mode detaches the panel from its trigger and docks
|
|
208
|
+
// it to the bottom of the screen, so there is nothing left to point at.
|
|
209
|
+
// Emitted for every caller, not only the ones that support sheets today: the
|
|
210
|
+
// arrow is what would be wrong there, so the arrow is what should know.
|
|
211
|
+
&.wr-overlay-sheet #{$panel}::after {
|
|
212
|
+
content: none;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@each $placement in $placements-used {
|
|
217
|
+
$spec: map.get($placements, $placement);
|
|
218
|
+
|
|
219
|
+
@if $spec == null {
|
|
220
|
+
@error 'wr arrow: unknown placement `#{$placement}` — expected one of #{map.keys($placements)}.';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
$edge: list.nth($spec, 1);
|
|
224
|
+
$align: list.nth($spec, 2);
|
|
225
|
+
|
|
226
|
+
// A bordered panel pulls one extra pixel, so the square's own border lines
|
|
227
|
+
// up flush with the panel's instead of leaving the panel border showing
|
|
228
|
+
// across the arrow's base. A borderless one has nothing to line up with.
|
|
229
|
+
$pull: $half;
|
|
230
|
+
|
|
231
|
+
@if $border != null {
|
|
232
|
+
$pull: $half + 1px;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
#{$pane}--#{$placement} #{$panel}::after {
|
|
236
|
+
@include at($edge, $align, $pull);
|
|
237
|
+
|
|
238
|
+
@if $border != null {
|
|
239
|
+
@include faces($edge, $border);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
@use '../../theme/styles' as theme;
|
|
2
|
+
@use '../../overlay/styles/arrow';
|
|
2
3
|
|
|
3
4
|
.wr-popconfirm-overlay {
|
|
4
5
|
> .wr-popconfirm {
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
color: var(--wr-color-on-surface);
|
|
14
15
|
font-family: var(--wr-font-family-base);
|
|
15
16
|
animation: wr-overlay-in var(--wr-overlay-duration) var(--wr-overlay-ease);
|
|
17
|
+
// rtl-ok: scale-in growth corner — `transform-origin` takes no logical keywords
|
|
16
18
|
transform-origin: top left;
|
|
17
19
|
}
|
|
18
20
|
}
|
|
@@ -30,3 +32,18 @@
|
|
|
30
32
|
gap: 0.375rem;
|
|
31
33
|
}
|
|
32
34
|
}
|
|
35
|
+
|
|
36
|
+
// The arrow that ties the panel to the control it is asking about — the same one
|
|
37
|
+
// `wr-popover` draws, from the same mixin, because the two are the same kind of
|
|
38
|
+
// anchored non-modal panel and a second copy of the geometry would drift.
|
|
39
|
+
//
|
|
40
|
+
// Four placements, not twelve: `WrPopconfirmPosition` is `top | bottom | left |
|
|
41
|
+
// right`, and the eight `-start` / `-end` rules would be selectors that can
|
|
42
|
+
// never match. Widen this list if the input ever grows them.
|
|
43
|
+
@include arrow.anchored-arrow(
|
|
44
|
+
$pane: '.wr-popconfirm-overlay',
|
|
45
|
+
$panel: '> .wr-popconfirm',
|
|
46
|
+
$background: var(--wr-color-surface),
|
|
47
|
+
$border: var(--wr-color-outline),
|
|
48
|
+
$placements-used: (top, bottom, left, right)
|
|
49
|
+
);
|