ngwr 6.1.1 → 7.0.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/LICENSE +1 -3
- package/README.md +179 -32
- package/affix/styles/_index.scss +12 -0
- package/alert/styles/_index.scss +75 -32
- package/anchor/styles/_index.scss +59 -0
- package/aurora/aurora.scss +1 -0
- package/aurora/styles/_index.scss +30 -0
- package/avatar/styles/_index.scss +30 -14
- package/back-top/styles/_index.scss +42 -0
- package/badge/styles/_index.scss +80 -0
- package/badge/styles/_tag.scss +81 -0
- package/bar-chart/styles/_index.scss +56 -0
- package/blur-text/blur-text.scss +1 -0
- package/blur-text/styles/_index.scss +14 -0
- package/border-glow/border-glow.scss +4 -0
- package/border-glow/styles/_index.scss +228 -0
- package/breadcrumbs/breadcrumbs.scss +1 -0
- package/breadcrumbs/styles/_index.scss +76 -0
- package/button/styles/_index.scss +148 -125
- package/calendar/styles/_index.scss +252 -0
- package/calendar-heatmap/styles/_index.scss +69 -0
- package/card/card.scss +1 -0
- package/card/styles/_index.scss +96 -0
- package/carousel/styles/_index.scss +102 -0
- package/cascader/cascader.scss +1 -0
- package/cascader/styles/_index.scss +174 -0
- package/checkbox/styles/_index.scss +76 -60
- package/circular-text/circular-text.scss +1 -0
- package/circular-text/styles/_index.scss +45 -0
- package/click-spark/click-spark.scss +1 -0
- package/click-spark/styles/_index.scss +16 -0
- package/collapse/styles/_index.scss +92 -0
- package/color-picker/styles/_index.scss +247 -0
- package/command-palette/styles/_index.scss +188 -0
- package/compare/styles/_index.scss +100 -0
- package/context-menu/styles/_index.scss +112 -0
- package/counter/styles/_index.scss +44 -0
- package/date-picker/styles/_index.scss +204 -0
- package/decrypt-text/decrypt-text.scss +1 -0
- package/decrypt-text/styles/_index.scss +26 -0
- package/density/styles/_index.scss +37 -0
- package/descriptions/styles/_index.scss +104 -0
- package/dialog/styles/_index.scss +101 -27
- package/divider/styles/_index.scss +43 -24
- package/donut-chart/styles/_index.scss +76 -0
- package/drag-drop/sortable-list.scss +1 -0
- package/drag-drop/styles/_index.scss +99 -0
- package/drawer/styles/_index.scss +164 -0
- package/dropdown/styles/_index.scss +76 -56
- package/empty/styles/_index.scss +49 -0
- package/falling-text/falling-text.scss +1 -0
- package/falling-text/styles/_index.scss +30 -0
- package/fesm2022/ngwr-affix.mjs +102 -0
- package/fesm2022/ngwr-affix.mjs.map +1 -0
- package/fesm2022/ngwr-alert.mjs +70 -52
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-anchor.mjs +103 -0
- package/fesm2022/ngwr-anchor.mjs.map +1 -0
- package/fesm2022/ngwr-aurora.mjs +309 -0
- package/fesm2022/ngwr-aurora.mjs.map +1 -0
- package/fesm2022/ngwr-avatar.mjs +81 -71
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-back-top.mjs +81 -0
- package/fesm2022/ngwr-back-top.mjs.map +1 -0
- package/fesm2022/ngwr-badge.mjs +192 -0
- package/fesm2022/ngwr-badge.mjs.map +1 -0
- package/fesm2022/ngwr-bar-chart.mjs +69 -0
- package/fesm2022/ngwr-bar-chart.mjs.map +1 -0
- package/fesm2022/ngwr-blur-text.mjs +181 -0
- package/fesm2022/ngwr-blur-text.mjs.map +1 -0
- package/fesm2022/ngwr-border-glow.mjs +278 -0
- package/fesm2022/ngwr-border-glow.mjs.map +1 -0
- package/fesm2022/ngwr-breadcrumbs.mjs +101 -0
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -0
- package/fesm2022/ngwr-button.mjs +179 -104
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +145 -0
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -0
- package/fesm2022/ngwr-calendar.mjs +402 -0
- package/fesm2022/ngwr-calendar.mjs.map +1 -0
- package/fesm2022/ngwr-card.mjs +100 -0
- package/fesm2022/ngwr-card.mjs.map +1 -0
- package/fesm2022/ngwr-carousel.mjs +128 -0
- package/fesm2022/ngwr-carousel.mjs.map +1 -0
- package/fesm2022/ngwr-cascader.mjs +280 -0
- package/fesm2022/ngwr-cascader.mjs.map +1 -0
- package/fesm2022/ngwr-checkbox.mjs +214 -54
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +168 -0
- package/fesm2022/ngwr-circular-text.mjs.map +1 -0
- package/fesm2022/ngwr-click-spark.mjs +163 -0
- package/fesm2022/ngwr-click-spark.mjs.map +1 -0
- package/fesm2022/ngwr-clipboard.mjs +130 -0
- package/fesm2022/ngwr-clipboard.mjs.map +1 -0
- package/fesm2022/ngwr-collapse.mjs +232 -0
- package/fesm2022/ngwr-collapse.mjs.map +1 -0
- package/fesm2022/ngwr-color-picker.mjs +646 -0
- package/fesm2022/ngwr-color-picker.mjs.map +1 -0
- package/fesm2022/ngwr-command-palette.mjs +259 -0
- package/fesm2022/ngwr-command-palette.mjs.map +1 -0
- package/fesm2022/ngwr-compare.mjs +137 -0
- package/fesm2022/ngwr-compare.mjs.map +1 -0
- package/fesm2022/ngwr-confetti.mjs +160 -0
- package/fesm2022/ngwr-confetti.mjs.map +1 -0
- package/fesm2022/ngwr-context-menu.mjs +653 -0
- package/fesm2022/ngwr-context-menu.mjs.map +1 -0
- package/fesm2022/ngwr-cookie.mjs +127 -0
- package/fesm2022/ngwr-cookie.mjs.map +1 -0
- package/fesm2022/ngwr-counter.mjs +415 -0
- package/fesm2022/ngwr-counter.mjs.map +1 -0
- package/fesm2022/ngwr-date-adapter-fns.mjs +180 -0
- package/fesm2022/ngwr-date-adapter-fns.mjs.map +1 -0
- package/fesm2022/ngwr-date-adapter-luxon.mjs +201 -0
- package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -0
- package/fesm2022/ngwr-date-adapter.mjs +419 -0
- package/fesm2022/ngwr-date-adapter.mjs.map +1 -0
- package/fesm2022/ngwr-date-picker.mjs +624 -0
- package/fesm2022/ngwr-date-picker.mjs.map +1 -0
- package/fesm2022/ngwr-decrypt-text.mjs +396 -0
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -0
- package/fesm2022/ngwr-density.mjs +157 -0
- package/fesm2022/ngwr-density.mjs.map +1 -0
- package/fesm2022/ngwr-descriptions.mjs +83 -0
- package/fesm2022/ngwr-descriptions.mjs.map +1 -0
- package/fesm2022/ngwr-dialog.mjs +249 -377
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +315 -0
- package/fesm2022/ngwr-directives.mjs.map +1 -0
- package/fesm2022/ngwr-divider.mjs +74 -59
- package/fesm2022/ngwr-divider.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart.mjs +131 -0
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -0
- package/fesm2022/ngwr-drag-drop.mjs +117 -0
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -0
- package/fesm2022/ngwr-drawer.mjs +315 -0
- package/fesm2022/ngwr-drawer.mjs.map +1 -0
- package/fesm2022/ngwr-dropdown.mjs +276 -229
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +57 -0
- package/fesm2022/ngwr-empty.mjs.map +1 -0
- package/fesm2022/ngwr-falling-text.mjs +316 -0
- package/fesm2022/ngwr-falling-text.mjs.map +1 -0
- package/fesm2022/ngwr-file-upload.mjs +260 -0
- package/fesm2022/ngwr-file-upload.mjs.map +1 -0
- package/fesm2022/ngwr-form.mjs +133 -48
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +373 -0
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -0
- package/fesm2022/ngwr-gauge.mjs +83 -0
- package/fesm2022/ngwr-gauge.mjs.map +1 -0
- package/fesm2022/ngwr-glitch-text.mjs +67 -0
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -0
- package/fesm2022/ngwr-gradient-text.mjs +109 -0
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -0
- package/fesm2022/ngwr-hotkey.mjs +263 -0
- package/fesm2022/ngwr-hotkey.mjs.map +1 -0
- package/fesm2022/ngwr-i18n-en.mjs +168 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -0
- package/fesm2022/ngwr-i18n-ru.mjs +156 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -0
- package/fesm2022/ngwr-i18n.mjs +478 -0
- package/fesm2022/ngwr-i18n.mjs.map +1 -0
- package/fesm2022/ngwr-icon-adapters-feather.mjs +74 -0
- package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -0
- package/fesm2022/ngwr-icon-adapters-lucide.mjs +88 -0
- package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -0
- package/fesm2022/ngwr-icon.mjs +189 -649
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper.mjs +299 -0
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -0
- package/fesm2022/ngwr-input-number.mjs +239 -0
- package/fesm2022/ngwr-input-number.mjs.map +1 -0
- package/fesm2022/ngwr-input-otp.mjs +216 -0
- package/fesm2022/ngwr-input-otp.mjs.map +1 -0
- package/fesm2022/ngwr-input.mjs +183 -104
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-keyboard.mjs +47 -0
- package/fesm2022/ngwr-keyboard.mjs.map +1 -0
- package/fesm2022/ngwr-knob.mjs +227 -0
- package/fesm2022/ngwr-knob.mjs.map +1 -0
- package/fesm2022/ngwr-layout.mjs +197 -0
- package/fesm2022/ngwr-layout.mjs.map +1 -0
- package/fesm2022/ngwr-lightbox.mjs +131 -0
- package/fesm2022/ngwr-lightbox.mjs.map +1 -0
- package/fesm2022/ngwr-line-chart.mjs +176 -0
- package/fesm2022/ngwr-line-chart.mjs.map +1 -0
- package/fesm2022/ngwr-list.mjs +132 -0
- package/fesm2022/ngwr-list.mjs.map +1 -0
- package/fesm2022/ngwr-loading-bar.mjs +160 -0
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -0
- package/fesm2022/ngwr-marquee.mjs +224 -0
- package/fesm2022/ngwr-marquee.mjs.map +1 -0
- package/fesm2022/ngwr-media.mjs +132 -0
- package/fesm2022/ngwr-media.mjs.map +1 -0
- package/fesm2022/ngwr-mention.mjs +374 -0
- package/fesm2022/ngwr-mention.mjs.map +1 -0
- package/fesm2022/ngwr-meta.mjs +266 -0
- package/fesm2022/ngwr-meta.mjs.map +1 -0
- package/fesm2022/ngwr-meter-group.mjs +73 -0
- package/fesm2022/ngwr-meter-group.mjs.map +1 -0
- package/fesm2022/ngwr-overlay.mjs +145 -0
- package/fesm2022/ngwr-overlay.mjs.map +1 -0
- package/fesm2022/ngwr-page-header.mjs +53 -0
- package/fesm2022/ngwr-page-header.mjs.map +1 -0
- package/fesm2022/ngwr-pagination.mjs +152 -96
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +290 -11
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +63 -0
- package/fesm2022/ngwr-platform.mjs.map +1 -0
- package/fesm2022/ngwr-popconfirm.mjs +188 -0
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -0
- package/fesm2022/ngwr-popover.mjs +361 -0
- package/fesm2022/ngwr-popover.mjs.map +1 -0
- package/fesm2022/ngwr-progress.mjs +47 -39
- package/fesm2022/ngwr-progress.mjs.map +1 -1
- package/fesm2022/ngwr-qr.mjs +160 -179
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio.mjs +204 -0
- package/fesm2022/ngwr-radio.mjs.map +1 -0
- package/fesm2022/ngwr-rating.mjs +180 -0
- package/fesm2022/ngwr-rating.mjs.map +1 -0
- package/fesm2022/ngwr-result.mjs +119 -0
- package/fesm2022/ngwr-result.mjs.map +1 -0
- package/fesm2022/ngwr-rotating-text.mjs +256 -0
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -0
- package/fesm2022/ngwr-scramble-text.mjs +165 -0
- package/fesm2022/ngwr-scramble-text.mjs.map +1 -0
- package/fesm2022/ngwr-scroll.mjs +107 -0
- package/fesm2022/ngwr-scroll.mjs.map +1 -0
- package/fesm2022/ngwr-segmented.mjs +96 -0
- package/fesm2022/ngwr-segmented.mjs.map +1 -0
- package/fesm2022/ngwr-select.mjs +844 -309
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +127 -0
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -0
- package/fesm2022/ngwr-sidebar.mjs +134 -0
- package/fesm2022/ngwr-sidebar.mjs.map +1 -0
- package/fesm2022/ngwr-skeleton.mjs +59 -34
- package/fesm2022/ngwr-skeleton.mjs.map +1 -1
- package/fesm2022/ngwr-slider.mjs +234 -0
- package/fesm2022/ngwr-slider.mjs.map +1 -0
- package/fesm2022/ngwr-sparkline.mjs +104 -0
- package/fesm2022/ngwr-sparkline.mjs.map +1 -0
- package/fesm2022/ngwr-speed-dial.mjs +79 -0
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -0
- package/fesm2022/ngwr-spinner.mjs +34 -28
- package/fesm2022/ngwr-spinner.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor.mjs +970 -0
- package/fesm2022/ngwr-splash-cursor.mjs.map +1 -0
- package/fesm2022/ngwr-split-text.mjs +200 -0
- package/fesm2022/ngwr-split-text.mjs.map +1 -0
- package/fesm2022/ngwr-splitter.mjs +125 -0
- package/fesm2022/ngwr-splitter.mjs.map +1 -0
- package/fesm2022/ngwr-spotlight-card.mjs +138 -0
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -0
- package/fesm2022/ngwr-squircle.mjs +320 -0
- package/fesm2022/ngwr-squircle.mjs.map +1 -0
- package/fesm2022/ngwr-star-border.mjs +72 -0
- package/fesm2022/ngwr-star-border.mjs.map +1 -0
- package/fesm2022/ngwr-statistic.mjs +220 -0
- package/fesm2022/ngwr-statistic.mjs.map +1 -0
- package/fesm2022/ngwr-stepper.mjs +186 -0
- package/fesm2022/ngwr-stepper.mjs.map +1 -0
- package/fesm2022/ngwr-storage.mjs +317 -0
- package/fesm2022/ngwr-storage.mjs.map +1 -0
- package/fesm2022/ngwr-switch.mjs +99 -0
- package/fesm2022/ngwr-switch.mjs.map +1 -0
- package/fesm2022/ngwr-table.mjs +217 -206
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +217 -0
- package/fesm2022/ngwr-tabs.mjs.map +1 -0
- package/fesm2022/ngwr-textarea.mjs +106 -120
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +152 -0
- package/fesm2022/ngwr-theme.mjs.map +1 -0
- package/fesm2022/ngwr-tilt-card.mjs +137 -0
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -0
- package/fesm2022/ngwr-timeline.mjs +73 -0
- package/fesm2022/ngwr-timeline.mjs.map +1 -0
- package/fesm2022/ngwr-toast.mjs +446 -0
- package/fesm2022/ngwr-toast.mjs.map +1 -0
- package/fesm2022/ngwr-toolbar.mjs +46 -0
- package/fesm2022/ngwr-toolbar.mjs.map +1 -0
- package/fesm2022/ngwr-tree.mjs +483 -0
- package/fesm2022/ngwr-tree.mjs.map +1 -0
- package/fesm2022/ngwr-typewriter.mjs +229 -0
- package/fesm2022/ngwr-typewriter.mjs.map +1 -0
- package/fesm2022/ngwr-typography.mjs +79 -0
- package/fesm2022/ngwr-typography.mjs.map +1 -0
- package/fesm2022/ngwr-utils.mjs +535 -0
- package/fesm2022/ngwr-utils.mjs.map +1 -0
- package/fesm2022/ngwr-validators.mjs +214 -0
- package/fesm2022/ngwr-validators.mjs.map +1 -0
- package/fesm2022/ngwr-version.mjs +51 -0
- package/fesm2022/ngwr-version.mjs.map +1 -0
- package/fesm2022/ngwr-virtual-scroll.mjs +99 -0
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -0
- package/fesm2022/ngwr-waves.mjs +356 -0
- package/fesm2022/ngwr-waves.mjs.map +1 -0
- package/fesm2022/ngwr-window.mjs +1141 -0
- package/fesm2022/ngwr-window.mjs.map +1 -0
- package/fesm2022/ngwr.mjs +0 -3
- package/fesm2022/ngwr.mjs.map +1 -1
- package/file-upload/styles/_index.scss +152 -0
- package/form/styles/_index.scss +85 -42
- package/gauge/styles/_index.scss +37 -0
- package/glitch-text/glitch-text.scss +1 -0
- package/glitch-text/styles/_index.scss +144 -0
- package/gradient-text/gradient-text.scss +1 -0
- package/gradient-text/styles/_index.scss +113 -0
- package/icon/styles/_index.scss +21 -15
- package/image-cropper/styles/_index.scss +114 -0
- package/input/styles/_index.scss +133 -93
- package/input-number/styles/_index.scss +75 -0
- package/input-otp/styles/_index.scss +53 -0
- package/keyboard/styles/_index.scss +55 -0
- package/knob/styles/_index.scss +54 -0
- package/layout/styles/_index.scss +65 -0
- package/lightbox/lightbox.scss +1 -0
- package/lightbox/styles/_index.scss +144 -0
- package/line-chart/styles/_index.scss +93 -0
- package/list/list.scss +1 -0
- package/list/styles/_index.scss +101 -0
- package/loading-bar/loading-bar.scss +1 -0
- package/loading-bar/styles/_index.scss +29 -0
- package/marquee/marquee.scss +1 -0
- package/marquee/styles/_index.scss +138 -0
- package/mention/styles/_index.scss +47 -0
- package/meter-group/styles/_index.scss +61 -0
- package/package.json +576 -99
- package/page-header/styles/_index.scss +78 -0
- package/pagination/styles/_index.scss +89 -34
- package/popconfirm/styles/_index.scss +32 -0
- package/popover/styles/_index.scss +83 -0
- package/progress/styles/_index.scss +29 -14
- package/qr/styles/_index.scss +6 -3
- package/radio/styles/_index.scss +111 -0
- package/rating/styles/_index.scss +63 -0
- package/result/styles/_index.scss +69 -0
- package/rotating-text/rotating-text.scss +1 -0
- package/rotating-text/styles/_index.scss +39 -0
- package/schematics/collection.json +37 -0
- package/schematics/component-style/index.js +49 -0
- package/{cdk/utils/index.d.ts → schematics/component-style/schema.js} +2 -8
- package/schematics/component-style/schema.json +20 -0
- package/schematics/icon-set/index.js +96 -0
- package/schematics/icon-set/schema.js +8 -0
- package/schematics/icon-set/schema.json +39 -0
- package/schematics/migrations/v7/index.js +187 -0
- package/schematics/migrations.json +10 -0
- package/schematics/ng-add/index.js +180 -0
- package/schematics/ng-add/schema.js +8 -0
- package/schematics/ng-add/schema.json +80 -0
- package/schematics/page/index.js +63 -0
- package/schematics/page/schema.js +8 -0
- package/schematics/page/schema.json +39 -0
- package/schematics/page/templates.js +198 -0
- package/schematics/provider/index.js +126 -0
- package/schematics/provider/schema.js +8 -0
- package/schematics/provider/schema.json +52 -0
- package/schematics/use/index.js +143 -0
- package/schematics/use/schema.js +8 -0
- package/schematics/use/schema.json +25 -0
- package/schematics/use/symbol-map.json +356 -0
- package/scramble-text/scramble-text.scss +1 -0
- package/scramble-text/styles/_index.scss +8 -0
- package/segmented/styles/_index.scss +92 -0
- package/select/styles/_index.scss +245 -159
- package/shiny-text/shiny-text.scss +1 -0
- package/shiny-text/styles/_index.scss +101 -0
- package/sidebar/styles/_index.scss +208 -0
- package/skeleton/styles/_index.scss +33 -33
- package/slider/styles/_index.scss +77 -0
- package/sparkline/styles/_index.scss +9 -0
- package/speed-dial/styles/_index.scss +158 -0
- package/spinner/styles/_index.scss +38 -22
- package/splash-cursor/splash-cursor.scss +1 -0
- package/splash-cursor/styles/_index.scss +27 -0
- package/split-text/split-text.scss +1 -0
- package/split-text/styles/_index.scss +22 -0
- package/splitter/styles/_index.scss +85 -0
- package/spotlight-card/spotlight-card.scss +1 -0
- package/spotlight-card/styles/_index.scss +43 -0
- package/squircle/styles/_index.scss +34 -0
- package/star-border/star-border.scss +1 -0
- package/star-border/styles/_index.scss +118 -0
- package/statistic/styles/_index.scss +54 -0
- package/stepper/styles/_index.scss +162 -0
- package/styles/_animations.scss +198 -0
- package/styles/_breakpoints.scss +75 -0
- package/styles/_grid.scss +71 -0
- package/styles/_reset.scss +123 -0
- package/styles/_typography.scss +122 -0
- package/styles.scss +102 -13
- package/switch/styles/_index.scss +82 -0
- package/table/styles/_index.scss +255 -4
- package/tabs/styles/_index.scss +71 -0
- package/textarea/styles/_index.scss +47 -56
- package/theme/styles/_colors.scss +83 -0
- package/theme/styles/_dark.scss +85 -0
- package/theme/styles/_focus.scss +93 -0
- package/theme/styles/_index.scss +5 -0
- package/theme/styles/_mixins.scss +57 -0
- package/theme/styles/_variables.scss +118 -0
- package/tilt-card/styles/_index.scss +40 -0
- package/tilt-card/tilt-card.scss +1 -0
- package/timeline/styles/_index.scss +127 -0
- package/toast/styles/_index.scss +263 -0
- package/toolbar/styles/_index.scss +65 -0
- package/tree/styles/_index.scss +271 -0
- package/types/ngwr-affix.d.ts +43 -0
- package/types/ngwr-alert.d.ts +68 -0
- package/types/ngwr-anchor.d.ts +53 -0
- package/types/ngwr-aurora.d.ts +52 -0
- package/types/ngwr-avatar.d.ts +90 -0
- package/types/ngwr-back-top.d.ts +37 -0
- package/types/ngwr-badge.d.ts +158 -0
- package/types/ngwr-bar-chart.d.ts +47 -0
- package/types/ngwr-blur-text.d.ts +62 -0
- package/types/ngwr-border-glow.d.ts +66 -0
- package/types/ngwr-breadcrumbs.d.ts +61 -0
- package/types/ngwr-button.d.ts +176 -0
- package/types/ngwr-calendar-heatmap.d.ts +58 -0
- package/types/ngwr-calendar.d.ts +116 -0
- package/types/ngwr-card.d.ts +50 -0
- package/types/ngwr-carousel.d.ts +57 -0
- package/types/ngwr-cascader.d.ts +107 -0
- package/types/ngwr-checkbox.d.ts +139 -0
- package/types/ngwr-circular-text.d.ts +49 -0
- package/types/ngwr-click-spark.d.ts +52 -0
- package/types/ngwr-clipboard.d.ts +53 -0
- package/types/ngwr-collapse.d.ts +139 -0
- package/types/ngwr-color-picker.d.ts +302 -0
- package/types/ngwr-command-palette.d.ts +113 -0
- package/types/ngwr-compare.d.ts +55 -0
- package/types/ngwr-confetti.d.ts +70 -0
- package/types/ngwr-context-menu.d.ts +207 -0
- package/types/ngwr-cookie.d.ts +60 -0
- package/types/ngwr-counter.d.ts +182 -0
- package/types/ngwr-date-adapter-fns.d.ts +73 -0
- package/types/ngwr-date-adapter-luxon.d.ts +73 -0
- package/types/ngwr-date-adapter.d.ts +198 -0
- package/types/ngwr-date-picker.d.ts +117 -0
- package/types/ngwr-decrypt-text.d.ts +85 -0
- package/types/ngwr-density.d.ts +97 -0
- package/types/ngwr-descriptions.d.ts +41 -0
- package/types/ngwr-dialog.d.ts +210 -0
- package/types/ngwr-directives.d.ts +125 -0
- package/types/ngwr-divider.d.ts +73 -0
- package/types/ngwr-donut-chart.d.ts +53 -0
- package/types/ngwr-drag-drop.d.ts +83 -0
- package/types/ngwr-drawer.d.ts +155 -0
- package/types/ngwr-dropdown.d.ts +124 -0
- package/types/ngwr-empty.d.ts +38 -0
- package/types/ngwr-falling-text.d.ts +63 -0
- package/types/ngwr-file-upload.d.ts +94 -0
- package/types/ngwr-form.d.ts +95 -0
- package/types/ngwr-fuzzy-text.d.ts +74 -0
- package/types/ngwr-gauge.d.ts +39 -0
- package/types/ngwr-glitch-text.d.ts +31 -0
- package/types/ngwr-gradient-text.d.ts +46 -0
- package/types/ngwr-hotkey.d.ts +142 -0
- package/types/ngwr-i18n-en.d.ts +18 -0
- package/types/ngwr-i18n-ru.d.ts +6 -0
- package/types/ngwr-i18n.d.ts +336 -0
- package/types/ngwr-icon-adapters-feather.d.ts +44 -0
- package/types/ngwr-icon-adapters-lucide.d.ts +54 -0
- package/types/ngwr-icon.d.ts +180 -0
- package/types/ngwr-image-cropper.d.ts +110 -0
- package/types/ngwr-input-number.d.ts +77 -0
- package/types/ngwr-input-otp.d.ts +78 -0
- package/types/ngwr-input.d.ts +131 -0
- package/types/ngwr-keyboard.d.ts +34 -0
- package/types/ngwr-knob.d.ts +77 -0
- package/types/ngwr-layout.d.ts +89 -0
- package/types/ngwr-lightbox.d.ts +50 -0
- package/types/ngwr-line-chart.d.ts +90 -0
- package/types/ngwr-list.d.ts +71 -0
- package/types/ngwr-loading-bar.d.ts +79 -0
- package/types/ngwr-marquee.d.ts +95 -0
- package/types/ngwr-media.d.ts +74 -0
- package/types/ngwr-mention.d.ts +110 -0
- package/types/ngwr-meta.d.ts +164 -0
- package/types/ngwr-meter-group.d.ts +57 -0
- package/types/ngwr-overlay.d.ts +110 -0
- package/types/ngwr-page-header.d.ts +35 -0
- package/types/ngwr-pagination.d.ts +119 -0
- package/types/ngwr-pipes.d.ts +177 -0
- package/types/ngwr-platform.d.ts +38 -0
- package/types/ngwr-popconfirm.d.ts +79 -0
- package/types/ngwr-popover.d.ts +90 -0
- package/types/ngwr-progress.d.ts +33 -0
- package/types/ngwr-qr.d.ts +56 -0
- package/types/ngwr-radio.d.ts +120 -0
- package/types/ngwr-rating.d.ts +62 -0
- package/types/ngwr-result.d.ts +64 -0
- package/types/ngwr-rotating-text.d.ts +72 -0
- package/types/ngwr-scramble-text.d.ts +49 -0
- package/types/ngwr-scroll.d.ts +65 -0
- package/types/ngwr-segmented.d.ts +65 -0
- package/types/ngwr-select.d.ts +413 -0
- package/types/ngwr-shiny-text.d.ts +70 -0
- package/types/ngwr-sidebar.d.ts +83 -0
- package/types/ngwr-skeleton.d.ts +41 -0
- package/types/ngwr-slider.d.ts +86 -0
- package/types/ngwr-sparkline.d.ts +48 -0
- package/types/ngwr-speed-dial.d.ts +51 -0
- package/types/ngwr-spinner.d.ts +41 -0
- package/types/ngwr-splash-cursor.d.ts +94 -0
- package/types/ngwr-split-text.d.ts +83 -0
- package/types/ngwr-splitter.d.ts +52 -0
- package/types/ngwr-spotlight-card.d.ts +71 -0
- package/types/ngwr-squircle.d.ts +132 -0
- package/types/ngwr-star-border.d.ts +40 -0
- package/types/ngwr-statistic.d.ts +88 -0
- package/types/ngwr-stepper.d.ts +99 -0
- package/types/ngwr-storage.d.ts +178 -0
- package/types/ngwr-switch.d.ts +39 -0
- package/types/ngwr-table.d.ts +237 -0
- package/types/ngwr-tabs.d.ts +125 -0
- package/types/ngwr-textarea.d.ts +60 -0
- package/types/ngwr-theme.d.ts +124 -0
- package/types/ngwr-tilt-card.d.ts +63 -0
- package/types/ngwr-timeline.d.ts +40 -0
- package/types/ngwr-toast.d.ts +230 -0
- package/types/ngwr-toolbar.d.ts +30 -0
- package/types/ngwr-tree.d.ts +150 -0
- package/types/ngwr-typewriter.d.ts +84 -0
- package/types/ngwr-typography.d.ts +56 -0
- package/types/ngwr-utils.d.ts +447 -0
- package/types/ngwr-validators.d.ts +70 -0
- package/types/ngwr-version.d.ts +44 -0
- package/types/ngwr-virtual-scroll.d.ts +64 -0
- package/types/ngwr-waves.d.ts +71 -0
- package/types/ngwr-window.d.ts +437 -0
- package/typewriter/styles/_index.scss +33 -0
- package/typewriter/typewriter.scss +1 -0
- package/typography/styles/_index.scss +211 -0
- package/virtual-scroll/styles/_index.scss +27 -0
- package/virtual-scroll/virtual-scroll.scss +1 -0
- package/waves/styles/_index.scss +24 -0
- package/waves/waves.scss +1 -0
- package/window/styles/_index.scss +430 -0
- package/alert/index.d.ts +0 -32
- package/alert/styles/theming.scss +0 -26
- package/alert/styles/vars.scss +0 -11
- package/avatar/index.d.ts +0 -30
- package/button/index.d.ts +0 -45
- package/button/styles/vars.scss +0 -13
- package/cdk/index.d.ts +0 -24
- package/cdk/rxjs/index.d.ts +0 -40
- package/cdk/types/index.d.ts +0 -27
- package/checkbox/index.d.ts +0 -31
- package/checkbox/styles/vars.scss +0 -16
- package/dialog/index.d.ts +0 -193
- package/dialog/styles/vars.scss +0 -7
- package/divider/index.d.ts +0 -16
- package/dropdown/index.d.ts +0 -68
- package/dropdown/styles/vars.scss +0 -18
- package/fesm2022/ngwr-cdk-rxjs.mjs +0 -53
- package/fesm2022/ngwr-cdk-rxjs.mjs.map +0 -1
- package/fesm2022/ngwr-cdk-types.mjs +0 -44
- package/fesm2022/ngwr-cdk-types.mjs.map +0 -1
- package/fesm2022/ngwr-cdk-utils.mjs +0 -30
- package/fesm2022/ngwr-cdk-utils.mjs.map +0 -1
- package/fesm2022/ngwr-cdk.mjs +0 -46
- package/fesm2022/ngwr-cdk.mjs.map +0 -1
- package/fesm2022/ngwr-tag.mjs +0 -87
- package/fesm2022/ngwr-tag.mjs.map +0 -1
- package/form/index.d.ts +0 -28
- package/icon/index.d.ts +0 -830
- package/icon/styles/vars.scss +0 -3
- package/input/index.d.ts +0 -43
- package/input/styles/vars.scss +0 -17
- package/pagination/index.d.ts +0 -34
- package/pagination/styles/vars.scss +0 -14
- package/pipes/index.d.ts +0 -17
- package/progress/index.d.ts +0 -13
- package/progress/styles/theming.scss +0 -8
- package/progress/styles/vars.scss +0 -6
- package/qr/index.d.ts +0 -127
- package/select/index.d.ts +0 -121
- package/select/styles/vars.scss +0 -32
- package/skeleton/index.d.ts +0 -13
- package/spinner/index.d.ts +0 -23
- package/styles/animations.scss +0 -92
- package/styles/cdk.scss +0 -5
- package/styles/colors.scss +0 -72
- package/styles/grid.scss +0 -99
- package/styles/maps.scss +0 -31
- package/styles/reset.scss +0 -112
- package/styles/variables.scss +0 -72
- package/table/index.d.ts +0 -101
- package/table/styles/table-filter.scss +0 -202
- package/table/styles/table-sort.scss +0 -47
- package/table/styles/table.scss +0 -232
- package/table/styles/vars.scss +0 -21
- package/tag/index.d.ts +0 -24
- package/tag/styles/_index.scss +0 -32
- package/tag/styles/theming.scss +0 -21
- package/tag/styles/vars.scss +0 -12
- package/textarea/index.d.ts +0 -47
- package/textarea/styles/vars.scss +0 -17
- /package/{index.d.ts → types/ngwr.d.ts} +0 -0
|
@@ -0,0 +1,1141 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, model, input, computed, output, inject, signal, afterNextRender, effect, ViewEncapsulation, Component, Injector, DestroyRef, viewChild, ViewContainerRef, runInInjectionContext, ChangeDetectionStrategy, EnvironmentInjector, PLATFORM_ID, Service } from '@angular/core';
|
|
3
|
+
import { ComponentPortal } from '@angular/cdk/portal';
|
|
4
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
5
|
+
import { WR_OVERLAY } from 'ngwr/overlay';
|
|
6
|
+
import { WrStorage } from 'ngwr/storage';
|
|
7
|
+
import { randomId, clamp } from 'ngwr/utils';
|
|
8
|
+
import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
9
|
+
import { Subject } from 'rxjs';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @license
|
|
13
|
+
*
|
|
14
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
15
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
16
|
+
*/
|
|
17
|
+
/** Resolves to the active `WrWindowRef` inside a programmatically-opened window's content tree. */
|
|
18
|
+
const WR_WINDOW_REF = new InjectionToken('WR_WINDOW_REF');
|
|
19
|
+
/** Resolves to the `data` passed in `WrWindowConfig.data`. */
|
|
20
|
+
const WR_WINDOW_DATA = new InjectionToken('WR_WINDOW_DATA');
|
|
21
|
+
|
|
22
|
+
/** Namespaced localStorage key for a persisted window / workspace. */
|
|
23
|
+
function storageKey(cfg) {
|
|
24
|
+
const prefix = cfg.prefix ? `${cfg.prefix}:` : '';
|
|
25
|
+
return `wr:window:${prefix}${cfg.key}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @license
|
|
30
|
+
*
|
|
31
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
32
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Best-effort OS detection — runs once at module load. Looks at the
|
|
36
|
+
* new `navigator.userAgentData.platform` first (Chromium, no UA
|
|
37
|
+
* sniffing required), falls back to `navigator.platform` /
|
|
38
|
+
* `navigator.userAgent`. SSR-safe — returns `'windows'` when there's
|
|
39
|
+
* no `window`.
|
|
40
|
+
*/
|
|
41
|
+
function detectOs() {
|
|
42
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined')
|
|
43
|
+
return 'windows';
|
|
44
|
+
const uaData = navigator.userAgentData;
|
|
45
|
+
// First non-empty source — `??` alone would keep an empty platform
|
|
46
|
+
// string and skip the user-agent fallback.
|
|
47
|
+
const candidates = [uaData?.platform, navigator.platform, navigator.userAgent];
|
|
48
|
+
const platform = (candidates.find(v => !!v) ?? '').toLowerCase();
|
|
49
|
+
if (platform.includes('mac') ||
|
|
50
|
+
platform.includes('darwin') ||
|
|
51
|
+
platform.includes('iphone') ||
|
|
52
|
+
platform.includes('ipad')) {
|
|
53
|
+
return 'macos';
|
|
54
|
+
}
|
|
55
|
+
if (platform.includes('linux') || platform.includes('x11') || platform.includes('cros'))
|
|
56
|
+
return 'linux';
|
|
57
|
+
return 'windows';
|
|
58
|
+
}
|
|
59
|
+
const DETECTED_OS = detectOs();
|
|
60
|
+
const SIZE_PRESETS = {
|
|
61
|
+
sm: { width: 320, height: 200 },
|
|
62
|
+
md: { width: 480, height: 320 },
|
|
63
|
+
lg: { width: 720, height: 480 },
|
|
64
|
+
};
|
|
65
|
+
const MIN_ON_VIEWPORT = 24; // keep at least this many px of the window inside the viewport
|
|
66
|
+
function viewportWidth() {
|
|
67
|
+
return typeof window === 'undefined' ? 1024 : window.innerWidth;
|
|
68
|
+
}
|
|
69
|
+
function viewportHeight() {
|
|
70
|
+
return typeof window === 'undefined' ? 768 : window.innerHeight;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Internal rendering primitive for a single floating window.
|
|
74
|
+
*
|
|
75
|
+
* Not part of the public API — windows are opened through
|
|
76
|
+
* `WrWindowManager.open(Component, config)`, which portals this
|
|
77
|
+
* component for each call. Inputs map 1:1 to `WrWindowConfig`
|
|
78
|
+
* fields so the container can wire them up declaratively.
|
|
79
|
+
*
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
class WrWindow {
|
|
83
|
+
/** Two-way bindable visibility. `false` removes the window from the layer. */
|
|
84
|
+
open = model(true, /* @ts-ignore */
|
|
85
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
86
|
+
/** Two-way bindable state. */
|
|
87
|
+
state = model('normal', /* @ts-ignore */
|
|
88
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
89
|
+
/** Header title. */
|
|
90
|
+
title = input('', /* @ts-ignore */
|
|
91
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
92
|
+
/** Auto-generated id for the title element, referenced by `aria-labelledby`. */
|
|
93
|
+
titleId = randomId('wr-window-title');
|
|
94
|
+
/** Initial position. `null` = auto-cascade from the manager. */
|
|
95
|
+
initialX = input(null, { ...(ngDevMode ? { debugName: "initialX" } : /* istanbul ignore next */ {}), transform: (v) => (v == null ? null : coerceNumberProperty(v, 0)) });
|
|
96
|
+
initialY = input(null, { ...(ngDevMode ? { debugName: "initialY" } : /* istanbul ignore next */ {}), transform: (v) => (v == null ? null : coerceNumberProperty(v, 0)) });
|
|
97
|
+
/**
|
|
98
|
+
* Size preset — seeds initial width / height when `[initialWidth]` /
|
|
99
|
+
* `[initialHeight]` are not provided. `null` keeps the explicit
|
|
100
|
+
* pixel inputs in charge. @default 'md'
|
|
101
|
+
*/
|
|
102
|
+
size = input('md', /* @ts-ignore */
|
|
103
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
104
|
+
/** Initial / forced size in pixels. Wins over `[size]` when set. */
|
|
105
|
+
initialWidth = input(null, { ...(ngDevMode ? { debugName: "initialWidth" } : /* istanbul ignore next */ {}), transform: (v) => (v == null ? null : coerceNumberProperty(v, 480)) });
|
|
106
|
+
initialHeight = input(null, { ...(ngDevMode ? { debugName: "initialHeight" } : /* istanbul ignore next */ {}), transform: (v) => (v == null ? null : coerceNumberProperty(v, 320)) });
|
|
107
|
+
/**
|
|
108
|
+
* OS chrome style. `'auto'` reads `navigator.userAgentData.platform`
|
|
109
|
+
* / `navigator.platform` and picks the matching preset (SSR-safe;
|
|
110
|
+
* unknown platforms fall back to `windows`).
|
|
111
|
+
* @default 'auto'
|
|
112
|
+
*/
|
|
113
|
+
os = input('auto', /* @ts-ignore */
|
|
114
|
+
...(ngDevMode ? [{ debugName: "os" }] : /* istanbul ignore next */ []));
|
|
115
|
+
/** Resolved OS — `auto` → detected platform. */
|
|
116
|
+
resolvedOs = computed(() => {
|
|
117
|
+
const o = this.os();
|
|
118
|
+
return o === 'auto' ? DETECTED_OS : o;
|
|
119
|
+
}, /* @ts-ignore */
|
|
120
|
+
...(ngDevMode ? [{ debugName: "resolvedOs" }] : /* istanbul ignore next */ []));
|
|
121
|
+
/** Title-bar density. @default 'normal' */
|
|
122
|
+
chromeSize = input('normal', /* @ts-ignore */
|
|
123
|
+
...(ngDevMode ? [{ debugName: "chromeSize" }] : /* istanbul ignore next */ []));
|
|
124
|
+
/**
|
|
125
|
+
* Enter / leave animations. Set to `false` to disable the open-fade
|
|
126
|
+
* and the minimize / maximize transitions. Honoured automatically
|
|
127
|
+
* when `prefers-reduced-motion: reduce`. @default true
|
|
128
|
+
*/
|
|
129
|
+
animations = input(true, { ...(ngDevMode ? { debugName: "animations" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
130
|
+
minWidth = input(220, { ...(ngDevMode ? { debugName: "minWidth" } : /* istanbul ignore next */ {}), transform: (v) => coerceNumberProperty(v, 220) });
|
|
131
|
+
minHeight = input(140, { ...(ngDevMode ? { debugName: "minHeight" } : /* istanbul ignore next */ {}), transform: (v) => coerceNumberProperty(v, 140) });
|
|
132
|
+
maxWidth = input(Number.POSITIVE_INFINITY, { ...(ngDevMode ? { debugName: "maxWidth" } : /* istanbul ignore next */ {}), transform: (v) => (v == null ? Number.POSITIVE_INFINITY : coerceNumberProperty(v, Infinity)) });
|
|
133
|
+
maxHeight = input(Number.POSITIVE_INFINITY, { ...(ngDevMode ? { debugName: "maxHeight" } : /* istanbul ignore next */ {}), transform: (v) => (v == null ? Number.POSITIVE_INFINITY : coerceNumberProperty(v, Infinity)) });
|
|
134
|
+
movable = input(true, { ...(ngDevMode ? { debugName: "movable" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
135
|
+
resizable = input(true, { ...(ngDevMode ? { debugName: "resizable" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
136
|
+
keepInViewport = input(true, { ...(ngDevMode ? { debugName: "keepInViewport" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
137
|
+
/** Drag-to-edge snap behaviour. @default 'none' */
|
|
138
|
+
snap = input('none', /* @ts-ignore */
|
|
139
|
+
...(ngDevMode ? [{ debugName: "snap" }] : /* istanbul ignore next */ []));
|
|
140
|
+
/**
|
|
141
|
+
* CSS selector inside the projected body that restricts the move
|
|
142
|
+
* grab — pointer-downs outside this selector won't start a drag.
|
|
143
|
+
* Use to keep interactive body content (sliders, inputs) from
|
|
144
|
+
* hijacking the title-bar drag. `null` (default) means the whole
|
|
145
|
+
* title bar is grabbable.
|
|
146
|
+
*/
|
|
147
|
+
dragHandle = input(null, /* @ts-ignore */
|
|
148
|
+
...(ngDevMode ? [{ debugName: "dragHandle" }] : /* istanbul ignore next */ []));
|
|
149
|
+
showMinimize = input(true, { ...(ngDevMode ? { debugName: "showMinimize" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
150
|
+
showMaximize = input(true, { ...(ngDevMode ? { debugName: "showMaximize" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
151
|
+
showClose = input(true, { ...(ngDevMode ? { debugName: "showClose" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
152
|
+
closed = output();
|
|
153
|
+
moved = output();
|
|
154
|
+
resized = output();
|
|
155
|
+
manager = inject(WrWindowManager);
|
|
156
|
+
// Position / size signals are the source of truth at runtime.
|
|
157
|
+
// Inputs (x/y/initialWidth/initialHeight) seed them in afterNextRender.
|
|
158
|
+
/** Live X (top-left). @internal */ x_ = signal(0, /* @ts-ignore */
|
|
159
|
+
...(ngDevMode ? [{ debugName: "x_" }] : /* istanbul ignore next */ []));
|
|
160
|
+
/** Live Y (top-left). @internal */ y_ = signal(0, /* @ts-ignore */
|
|
161
|
+
...(ngDevMode ? [{ debugName: "y_" }] : /* istanbul ignore next */ []));
|
|
162
|
+
/** Live width. @internal */ width_ = signal(0, /* @ts-ignore */
|
|
163
|
+
...(ngDevMode ? [{ debugName: "width_" }] : /* istanbul ignore next */ []));
|
|
164
|
+
/** Live height. @internal */ height_ = signal(0, /* @ts-ignore */
|
|
165
|
+
...(ngDevMode ? [{ debugName: "height_" }] : /* istanbul ignore next */ []));
|
|
166
|
+
// Saved geometry to restore from `maximized`.
|
|
167
|
+
restoreGeometry = null;
|
|
168
|
+
/** Stack z-index — pulled from the manager on focus. @internal */
|
|
169
|
+
z = signal(this.manager.bringToFront(), /* @ts-ignore */
|
|
170
|
+
...(ngDevMode ? [{ debugName: "z" }] : /* istanbul ignore next */ []));
|
|
171
|
+
classes = computed(() => {
|
|
172
|
+
const parts = [
|
|
173
|
+
'wr-window',
|
|
174
|
+
`wr-window--${this.state()}`,
|
|
175
|
+
`wr-window--os-${this.resolvedOs()}`,
|
|
176
|
+
`wr-window--chrome-${this.chromeSize()}`,
|
|
177
|
+
];
|
|
178
|
+
if (!this.open())
|
|
179
|
+
parts.push('wr-window--closed');
|
|
180
|
+
if (this.resizable() && this.state() === 'normal')
|
|
181
|
+
parts.push('wr-window--resizable');
|
|
182
|
+
if (!this.animations())
|
|
183
|
+
parts.push('wr-window--no-anim');
|
|
184
|
+
return parts.join(' ');
|
|
185
|
+
}, /* @ts-ignore */
|
|
186
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
187
|
+
// Effective geometry exposed to the host bindings AND to programmatic
|
|
188
|
+
// consumers (`WrWindowRef`). When maximized we ignore the live
|
|
189
|
+
// x_/y_/width_/height_ and fill the viewport instead.
|
|
190
|
+
x = computed(() => (this.state() === 'maximized' ? 0 : this.x_()), /* @ts-ignore */
|
|
191
|
+
...(ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []));
|
|
192
|
+
y = computed(() => (this.state() === 'maximized' ? 0 : this.y_()), /* @ts-ignore */
|
|
193
|
+
...(ngDevMode ? [{ debugName: "y" }] : /* istanbul ignore next */ []));
|
|
194
|
+
width = computed(() => (this.state() === 'maximized' ? viewportWidth() : this.width_()), /* @ts-ignore */
|
|
195
|
+
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
196
|
+
height = computed(() => this.state() === 'maximized'
|
|
197
|
+
? viewportHeight()
|
|
198
|
+
: this.state() === 'minimized'
|
|
199
|
+
? this.minimizedHeight()
|
|
200
|
+
: this.height_(), /* @ts-ignore */
|
|
201
|
+
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
202
|
+
minimizedHeight = signal(40, /* @ts-ignore */
|
|
203
|
+
...(ngDevMode ? [{ debugName: "minimizedHeight" }] : /* istanbul ignore next */ [])); // header-only height
|
|
204
|
+
/**
|
|
205
|
+
* Visible during drag when a snap target is engaged — drives the
|
|
206
|
+
* SCSS hint rendered by `WrWindowContainer` (NOT here — `.wr-window`
|
|
207
|
+
* has `overflow: hidden` which clips position-fixed descendants
|
|
208
|
+
* during the open animation while a transform is applied).
|
|
209
|
+
*/
|
|
210
|
+
snapTarget = signal(null, /* @ts-ignore */
|
|
211
|
+
...(ngDevMode ? [{ debugName: "snapTarget" }] : /* istanbul ignore next */ []));
|
|
212
|
+
constructor() {
|
|
213
|
+
afterNextRender(() => this.seedInitialGeometry());
|
|
214
|
+
// Re-clamp into viewport when window or viewport changes.
|
|
215
|
+
effect(() => {
|
|
216
|
+
if (this.state() !== 'normal')
|
|
217
|
+
return;
|
|
218
|
+
this.clampToViewport();
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// Public API
|
|
222
|
+
close() {
|
|
223
|
+
this.open.set(false);
|
|
224
|
+
this.closed.emit();
|
|
225
|
+
}
|
|
226
|
+
minimize() {
|
|
227
|
+
this.state.set(this.state() === 'minimized' ? 'normal' : 'minimized');
|
|
228
|
+
}
|
|
229
|
+
maximize() {
|
|
230
|
+
if (this.state() === 'maximized') {
|
|
231
|
+
this.restore();
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.restoreGeometry = { x: this.x_(), y: this.y_(), width: this.width_(), height: this.height_() };
|
|
235
|
+
this.state.set('maximized');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** Force back to `normal` from minimized / maximized. */
|
|
239
|
+
restore() {
|
|
240
|
+
if (this.state() === 'maximized' && this.restoreGeometry) {
|
|
241
|
+
this.x_.set(this.restoreGeometry.x);
|
|
242
|
+
this.y_.set(this.restoreGeometry.y);
|
|
243
|
+
this.width_.set(this.restoreGeometry.width);
|
|
244
|
+
this.height_.set(this.restoreGeometry.height);
|
|
245
|
+
this.restoreGeometry = null;
|
|
246
|
+
}
|
|
247
|
+
this.state.set('normal');
|
|
248
|
+
}
|
|
249
|
+
/** Programmatic move — clamped to the viewport when `keepInViewport`. */
|
|
250
|
+
moveTo(x, y) {
|
|
251
|
+
this.x_.set(x);
|
|
252
|
+
this.y_.set(y);
|
|
253
|
+
this.clampToViewport();
|
|
254
|
+
}
|
|
255
|
+
/** Programmatic resize — clamped to min/max bounds. */
|
|
256
|
+
resizeTo(width, height) {
|
|
257
|
+
this.width_.set(clamp(width, this.minWidth(), this.maxWidth()));
|
|
258
|
+
this.height_.set(clamp(height, this.minHeight(), this.maxHeight()));
|
|
259
|
+
this.clampToViewport();
|
|
260
|
+
}
|
|
261
|
+
/** Re-position centered against the current viewport. */
|
|
262
|
+
center() {
|
|
263
|
+
const w = this.width_();
|
|
264
|
+
const h = this.height_();
|
|
265
|
+
this.x_.set(Math.max(0, (viewportWidth() - w) / 2));
|
|
266
|
+
this.y_.set(Math.max(0, (viewportHeight() - h) / 2));
|
|
267
|
+
}
|
|
268
|
+
/** Bring this window to the top of the stack. */
|
|
269
|
+
focus() {
|
|
270
|
+
this.z.set(this.manager.bringToFront());
|
|
271
|
+
}
|
|
272
|
+
// Host handlers
|
|
273
|
+
/** @internal — alias kept for the host `(pointerdown)` binding. */
|
|
274
|
+
focusWindow() {
|
|
275
|
+
this.focus();
|
|
276
|
+
}
|
|
277
|
+
startMove(event) {
|
|
278
|
+
if (!this.movable() || this.state() !== 'normal')
|
|
279
|
+
return;
|
|
280
|
+
const targetEl = event.target;
|
|
281
|
+
if (targetEl.closest('.wr-window__chrome-action'))
|
|
282
|
+
return;
|
|
283
|
+
const handle = this.dragHandle();
|
|
284
|
+
if (handle && !targetEl.closest(handle))
|
|
285
|
+
return;
|
|
286
|
+
event.preventDefault();
|
|
287
|
+
const target = event.currentTarget;
|
|
288
|
+
target.setPointerCapture(event.pointerId);
|
|
289
|
+
const startX = this.x_();
|
|
290
|
+
const startY = this.y_();
|
|
291
|
+
const originX = event.clientX;
|
|
292
|
+
const originY = event.clientY;
|
|
293
|
+
const move = (e) => {
|
|
294
|
+
this.x_.set(startX + (e.clientX - originX));
|
|
295
|
+
this.y_.set(startY + (e.clientY - originY));
|
|
296
|
+
this.clampToViewport();
|
|
297
|
+
this.snapTarget.set(this.detectSnapTarget(e.clientX, e.clientY));
|
|
298
|
+
};
|
|
299
|
+
const up = (e) => {
|
|
300
|
+
target.releasePointerCapture(e.pointerId);
|
|
301
|
+
target.removeEventListener('pointermove', move);
|
|
302
|
+
target.removeEventListener('pointerup', up);
|
|
303
|
+
const snap = this.snapTarget();
|
|
304
|
+
this.snapTarget.set(null);
|
|
305
|
+
if (snap) {
|
|
306
|
+
this.applySnap(snap);
|
|
307
|
+
}
|
|
308
|
+
this.moved.emit({ x: this.x_(), y: this.y_() });
|
|
309
|
+
};
|
|
310
|
+
target.addEventListener('pointermove', move);
|
|
311
|
+
target.addEventListener('pointerup', up);
|
|
312
|
+
}
|
|
313
|
+
/** Map a cursor position to a snap target, honouring `[snap]`. */
|
|
314
|
+
detectSnapTarget(clientX, clientY) {
|
|
315
|
+
const mode = this.snap();
|
|
316
|
+
if (mode === 'none')
|
|
317
|
+
return null;
|
|
318
|
+
const HOT = 12;
|
|
319
|
+
const vw = viewportWidth();
|
|
320
|
+
const vh = viewportHeight();
|
|
321
|
+
const nearLeft = clientX <= HOT;
|
|
322
|
+
const nearRight = clientX >= vw - HOT;
|
|
323
|
+
const nearTop = clientY <= HOT;
|
|
324
|
+
const nearBottom = clientY >= vh - HOT;
|
|
325
|
+
if (mode === 'all') {
|
|
326
|
+
if (nearTop && nearLeft)
|
|
327
|
+
return 'top-left';
|
|
328
|
+
if (nearTop && nearRight)
|
|
329
|
+
return 'top-right';
|
|
330
|
+
if (nearBottom && nearLeft)
|
|
331
|
+
return 'bottom-left';
|
|
332
|
+
if (nearBottom && nearRight)
|
|
333
|
+
return 'bottom-right';
|
|
334
|
+
}
|
|
335
|
+
if (nearTop)
|
|
336
|
+
return 'top'; // top edge → maximise
|
|
337
|
+
if (nearLeft)
|
|
338
|
+
return 'left';
|
|
339
|
+
if (nearRight)
|
|
340
|
+
return 'right';
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
/** Move + resize the window into the requested snap region. */
|
|
344
|
+
applySnap(target) {
|
|
345
|
+
const vw = viewportWidth();
|
|
346
|
+
const vh = viewportHeight();
|
|
347
|
+
const halfW = Math.floor(vw / 2);
|
|
348
|
+
const halfH = Math.floor(vh / 2);
|
|
349
|
+
switch (target) {
|
|
350
|
+
case 'top':
|
|
351
|
+
// Maximise — share state so the chrome reflects it.
|
|
352
|
+
this.restoreGeometry = { x: this.x_(), y: this.y_(), width: this.width_(), height: this.height_() };
|
|
353
|
+
this.state.set('maximized');
|
|
354
|
+
return;
|
|
355
|
+
case 'left':
|
|
356
|
+
this.x_.set(0);
|
|
357
|
+
this.y_.set(0);
|
|
358
|
+
this.width_.set(halfW);
|
|
359
|
+
this.height_.set(vh);
|
|
360
|
+
break;
|
|
361
|
+
case 'right':
|
|
362
|
+
this.x_.set(halfW);
|
|
363
|
+
this.y_.set(0);
|
|
364
|
+
this.width_.set(vw - halfW);
|
|
365
|
+
this.height_.set(vh);
|
|
366
|
+
break;
|
|
367
|
+
case 'top-left':
|
|
368
|
+
this.x_.set(0);
|
|
369
|
+
this.y_.set(0);
|
|
370
|
+
this.width_.set(halfW);
|
|
371
|
+
this.height_.set(halfH);
|
|
372
|
+
break;
|
|
373
|
+
case 'top-right':
|
|
374
|
+
this.x_.set(halfW);
|
|
375
|
+
this.y_.set(0);
|
|
376
|
+
this.width_.set(vw - halfW);
|
|
377
|
+
this.height_.set(halfH);
|
|
378
|
+
break;
|
|
379
|
+
case 'bottom-left':
|
|
380
|
+
this.x_.set(0);
|
|
381
|
+
this.y_.set(halfH);
|
|
382
|
+
this.width_.set(halfW);
|
|
383
|
+
this.height_.set(vh - halfH);
|
|
384
|
+
break;
|
|
385
|
+
case 'bottom-right':
|
|
386
|
+
this.x_.set(halfW);
|
|
387
|
+
this.y_.set(halfH);
|
|
388
|
+
this.width_.set(vw - halfW);
|
|
389
|
+
this.height_.set(vh - halfH);
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
this.resized.emit({ width: this.width_(), height: this.height_() });
|
|
393
|
+
}
|
|
394
|
+
startResize(event, edges) {
|
|
395
|
+
if (!this.resizable() || this.state() !== 'normal')
|
|
396
|
+
return;
|
|
397
|
+
event.preventDefault();
|
|
398
|
+
event.stopPropagation();
|
|
399
|
+
const target = event.currentTarget;
|
|
400
|
+
target.setPointerCapture(event.pointerId);
|
|
401
|
+
const startX = this.x_();
|
|
402
|
+
const startY = this.y_();
|
|
403
|
+
const startW = this.width_();
|
|
404
|
+
const startH = this.height_();
|
|
405
|
+
const originX = event.clientX;
|
|
406
|
+
const originY = event.clientY;
|
|
407
|
+
const move = (e) => {
|
|
408
|
+
const dx = e.clientX - originX;
|
|
409
|
+
const dy = e.clientY - originY;
|
|
410
|
+
let nextX = startX;
|
|
411
|
+
let nextY = startY;
|
|
412
|
+
let nextW = startW;
|
|
413
|
+
let nextH = startH;
|
|
414
|
+
if (edges.r)
|
|
415
|
+
nextW = startW + dx;
|
|
416
|
+
if (edges.b)
|
|
417
|
+
nextH = startH + dy;
|
|
418
|
+
if (edges.l) {
|
|
419
|
+
nextW = startW - dx;
|
|
420
|
+
nextX = startX + dx;
|
|
421
|
+
}
|
|
422
|
+
if (edges.t) {
|
|
423
|
+
nextH = startH - dy;
|
|
424
|
+
nextY = startY + dy;
|
|
425
|
+
}
|
|
426
|
+
// Enforce min/max — when shrinking from left/top we have to fix the position too.
|
|
427
|
+
const clampedW = clamp(nextW, this.minWidth(), this.maxWidth());
|
|
428
|
+
const clampedH = clamp(nextH, this.minHeight(), this.maxHeight());
|
|
429
|
+
if (edges.l)
|
|
430
|
+
nextX += nextW - clampedW;
|
|
431
|
+
if (edges.t)
|
|
432
|
+
nextY += nextH - clampedH;
|
|
433
|
+
this.x_.set(nextX);
|
|
434
|
+
this.y_.set(nextY);
|
|
435
|
+
this.width_.set(clampedW);
|
|
436
|
+
this.height_.set(clampedH);
|
|
437
|
+
this.clampToViewport();
|
|
438
|
+
};
|
|
439
|
+
const up = (e) => {
|
|
440
|
+
target.releasePointerCapture(e.pointerId);
|
|
441
|
+
target.removeEventListener('pointermove', move);
|
|
442
|
+
target.removeEventListener('pointerup', up);
|
|
443
|
+
this.resized.emit({ width: this.width_(), height: this.height_() });
|
|
444
|
+
};
|
|
445
|
+
target.addEventListener('pointermove', move);
|
|
446
|
+
target.addEventListener('pointerup', up);
|
|
447
|
+
}
|
|
448
|
+
// Bound resize handlers for the template — avoids inline object literals
|
|
449
|
+
// re-creating on every change-detection tick.
|
|
450
|
+
resizeTL = (e) => this.startResize(e, { t: true, l: true });
|
|
451
|
+
resizeT = (e) => this.startResize(e, { t: true });
|
|
452
|
+
resizeTR = (e) => this.startResize(e, { t: true, r: true });
|
|
453
|
+
resizeR = (e) => this.startResize(e, { r: true });
|
|
454
|
+
resizeBR = (e) => this.startResize(e, { b: true, r: true });
|
|
455
|
+
resizeB = (e) => this.startResize(e, { b: true });
|
|
456
|
+
resizeBL = (e) => this.startResize(e, { b: true, l: true });
|
|
457
|
+
resizeL = (e) => this.startResize(e, { l: true });
|
|
458
|
+
// Internals
|
|
459
|
+
seedInitialGeometry() {
|
|
460
|
+
const sizePreset = this.size();
|
|
461
|
+
const preset = sizePreset ? SIZE_PRESETS[sizePreset] : SIZE_PRESETS.md;
|
|
462
|
+
const explicitW = this.initialWidth();
|
|
463
|
+
const explicitH = this.initialHeight();
|
|
464
|
+
const w = clamp(explicitW ?? preset.width, this.minWidth(), this.maxWidth());
|
|
465
|
+
const h = clamp(explicitH ?? preset.height, this.minHeight(), this.maxHeight());
|
|
466
|
+
this.width_.set(w);
|
|
467
|
+
this.height_.set(h);
|
|
468
|
+
const xIn = this.initialX();
|
|
469
|
+
const yIn = this.initialY();
|
|
470
|
+
if (xIn != null && yIn != null) {
|
|
471
|
+
this.x_.set(xIn);
|
|
472
|
+
this.y_.set(yIn);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
const cascade = this.manager.nextStartOffset();
|
|
476
|
+
this.x_.set(xIn ?? cascade.x);
|
|
477
|
+
this.y_.set(yIn ?? cascade.y);
|
|
478
|
+
}
|
|
479
|
+
this.clampToViewport();
|
|
480
|
+
}
|
|
481
|
+
clampToViewport() {
|
|
482
|
+
if (!this.keepInViewport())
|
|
483
|
+
return;
|
|
484
|
+
const vw = viewportWidth();
|
|
485
|
+
const vh = viewportHeight();
|
|
486
|
+
const w = this.width_();
|
|
487
|
+
this.x_.set(clamp(this.x_(), MIN_ON_VIEWPORT - w, vw - MIN_ON_VIEWPORT));
|
|
488
|
+
this.y_.set(clamp(this.y_(), 0, vh - MIN_ON_VIEWPORT));
|
|
489
|
+
}
|
|
490
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindow, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
491
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", 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: "<div class=\"wr-window__chrome\" (pointerdown)=\"startMove($event)\" (dblclick)=\"showMaximize() && 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 aria-label=\"Close\"\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 (showMinimize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"state() === 'minimized' ? 'Restore' : 'Minimize'\"\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 (showMaximize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"state() === 'maximized' ? 'Restore down' : 'Maximize'\"\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 -->\n @if (resolvedOs() !== 'linux' && showMinimize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"state() === 'minimized' ? 'Restore' : 'Minimize'\"\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 (resolvedOs() !== 'linux' && showMaximize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"state() === 'maximized' ? 'Restore down' : 'Maximize'\"\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 aria-label=\"Close\"\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 });
|
|
492
|
+
}
|
|
493
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindow, decorators: [{
|
|
494
|
+
type: Component,
|
|
495
|
+
args: [{ selector: 'wr-window', encapsulation: ViewEncapsulation.None, host: {
|
|
496
|
+
role: 'dialog',
|
|
497
|
+
'[class]': 'classes()',
|
|
498
|
+
'[style.left.px]': 'x()',
|
|
499
|
+
'[style.top.px]': 'y()',
|
|
500
|
+
'[style.width.px]': 'width()',
|
|
501
|
+
'[style.height.px]': 'height()',
|
|
502
|
+
'[style.z-index]': 'z()',
|
|
503
|
+
'[attr.aria-hidden]': '!open()',
|
|
504
|
+
'[attr.aria-labelledby]': 'titleId',
|
|
505
|
+
'(pointerdown)': 'focusWindow()',
|
|
506
|
+
}, template: "<div class=\"wr-window__chrome\" (pointerdown)=\"startMove($event)\" (dblclick)=\"showMaximize() && 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 aria-label=\"Close\"\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 (showMinimize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"state() === 'minimized' ? 'Restore' : 'Minimize'\"\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 (showMaximize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"state() === 'maximized' ? 'Restore down' : 'Maximize'\"\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 -->\n @if (resolvedOs() !== 'linux' && showMinimize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"state() === 'minimized' ? 'Restore' : 'Minimize'\"\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 (resolvedOs() !== 'linux' && showMaximize()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"state() === 'maximized' ? 'Restore down' : 'Maximize'\"\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 aria-label=\"Close\"\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" }]
|
|
507
|
+
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }, { type: i0.Output, args: ["stateChange"] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], initialX: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialX", required: false }] }], initialY: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialY", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], initialWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialWidth", required: false }] }], initialHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialHeight", required: false }] }], os: [{ type: i0.Input, args: [{ isSignal: true, alias: "os", required: false }] }], chromeSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "chromeSize", required: false }] }], animations: [{ type: i0.Input, args: [{ isSignal: true, alias: "animations", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], movable: [{ type: i0.Input, args: [{ isSignal: true, alias: "movable", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], keepInViewport: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepInViewport", required: false }] }], snap: [{ type: i0.Input, args: [{ isSignal: true, alias: "snap", required: false }] }], dragHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragHandle", required: false }] }], showMinimize: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMinimize", required: false }] }], showMaximize: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMaximize", required: false }] }], showClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClose", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], moved: [{ type: i0.Output, args: ["moved"] }], resized: [{ type: i0.Output, args: ["resized"] }] } });
|
|
508
|
+
|
|
509
|
+
/** Coalesce live position/size writes — drag/resize fires every move. */
|
|
510
|
+
const PERSIST_DEBOUNCE_MS = 200;
|
|
511
|
+
/**
|
|
512
|
+
* Internal host attached by `WrWindowManager.open()`. Reads the config,
|
|
513
|
+
* configures a `<wr-window>`, projects the consumer's component into
|
|
514
|
+
* its body, and wires the resulting instance back to the `WrWindowRef`
|
|
515
|
+
* so consumers can drive everything imperatively.
|
|
516
|
+
*
|
|
517
|
+
* @internal
|
|
518
|
+
*/
|
|
519
|
+
class WrWindowContainer {
|
|
520
|
+
/** Set by the manager before view init. */
|
|
521
|
+
config;
|
|
522
|
+
/** Set by the manager before view init. */
|
|
523
|
+
ref;
|
|
524
|
+
/** Set by the manager before view init. */
|
|
525
|
+
componentType;
|
|
526
|
+
/** Custom injector with `WR_WINDOW_REF` / `WR_WINDOW_DATA` populated. */
|
|
527
|
+
childInjector;
|
|
528
|
+
hostInjector = inject(Injector);
|
|
529
|
+
storage = inject(WrStorage);
|
|
530
|
+
destroyRef = inject(DestroyRef);
|
|
531
|
+
persistTimer = null;
|
|
532
|
+
contentVcr = viewChild.required('content', { read: ViewContainerRef });
|
|
533
|
+
wrWindow = viewChild.required(WrWindow);
|
|
534
|
+
constructor() {
|
|
535
|
+
afterNextRender(() => this.attach());
|
|
536
|
+
this.destroyRef.onDestroy(() => {
|
|
537
|
+
if (this.persistTimer !== null)
|
|
538
|
+
clearTimeout(this.persistTimer);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
attach() {
|
|
542
|
+
// 1. Create the consumer's component inside the body slot.
|
|
543
|
+
const componentRef = this.contentVcr().createComponent(this.componentType, {
|
|
544
|
+
injector: this.childInjector,
|
|
545
|
+
});
|
|
546
|
+
this.ref.componentRef = componentRef;
|
|
547
|
+
// 2. Bridge imperative actions to the underlying <wr-window>.
|
|
548
|
+
const w = this.wrWindow();
|
|
549
|
+
this.ref._doMinimize = () => w.minimize();
|
|
550
|
+
this.ref._doMaximize = () => w.maximize();
|
|
551
|
+
this.ref._doRestore = () => w.restore();
|
|
552
|
+
this.ref._doFocus = () => w.focus();
|
|
553
|
+
this.ref._doMoveTo = (x, y) => w.moveTo(x, y);
|
|
554
|
+
this.ref._doResizeTo = (width, height) => w.resizeTo(width, height);
|
|
555
|
+
this.ref._doCenter = () => w.center();
|
|
556
|
+
this.ref._doSetTitle = title => this.ref._title.set(title);
|
|
557
|
+
// 3. Sync the live geometry/state into the ref's signals so
|
|
558
|
+
// consumers can read them reactively without poking at <wr-window>.
|
|
559
|
+
// Angular 22 always allows signal writes inside effects — the
|
|
560
|
+
// `allowSignalWrites` option was deprecated in this version.
|
|
561
|
+
runInInjectionContext(this.hostInjector, () => {
|
|
562
|
+
effect(() => this.ref._state.set(w.state()));
|
|
563
|
+
effect(() => this.ref._x.set(w.x()));
|
|
564
|
+
effect(() => this.ref._y.set(w.y()));
|
|
565
|
+
effect(() => this.ref._width.set(w.width()));
|
|
566
|
+
effect(() => this.ref._height.set(w.height()));
|
|
567
|
+
effect(() => this.ref._z.set(w.z()));
|
|
568
|
+
});
|
|
569
|
+
// 4. Seed the title signal from config.
|
|
570
|
+
this.ref._title.set(this.config.title ?? '');
|
|
571
|
+
// 5. Storage — hydrate from disk + auto-save on geometry change.
|
|
572
|
+
if (this.config.storage) {
|
|
573
|
+
this.hydrateFromStorage(w);
|
|
574
|
+
this.installAutosave(w);
|
|
575
|
+
}
|
|
576
|
+
// 6. Apply any state the manager pinned for restore — bridges are
|
|
577
|
+
// now wired so `ref.minimize()` / `maximize()` actually fire.
|
|
578
|
+
const pending = this.ref.pendingStateOnMount;
|
|
579
|
+
if (pending) {
|
|
580
|
+
this.ref.pendingStateOnMount = null;
|
|
581
|
+
if (pending === 'minimized')
|
|
582
|
+
this.ref.minimize();
|
|
583
|
+
else if (pending === 'maximized')
|
|
584
|
+
this.ref.maximize();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
/** Apply persisted geometry on top of config defaults, before the user sees the window. */
|
|
588
|
+
hydrateFromStorage(w) {
|
|
589
|
+
const cfg = this.config.storage;
|
|
590
|
+
if (!cfg)
|
|
591
|
+
return;
|
|
592
|
+
const saved = this.storage.get(storageKey(cfg));
|
|
593
|
+
if (!saved)
|
|
594
|
+
return;
|
|
595
|
+
const persist = cfg.persist ?? 'all';
|
|
596
|
+
if (persist !== 'size') {
|
|
597
|
+
if (typeof saved.x === 'number')
|
|
598
|
+
w.x_.set(saved.x);
|
|
599
|
+
if (typeof saved.y === 'number')
|
|
600
|
+
w.y_.set(saved.y);
|
|
601
|
+
}
|
|
602
|
+
if (persist !== 'position') {
|
|
603
|
+
if (typeof saved.width === 'number')
|
|
604
|
+
w.width_.set(saved.width);
|
|
605
|
+
if (typeof saved.height === 'number')
|
|
606
|
+
w.height_.set(saved.height);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
/** Persist geometry whenever it changes, debounced. */
|
|
610
|
+
installAutosave(w) {
|
|
611
|
+
const cfg = this.config.storage;
|
|
612
|
+
if (!cfg)
|
|
613
|
+
return;
|
|
614
|
+
const persist = cfg.persist ?? 'all';
|
|
615
|
+
const key = storageKey(cfg);
|
|
616
|
+
runInInjectionContext(this.hostInjector, () => {
|
|
617
|
+
effect(() => {
|
|
618
|
+
// Read every signal we want to watch; ignore minimized/maximized
|
|
619
|
+
// overlay values — we only persist the user's chosen geometry.
|
|
620
|
+
const x = w.x_();
|
|
621
|
+
const y = w.y_();
|
|
622
|
+
const width = w.width_();
|
|
623
|
+
const height = w.height_();
|
|
624
|
+
if (this.persistTimer !== null)
|
|
625
|
+
clearTimeout(this.persistTimer);
|
|
626
|
+
this.persistTimer = setTimeout(() => {
|
|
627
|
+
this.persistTimer = null;
|
|
628
|
+
const payload = {};
|
|
629
|
+
if (persist !== 'size') {
|
|
630
|
+
payload.x = x;
|
|
631
|
+
payload.y = y;
|
|
632
|
+
}
|
|
633
|
+
if (persist !== 'position') {
|
|
634
|
+
payload.width = width;
|
|
635
|
+
payload.height = height;
|
|
636
|
+
}
|
|
637
|
+
this.storage.set(key, payload);
|
|
638
|
+
}, PERSIST_DEBOUNCE_MS);
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindowContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
643
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", 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 ?? true\"\n [showMaximize]=\"config.showMaximize ?? true\"\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 });
|
|
644
|
+
}
|
|
645
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindowContainer, decorators: [{
|
|
646
|
+
type: Component,
|
|
647
|
+
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 ?? true\"\n [showMaximize]=\"config.showMaximize ?? true\"\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" }]
|
|
648
|
+
}], ctorParameters: () => [], propDecorators: { contentVcr: [{ type: i0.ViewChild, args: ['content', { ...{ read: ViewContainerRef }, isSignal: true }] }], wrWindow: [{ type: i0.ViewChild, args: [i0.forwardRef(() => WrWindow), { isSignal: true }] }] } });
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* @license
|
|
652
|
+
*
|
|
653
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
654
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
655
|
+
*/
|
|
656
|
+
/**
|
|
657
|
+
* Handle returned by `WrWindowManager.open()`.
|
|
658
|
+
*
|
|
659
|
+
* Wraps the underlying overlay, exposes signals for the live geometry,
|
|
660
|
+
* and gives the consumer programmatic control over the window
|
|
661
|
+
* (focus, move, resize, minimize, maximize, close).
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```ts
|
|
665
|
+
* const ref = manager.open(EditorComponent, {
|
|
666
|
+
* title: 'Untitled.md',
|
|
667
|
+
* storage: { key: 'editor', prefix: 'app' },
|
|
668
|
+
* });
|
|
669
|
+
*
|
|
670
|
+
* ref.beforeClose(async () => {
|
|
671
|
+
* if (!isDirty) return true;
|
|
672
|
+
* return await confirmDiscard();
|
|
673
|
+
* });
|
|
674
|
+
*
|
|
675
|
+
* const saved = await ref.afterClosed(); // resolves with whatever the
|
|
676
|
+
* // component passed to ref.close(value)
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
679
|
+
class WrWindowRef {
|
|
680
|
+
_overlayRef;
|
|
681
|
+
/** Stable id — used by the taskbar, workspace save, and DI. */
|
|
682
|
+
id;
|
|
683
|
+
/** @internal — emits the close result once and completes. */
|
|
684
|
+
_closed = new Subject();
|
|
685
|
+
/** @internal — emits whenever the visual state changes. */
|
|
686
|
+
_stateChanged = new Subject();
|
|
687
|
+
/** @internal — set by the container right after attach. */
|
|
688
|
+
componentRef = null;
|
|
689
|
+
/** @internal — whether this window opts into the taskbar list. */
|
|
690
|
+
taskbarVisible = true;
|
|
691
|
+
/**
|
|
692
|
+
* @internal — non-`normal` state to apply right after the container
|
|
693
|
+
* wires its bridges (so `restoreLayout` can land a window straight
|
|
694
|
+
* into `minimized` / `maximized` even though it just opened).
|
|
695
|
+
*/
|
|
696
|
+
pendingStateOnMount = null;
|
|
697
|
+
// Live geometry signals (writable internally, exposed read-only)
|
|
698
|
+
/** @internal */ _state = signal('normal', /* @ts-ignore */
|
|
699
|
+
...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
|
|
700
|
+
/** @internal */ _x = signal(0, /* @ts-ignore */
|
|
701
|
+
...(ngDevMode ? [{ debugName: "_x" }] : /* istanbul ignore next */ []));
|
|
702
|
+
/** @internal */ _y = signal(0, /* @ts-ignore */
|
|
703
|
+
...(ngDevMode ? [{ debugName: "_y" }] : /* istanbul ignore next */ []));
|
|
704
|
+
/** @internal */ _width = signal(0, /* @ts-ignore */
|
|
705
|
+
...(ngDevMode ? [{ debugName: "_width" }] : /* istanbul ignore next */ []));
|
|
706
|
+
/** @internal */ _height = signal(0, /* @ts-ignore */
|
|
707
|
+
...(ngDevMode ? [{ debugName: "_height" }] : /* istanbul ignore next */ []));
|
|
708
|
+
/** @internal */ _z = signal(0, /* @ts-ignore */
|
|
709
|
+
...(ngDevMode ? [{ debugName: "_z" }] : /* istanbul ignore next */ []));
|
|
710
|
+
/** @internal */ _title = signal('', /* @ts-ignore */
|
|
711
|
+
...(ngDevMode ? [{ debugName: "_title" }] : /* istanbul ignore next */ []));
|
|
712
|
+
/** Window state. */
|
|
713
|
+
state = this._state.asReadonly();
|
|
714
|
+
/** Live X. */
|
|
715
|
+
x = this._x.asReadonly();
|
|
716
|
+
/** Live Y. */
|
|
717
|
+
y = this._y.asReadonly();
|
|
718
|
+
/** Live width. */
|
|
719
|
+
width = this._width.asReadonly();
|
|
720
|
+
/** Live height. */
|
|
721
|
+
height = this._height.asReadonly();
|
|
722
|
+
/** Stack z-index. */
|
|
723
|
+
z = this._z.asReadonly();
|
|
724
|
+
/** Live title. */
|
|
725
|
+
title = this._title.asReadonly();
|
|
726
|
+
// Hooks
|
|
727
|
+
_beforeCloseHook = null;
|
|
728
|
+
// Imperative bridges — wired by the manager / container after attach
|
|
729
|
+
/** @internal */ _doClose = null;
|
|
730
|
+
/** @internal */ _doMinimize = null;
|
|
731
|
+
/** @internal */ _doMaximize = null;
|
|
732
|
+
/** @internal */ _doRestore = null;
|
|
733
|
+
/** @internal */ _doFocus = null;
|
|
734
|
+
/** @internal */ _doMoveTo = null;
|
|
735
|
+
/** @internal */ _doResizeTo = null;
|
|
736
|
+
/** @internal */ _doCenter = null;
|
|
737
|
+
/** @internal */ _doSetTitle = null;
|
|
738
|
+
constructor(id, _overlayRef) {
|
|
739
|
+
this._overlayRef = _overlayRef;
|
|
740
|
+
this.id = id;
|
|
741
|
+
}
|
|
742
|
+
/** Resolved instance of the projected component. */
|
|
743
|
+
get componentInstance() {
|
|
744
|
+
if (!this.componentRef) {
|
|
745
|
+
throw new Error('WrWindowRef: component not yet attached');
|
|
746
|
+
}
|
|
747
|
+
return this.componentRef.instance;
|
|
748
|
+
}
|
|
749
|
+
// Lifecycle
|
|
750
|
+
/**
|
|
751
|
+
* Close the window, optionally returning a result. If a `beforeClose`
|
|
752
|
+
* hook is registered it is consulted first — a falsy resolution
|
|
753
|
+
* cancels the close.
|
|
754
|
+
*/
|
|
755
|
+
async close(result) {
|
|
756
|
+
if (this._beforeCloseHook) {
|
|
757
|
+
const ok = await this._beforeCloseHook(result);
|
|
758
|
+
if (!ok)
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
this._doClose?.(result);
|
|
762
|
+
}
|
|
763
|
+
/** Toggle the minimized state. */
|
|
764
|
+
minimize() {
|
|
765
|
+
this._doMinimize?.();
|
|
766
|
+
}
|
|
767
|
+
/** Toggle the maximized state. */
|
|
768
|
+
maximize() {
|
|
769
|
+
this._doMaximize?.();
|
|
770
|
+
}
|
|
771
|
+
/** Force-restore from minimized / maximized to `normal`. */
|
|
772
|
+
restore() {
|
|
773
|
+
this._doRestore?.();
|
|
774
|
+
}
|
|
775
|
+
/** Bring this window to the top of the stack. */
|
|
776
|
+
focus() {
|
|
777
|
+
this._doFocus?.();
|
|
778
|
+
}
|
|
779
|
+
// Programmatic geometry
|
|
780
|
+
moveTo(x, y) {
|
|
781
|
+
this._doMoveTo?.(x, y);
|
|
782
|
+
}
|
|
783
|
+
resizeTo(width, height) {
|
|
784
|
+
this._doResizeTo?.(width, height);
|
|
785
|
+
}
|
|
786
|
+
/** Re-position the window so it sits centered in the current viewport. */
|
|
787
|
+
center() {
|
|
788
|
+
this._doCenter?.();
|
|
789
|
+
}
|
|
790
|
+
setTitle(title) {
|
|
791
|
+
this._doSetTitle?.(title);
|
|
792
|
+
}
|
|
793
|
+
// Hooks
|
|
794
|
+
/**
|
|
795
|
+
* Register a guard that runs before `close()`. Return `false` (or a
|
|
796
|
+
* Promise resolving to false) to veto. Only one hook per ref —
|
|
797
|
+
* subsequent calls replace the previous.
|
|
798
|
+
*/
|
|
799
|
+
beforeClose(hook) {
|
|
800
|
+
this._beforeCloseHook = hook;
|
|
801
|
+
}
|
|
802
|
+
/** Resolves with the result passed to `close()` (or `undefined`). */
|
|
803
|
+
afterClosed() {
|
|
804
|
+
return new Promise(resolve => {
|
|
805
|
+
this._closed.subscribe({
|
|
806
|
+
next: value => resolve(value),
|
|
807
|
+
complete: () => resolve(undefined),
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
/** Escape hatch — the underlying CDK overlay ref. */
|
|
812
|
+
get overlayRef() {
|
|
813
|
+
return this._overlayRef;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Owns the open `<wr-window>` stack, hands out z-indexes, cascades the
|
|
819
|
+
* default spawn position, and opens programmatic windows that wrap a
|
|
820
|
+
* consumer component.
|
|
821
|
+
*
|
|
822
|
+
* Inject the singleton and call `open(component, config)` for a
|
|
823
|
+
* dialog-style flow:
|
|
824
|
+
*
|
|
825
|
+
* ```ts
|
|
826
|
+
* const manager = inject(WrWindowManager);
|
|
827
|
+
*
|
|
828
|
+
* const ref = manager.open(EditorComponent, {
|
|
829
|
+
* title: 'Untitled.md',
|
|
830
|
+
* size: 'lg',
|
|
831
|
+
* storage: { key: 'editor', prefix: 'my-app' },
|
|
832
|
+
* });
|
|
833
|
+
*
|
|
834
|
+
* const saved = await ref.afterClosed();
|
|
835
|
+
* ```
|
|
836
|
+
*
|
|
837
|
+
* Declarative `<wr-window [(open)]="...">` instances also register with
|
|
838
|
+
* the manager on focus, so both styles share the same stack ordering.
|
|
839
|
+
*/
|
|
840
|
+
class WrWindowManager {
|
|
841
|
+
overlay = inject(WR_OVERLAY);
|
|
842
|
+
parentInjector = inject(EnvironmentInjector);
|
|
843
|
+
storage = inject(WrStorage);
|
|
844
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
845
|
+
baseZ = 1000;
|
|
846
|
+
topZ = this.baseZ;
|
|
847
|
+
openCount = 0;
|
|
848
|
+
/** All open windows opened via `open()` — declarative `<wr-window>` is not tracked here. */
|
|
849
|
+
_windows = signal([], /* @ts-ignore */
|
|
850
|
+
...(ngDevMode ? [{ debugName: "_windows" }] : /* istanbul ignore next */ []));
|
|
851
|
+
/**
|
|
852
|
+
* Filled by `restoreLayout` right before it invokes the consumer's
|
|
853
|
+
* opener callback. The next `open()` call with a matching `config.id`
|
|
854
|
+
* pops the entry and uses it as the initial geometry (so the seed
|
|
855
|
+
* lands at the saved coords instead of the cascade default) and
|
|
856
|
+
* applies any non-`normal` state after the bridges are wired.
|
|
857
|
+
*/
|
|
858
|
+
pendingRestores = new Map();
|
|
859
|
+
/** All currently-open programmatic windows. */
|
|
860
|
+
windows = this._windows.asReadonly();
|
|
861
|
+
/** Programmatic windows that are minimized AND opted into the taskbar. */
|
|
862
|
+
minimized = computed(() => this._windows().filter(w => w.taskbarVisible && w.state() === 'minimized'), /* @ts-ignore */
|
|
863
|
+
...(ngDevMode ? [{ debugName: "minimized" }] : /* istanbul ignore next */ []));
|
|
864
|
+
/** Reserve the next z-index. Strictly increasing across the app's lifetime. */
|
|
865
|
+
bringToFront() {
|
|
866
|
+
this.topZ += 1;
|
|
867
|
+
return this.topZ;
|
|
868
|
+
}
|
|
869
|
+
/** Cascade offset for new windows, so two opens don't perfectly overlap. */
|
|
870
|
+
nextStartOffset() {
|
|
871
|
+
const offset = (this.openCount % 10) * 30;
|
|
872
|
+
this.openCount += 1;
|
|
873
|
+
return { x: 50 + offset, y: 50 + offset };
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Open a window with `component` rendered as its body. Returns a ref
|
|
877
|
+
* the caller can use to drive the window programmatically and await
|
|
878
|
+
* its result.
|
|
879
|
+
*
|
|
880
|
+
* **Singleton by id** — if `config.id` is set and a window with that
|
|
881
|
+
* id is already open, the existing ref is restored (if minimized),
|
|
882
|
+
* brought to the front, and returned. No duplicate window is
|
|
883
|
+
* created. Use this to coalesce repeated open() calls (e.g. a
|
|
884
|
+
* "Settings" menu item that should always re-focus the same window).
|
|
885
|
+
*/
|
|
886
|
+
open(component, config = {}) {
|
|
887
|
+
if (config.id) {
|
|
888
|
+
const existing = this.findById(config.id);
|
|
889
|
+
if (existing) {
|
|
890
|
+
existing.restore();
|
|
891
|
+
existing.focus();
|
|
892
|
+
return existing;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
// Honour a pending restore — `restoreLayout` populates this Map
|
|
896
|
+
// right before invoking the consumer's opener so the new window
|
|
897
|
+
// seeds at the saved geometry instead of the cascade default.
|
|
898
|
+
const pending = config.id ? this.pendingRestores.get(config.id) : undefined;
|
|
899
|
+
if (pending) {
|
|
900
|
+
this.pendingRestores.delete(config.id);
|
|
901
|
+
config = {
|
|
902
|
+
...config,
|
|
903
|
+
x: pending.x,
|
|
904
|
+
y: pending.y,
|
|
905
|
+
width: pending.width,
|
|
906
|
+
height: pending.height,
|
|
907
|
+
title: pending.title || config.title,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
// Each window gets its own CDK overlay pane positioned `static` —
|
|
911
|
+
// <wr-window> itself uses `position: fixed`, so the pane is only a
|
|
912
|
+
// mount point. Windows are never modal — reach for `WrDialog` when
|
|
913
|
+
// you need a backdrop + focus trap + scroll block.
|
|
914
|
+
const overlayRef = this.overlay.create({
|
|
915
|
+
positionStrategy: this.overlay.position().global(),
|
|
916
|
+
panelClass: 'wr-window-overlay',
|
|
917
|
+
hasBackdrop: false,
|
|
918
|
+
});
|
|
919
|
+
const id = config.id ?? randomId('wr-window');
|
|
920
|
+
const ref = new WrWindowRef(id, overlayRef);
|
|
921
|
+
ref.taskbarVisible = config.taskbar !== false;
|
|
922
|
+
// Provide WR_WINDOW_REF + WR_WINDOW_DATA inside the projected
|
|
923
|
+
// component's subtree so it can `inject(WR_WINDOW_REF)` etc.
|
|
924
|
+
const childInjector = Injector.create({
|
|
925
|
+
parent: this.parentInjector,
|
|
926
|
+
providers: [
|
|
927
|
+
{ provide: WR_WINDOW_REF, useValue: ref },
|
|
928
|
+
{ provide: WR_WINDOW_DATA, useValue: config.data ?? null },
|
|
929
|
+
],
|
|
930
|
+
});
|
|
931
|
+
const portal = new ComponentPortal(WrWindowContainer);
|
|
932
|
+
const containerRef = overlayRef.attach(portal);
|
|
933
|
+
const container = containerRef.instance;
|
|
934
|
+
container.config = config;
|
|
935
|
+
container.ref = ref;
|
|
936
|
+
container.componentType = component;
|
|
937
|
+
container.childInjector = childInjector;
|
|
938
|
+
if (this.isBrowser) {
|
|
939
|
+
overlayRef.overlayElement.setAttribute('role', 'dialog');
|
|
940
|
+
}
|
|
941
|
+
if (config.closeOnEscape !== false) {
|
|
942
|
+
overlayRef.keydownEvents().subscribe(event => {
|
|
943
|
+
if (event.key === 'Escape') {
|
|
944
|
+
event.preventDefault();
|
|
945
|
+
void ref.close();
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
// Wire close → disposal + completion of the result subject. The
|
|
950
|
+
// ref's `close()` already runs the beforeClose hook before reaching
|
|
951
|
+
// this bridge.
|
|
952
|
+
ref._doClose = result => {
|
|
953
|
+
overlayRef.dispose();
|
|
954
|
+
ref._closed.next(result);
|
|
955
|
+
ref._closed.complete();
|
|
956
|
+
this._windows.update(list => list.filter(r => r !== ref));
|
|
957
|
+
};
|
|
958
|
+
this._windows.update(list => [...list, ref]);
|
|
959
|
+
// If the pending restore had a non-`normal` state, hand it to the
|
|
960
|
+
// container so it can apply it right after wiring the bridges.
|
|
961
|
+
// queueMicrotask would fire before afterNextRender — the bridges
|
|
962
|
+
// wouldn't be set yet and ref.minimize() / maximize() would no-op.
|
|
963
|
+
if (pending && pending.state !== 'normal') {
|
|
964
|
+
ref.pendingStateOnMount = pending.state;
|
|
965
|
+
}
|
|
966
|
+
return ref;
|
|
967
|
+
}
|
|
968
|
+
/** Close every programmatically-opened window. */
|
|
969
|
+
closeAll() {
|
|
970
|
+
for (const ref of [...this._windows()]) {
|
|
971
|
+
void ref.close();
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
/** Look up an open window by its `config.id`. Returns `null` when no match. */
|
|
975
|
+
findById(id) {
|
|
976
|
+
return this._windows().find(r => r.id === id) ?? null;
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Drop the persisted geometry for a window so the next open uses the
|
|
980
|
+
* config defaults again. Matches `WrWindowConfig.storage` exactly.
|
|
981
|
+
*/
|
|
982
|
+
clearPersistedPosition(cfg) {
|
|
983
|
+
this.storage.remove(storageKey(cfg));
|
|
984
|
+
}
|
|
985
|
+
// Workspace save/restore
|
|
986
|
+
//
|
|
987
|
+
// A workspace is a snapshot of every open window's id + state +
|
|
988
|
+
// geometry. Reopening is the consumer's job (component identities
|
|
989
|
+
// aren't serialisable), so `restoreLayout` returns the captured
|
|
990
|
+
// snapshots and a helper that re-applies geometry once the windows
|
|
991
|
+
// have been opened again.
|
|
992
|
+
/** Snapshot of one window — what `saveLayout` writes / `restoreLayout` returns. */
|
|
993
|
+
layoutKey(name) {
|
|
994
|
+
return `wr:window-layout:${name}`;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Persist the geometry + state of every open programmatic window
|
|
998
|
+
* under `name`. Pair with `restoreLayout(name)` after re-opening the
|
|
999
|
+
* matching components.
|
|
1000
|
+
*/
|
|
1001
|
+
saveLayout(name) {
|
|
1002
|
+
const snapshot = this._windows().map(ref => ({
|
|
1003
|
+
id: ref.id,
|
|
1004
|
+
state: ref.state(),
|
|
1005
|
+
x: ref.x(),
|
|
1006
|
+
y: ref.y(),
|
|
1007
|
+
width: ref.width(),
|
|
1008
|
+
height: ref.height(),
|
|
1009
|
+
title: ref.title(),
|
|
1010
|
+
}));
|
|
1011
|
+
this.storage.set(this.layoutKey(name), snapshot);
|
|
1012
|
+
}
|
|
1013
|
+
/** Read a saved workspace. Returns `null` when no snapshot is found. */
|
|
1014
|
+
readLayout(name) {
|
|
1015
|
+
return this.storage.get(this.layoutKey(name));
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Apply a saved workspace.
|
|
1019
|
+
*
|
|
1020
|
+
* Each saved entry is matched to a currently-open window by `id`. For
|
|
1021
|
+
* matches, the geometry / state / title is re-applied immediately.
|
|
1022
|
+
* For misses, the optional `open` callback is invoked so the consumer
|
|
1023
|
+
* can re-open the right component for that id — the freshly-opened
|
|
1024
|
+
* window then seeds straight to the saved geometry (no cascade flicker)
|
|
1025
|
+
* and the saved state lands once the bridges are wired.
|
|
1026
|
+
*
|
|
1027
|
+
* ```ts
|
|
1028
|
+
* manager.restoreLayout('default', (id, snap) => {
|
|
1029
|
+
* if (id.startsWith('editor:')) {
|
|
1030
|
+
* manager.open(EditorComponent, { id, title: snap.title, data: ... });
|
|
1031
|
+
* }
|
|
1032
|
+
* });
|
|
1033
|
+
* ```
|
|
1034
|
+
*/
|
|
1035
|
+
restoreLayout(name, open) {
|
|
1036
|
+
const snapshot = this.readLayout(name);
|
|
1037
|
+
if (!snapshot)
|
|
1038
|
+
return;
|
|
1039
|
+
for (const snap of snapshot) {
|
|
1040
|
+
const existing = this.findById(snap.id);
|
|
1041
|
+
if (existing) {
|
|
1042
|
+
this.applyRestore(existing, snap);
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
if (!open)
|
|
1046
|
+
continue;
|
|
1047
|
+
// Stash the geometry so the next `open()` call with this id
|
|
1048
|
+
// consumes it as the seed instead of cascading.
|
|
1049
|
+
this.pendingRestores.set(snap.id, snap);
|
|
1050
|
+
open(snap.id, { title: snap.title });
|
|
1051
|
+
// Belt + braces: if the opener didn't actually open a window (or
|
|
1052
|
+
// opened with a different id), drop the stale pending entry so it
|
|
1053
|
+
// doesn't ambush a future open.
|
|
1054
|
+
if (!this.findById(snap.id))
|
|
1055
|
+
this.pendingRestores.delete(snap.id);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
applyRestore(ref, snap) {
|
|
1059
|
+
// Force back to a known 'normal' baseline before applying state —
|
|
1060
|
+
// ref.minimize() / maximize() are toggles, so this prevents a
|
|
1061
|
+
// saved 'minimized' from un-minimizing an already-minimized window.
|
|
1062
|
+
ref.restore();
|
|
1063
|
+
if (snap.title)
|
|
1064
|
+
ref.setTitle(snap.title);
|
|
1065
|
+
ref.moveTo(snap.x, snap.y);
|
|
1066
|
+
ref.resizeTo(snap.width, snap.height);
|
|
1067
|
+
if (snap.state === 'minimized')
|
|
1068
|
+
ref.minimize();
|
|
1069
|
+
else if (snap.state === 'maximized')
|
|
1070
|
+
ref.maximize();
|
|
1071
|
+
}
|
|
1072
|
+
/** Drop a saved workspace. */
|
|
1073
|
+
clearLayout(name) {
|
|
1074
|
+
this.storage.remove(this.layoutKey(name));
|
|
1075
|
+
}
|
|
1076
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindowManager, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
1077
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.0.1", ngImport: i0, type: WrWindowManager });
|
|
1078
|
+
}
|
|
1079
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindowManager, decorators: [{
|
|
1080
|
+
type: Service
|
|
1081
|
+
}] });
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* @license
|
|
1085
|
+
*
|
|
1086
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1087
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
1088
|
+
*/
|
|
1089
|
+
/**
|
|
1090
|
+
* OS-style window taskbar. Drop one anywhere in the app — typically
|
|
1091
|
+
* fixed to the bottom of the viewport — and it lists every window
|
|
1092
|
+
* `WrWindowManager.open()` has handed out that's currently
|
|
1093
|
+
* `minimized`. Clicking a tab restores the window; clicking the close
|
|
1094
|
+
* glyph dismisses it without restoring.
|
|
1095
|
+
*
|
|
1096
|
+
* Windows opted out via `config.taskbar = false` never show up here.
|
|
1097
|
+
*
|
|
1098
|
+
* @example
|
|
1099
|
+
* ```html
|
|
1100
|
+
* <wr-window-taskbar />
|
|
1101
|
+
* <wr-window-taskbar position="top" />
|
|
1102
|
+
* ```
|
|
1103
|
+
*
|
|
1104
|
+
* @see https://ngwr.dev/components/window
|
|
1105
|
+
*/
|
|
1106
|
+
class WrWindowTaskbar {
|
|
1107
|
+
/** Edge the taskbar pins itself to. @default 'bottom' */
|
|
1108
|
+
position = input('bottom', /* @ts-ignore */
|
|
1109
|
+
...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
1110
|
+
manager = inject(WrWindowManager);
|
|
1111
|
+
/** @internal — minimized windows that haven't opted out via `taskbar: false`. */
|
|
1112
|
+
items = computed(() => this.manager.minimized(), /* @ts-ignore */
|
|
1113
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
1114
|
+
classes = computed(() => `wr-window-taskbar wr-window-taskbar--${this.position()}`, /* @ts-ignore */
|
|
1115
|
+
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
1116
|
+
restore(ref) {
|
|
1117
|
+
ref.restore();
|
|
1118
|
+
ref.focus();
|
|
1119
|
+
}
|
|
1120
|
+
close(ref, event) {
|
|
1121
|
+
event.stopPropagation();
|
|
1122
|
+
void ref.close();
|
|
1123
|
+
}
|
|
1124
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindowTaskbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1125
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: WrWindowTaskbar, isStandalone: true, selector: "wr-window-taskbar", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar", "aria-label": "Minimized windows" }, properties: { "class": "classes()" } }, ngImport: i0, template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab\"\n [attr.aria-label]=\"`Restore ${ref.title() || 'Untitled'}`\"\n (click)=\"restore(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ ref.title() || 'Untitled' }}</span>\n <span\n class=\"wr-window-taskbar__tab-close\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Close window\"\n (click)=\"close(ref, $event)\"\n (keydown.enter)=\"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 </span>\n </button>\n }\n </div>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1126
|
+
}
|
|
1127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: WrWindowTaskbar, decorators: [{
|
|
1128
|
+
type: Component,
|
|
1129
|
+
args: [{ selector: 'wr-window-taskbar', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', role: 'toolbar', 'aria-label': 'Minimized windows' }, template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab\"\n [attr.aria-label]=\"`Restore ${ref.title() || 'Untitled'}`\"\n (click)=\"restore(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ ref.title() || 'Untitled' }}</span>\n <span\n class=\"wr-window-taskbar__tab-close\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Close window\"\n (click)=\"close(ref, $event)\"\n (keydown.enter)=\"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 </span>\n </button>\n }\n </div>\n}\n" }]
|
|
1130
|
+
}], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }] } });
|
|
1131
|
+
|
|
1132
|
+
// `WrWindow` itself is NOT exported. Windows are opened through
|
|
1133
|
+
// `WrWindowManager.open(Component, config)` — one canonical path keeps
|
|
1134
|
+
// the API surface small and predictable.
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Generated bundle index. Do not edit.
|
|
1138
|
+
*/
|
|
1139
|
+
|
|
1140
|
+
export { WR_WINDOW_DATA, WR_WINDOW_REF, WrWindowManager, WrWindowRef, WrWindowTaskbar };
|
|
1141
|
+
//# sourceMappingURL=ngwr-window.mjs.map
|