ngwr 13.0.1 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -13
- package/aurora/aurora.scss +1 -1
- package/aurora/styles/_dark.scss +21 -0
- package/aurora/styles/_index.scss +11 -30
- package/aurora/styles/_rules.scss +25 -0
- package/border-glow/styles/_dark.scss +46 -0
- package/border-glow/styles/_index.scss +5 -0
- package/border-glow/styles/_rules.scss +0 -31
- package/button/styles/_index.scss +16 -0
- package/color-picker/styles/_index.scss +43 -5
- package/event-calendar/styles/_index.scss +15 -0
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs +3 -3
- package/fesm2022/ngwr-alert.mjs +7 -7
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs +4 -4
- package/fesm2022/ngwr-aurora.mjs.map +1 -1
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs +14 -4
- package/fesm2022/ngwr-back-top.mjs.map +1 -1
- package/fesm2022/ngwr-badge.mjs +6 -6
- package/fesm2022/ngwr-bar-chart.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs +16 -5
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow.mjs +4 -4
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +7 -7
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-button.mjs +88 -10
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +3 -3
- package/fesm2022/ngwr-calendar-testing.mjs +15 -0
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +53 -7
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +9 -9
- package/fesm2022/ngwr-carousel.mjs +6 -6
- package/fesm2022/ngwr-cascader.mjs +4 -4
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +6 -6
- package/fesm2022/ngwr-circular-text.mjs +23 -4
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +3 -3
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-collapse.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker-testing.mjs +23 -7
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +93 -14
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +29 -5
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs +3 -3
- package/fesm2022/ngwr-context-menu.mjs +18 -12
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-counter.mjs +6 -6
- package/fesm2022/ngwr-date-adapters-fns.mjs +104 -36
- package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
- package/fesm2022/ngwr-date-picker-testing.mjs +10 -2
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +118 -28
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-date.mjs +483 -82
- package/fesm2022/ngwr-date.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text.mjs +3 -3
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-dialog.mjs +15 -15
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-divider.mjs +3 -3
- package/fesm2022/ngwr-donut-chart.mjs +3 -3
- package/fesm2022/ngwr-drag-drop.mjs +37 -9
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +18 -18
- package/fesm2022/ngwr-dropdown.mjs +29 -12
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +6 -6
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text.mjs +3 -3
- package/fesm2022/ngwr-file-upload.mjs +55 -15
- package/fesm2022/ngwr-file-upload.mjs.map +1 -1
- package/fesm2022/ngwr-form-testing.mjs +13 -7
- package/fesm2022/ngwr-form-testing.mjs.map +1 -1
- package/fesm2022/ngwr-form.mjs +44 -25
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
- package/fesm2022/ngwr-gauge.mjs +3 -3
- package/fesm2022/ngwr-glitch-text.mjs +3 -3
- package/fesm2022/ngwr-gradient-text.mjs +3 -3
- package/fesm2022/ngwr-hotkey.mjs +6 -6
- package/fesm2022/ngwr-i18n-en.mjs +63 -1
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +28 -1
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +159 -31
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-image-cropper.mjs +3 -3
- package/fesm2022/ngwr-input-number.mjs +5 -5
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +23 -5
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +37 -26
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +22 -4
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-lightbox.mjs +4 -4
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +3 -3
- package/fesm2022/ngwr-list.mjs +7 -7
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar-router.mjs +73 -0
- package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -0
- package/fesm2022/ngwr-loading-bar.mjs +27 -38
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown.mjs +6 -6
- package/fesm2022/ngwr-marquee.mjs +3 -3
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-mention.mjs +109 -19
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meter-group.mjs +3 -3
- package/fesm2022/ngwr-overlay.mjs +6 -6
- package/fesm2022/ngwr-page-header.mjs +3 -3
- package/fesm2022/ngwr-pagination-testing.mjs +11 -10
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +67 -32
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +21 -21
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-popconfirm.mjs +7 -6
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +7 -6
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs +3 -3
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-rating.mjs +48 -22
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-result.mjs +12 -12
- package/fesm2022/ngwr-rotating-text.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-segmented.mjs +12 -8
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +217 -59
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +7 -7
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +3 -3
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs +12 -8
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +3 -3
- package/fesm2022/ngwr-speed-dial.mjs +3 -3
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-split-text.mjs +3 -3
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-spotlight-card.mjs +7 -7
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +7 -6
- package/fesm2022/ngwr-squircle.mjs.map +1 -1
- package/fesm2022/ngwr-star-border.mjs +4 -4
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +44 -13
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-stepper.mjs +6 -6
- package/fesm2022/ngwr-storage.mjs +11 -3
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-table.mjs +51 -28
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-router.mjs +158 -0
- package/fesm2022/ngwr-tabs-router.mjs.map +1 -0
- package/fesm2022/ngwr-tabs-testing.mjs +13 -11
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +104 -16
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +46 -6
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +90 -4
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card.mjs +7 -7
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-toast.mjs +116 -11
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +3 -3
- package/fesm2022/ngwr-tour.mjs +6 -6
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +4 -4
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +35 -12
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +3 -3
- package/fesm2022/ngwr-typography.mjs +3 -3
- package/fesm2022/ngwr-utils.mjs +37 -1
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
- package/fesm2022/ngwr-waves.mjs +4 -4
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window-testing.mjs +10 -2
- package/fesm2022/ngwr-window-testing.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +15 -15
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/i18n/en.json +33 -6
- package/i18n/ru.json +33 -6
- package/llms-full.txt +29 -20
- package/llms.txt +127 -25
- package/mcp/catalog.js +3 -3
- package/mcp/providers.js +11 -0
- package/package.json +9 -1
- package/schematics/migrations/v13/index.js +101 -0
- package/schematics/migrations/v14/index.js +422 -0
- package/schematics/migrations.json +10 -0
- package/schematics/page/templates.js +1 -1
- package/schematics/use/symbol-map.json +1 -0
- package/shiny-text/styles/_dark.scss +20 -0
- package/shiny-text/styles/_index.scss +5 -0
- package/shiny-text/styles/_rules.scss +0 -4
- package/skills/ngwr/SKILL.md +4 -1
- package/skills/ngwr/references/catalog.md +2 -0
- package/skills/ngwr/references/setup.md +4 -0
- package/spotlight-card/styles/_dark.scss +21 -0
- package/spotlight-card/styles/_index.scss +5 -0
- package/spotlight-card/styles/_rules.scss +0 -6
- package/star-border/styles/_dark.scss +21 -0
- package/star-border/styles/_index.scss +5 -0
- package/star-border/styles/_rules.scss +0 -5
- package/stepper/styles/_index.scss +19 -1
- package/textarea/styles/_index.scss +14 -6
- package/theme/styles/_colors.scss +102 -0
- package/theme/styles/_focus.scss +31 -1
- package/tilt-card/styles/_dark.scss +21 -0
- package/tilt-card/styles/_index.scss +5 -0
- package/tilt-card/styles/_rules.scss +0 -5
- package/toast/styles/_index.scss +12 -4
- package/types/ngwr-alert.d.ts +3 -3
- package/types/ngwr-blur-text.d.ts +8 -0
- package/types/ngwr-button.d.ts +45 -2
- package/types/ngwr-calendar-testing.d.ts +21 -4
- package/types/ngwr-calendar.d.ts +37 -0
- package/types/ngwr-circular-text.d.ts +9 -0
- package/types/ngwr-collapse.d.ts +19 -15
- package/types/ngwr-color-picker-testing.d.ts +10 -4
- package/types/ngwr-color-picker.d.ts +70 -2
- package/types/ngwr-command-palette.d.ts +14 -0
- package/types/ngwr-context-menu.d.ts +1 -1
- package/types/ngwr-date-adapters-fns.d.ts +71 -29
- package/types/ngwr-date-picker-testing.d.ts +206 -185
- package/types/ngwr-date-picker.d.ts +7 -0
- package/types/ngwr-date.d.ts +143 -13
- package/types/ngwr-drag-drop.d.ts +19 -0
- package/types/ngwr-dropdown.d.ts +18 -2
- package/types/ngwr-event-calendar-testing.d.ts +12 -9
- package/types/ngwr-file-upload.d.ts +31 -3
- package/types/ngwr-form-testing.d.ts +13 -7
- package/types/ngwr-form.d.ts +47 -12
- package/types/ngwr-i18n.d.ts +89 -7
- package/types/ngwr-input-number.d.ts +1 -1
- package/types/ngwr-input-otp.d.ts +3 -0
- package/types/ngwr-input.d.ts +13 -5
- package/types/ngwr-knob-testing.d.ts +8 -4
- package/types/ngwr-knob.d.ts +18 -0
- package/types/ngwr-loading-bar-router.d.ts +35 -0
- package/types/ngwr-loading-bar.d.ts +19 -9
- package/types/ngwr-mention.d.ts +13 -0
- package/types/ngwr-pagination-testing.d.ts +11 -10
- package/types/ngwr-pagination.d.ts +39 -10
- package/types/ngwr-popconfirm.d.ts +1 -1
- package/types/ngwr-popover.d.ts +1 -1
- package/types/ngwr-rating.d.ts +25 -4
- package/types/ngwr-segmented.d.ts +1 -0
- package/types/ngwr-select.d.ts +110 -19
- package/types/ngwr-slider.d.ts +1 -0
- package/types/ngwr-splitter-testing.d.ts +9 -4
- package/types/ngwr-squircle.d.ts +1 -1
- package/types/ngwr-statistic.d.ts +22 -2
- package/types/ngwr-storage.d.ts +39 -1
- package/types/ngwr-table.d.ts +24 -5
- package/types/ngwr-tabs-router.d.ts +67 -0
- package/types/ngwr-tabs-testing.d.ts +12 -10
- package/types/ngwr-tabs.d.ts +103 -6
- package/types/ngwr-textarea.d.ts +29 -0
- package/types/ngwr-theme.d.ts +92 -2
- package/types/ngwr-toast.d.ts +53 -0
- package/types/ngwr-tree.d.ts +10 -2
- package/types/ngwr-utils.d.ts +33 -1
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-window-testing.d.ts +11 -3
- package/types/ngwr-window.d.ts +11 -4
- package/waves/styles/_dark.scss +19 -0
- package/waves/styles/_index.scss +5 -0
- package/waves/styles/_rules.scss +0 -3
- package/window/styles/_index.scss +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-segmented.mjs","sources":["../../../projects/lib/segmented/segmented.ts","../../../projects/lib/segmented/segmented.html","../../../projects/lib/segmented/ngwr-segmented.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n ElementRef,\n ViewEncapsulation,\n afterNextRender,\n computed,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { WR_FORM_FIELD } from 'ngwr/form';\nimport { WrIcon } from 'ngwr/icon';\n\nimport type { WrSegmentedOption } from './interfaces';\n\n/**\n * Single-choice picker rendered as a segmented control. Two-way binds\n * the selected `value`.\n *\n * A signal-forms native control: it implements `FormValueControl<T | null>`, so\n * `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone, and classic\n * `[(ngModel)]` / reactive forms keep working through Angular's bridge.\n *\n * It stays a `role=\"group\"` of `aria-pressed` toggle buttons rather than\n * becoming a `radiogroup`: being a form control changes what the value is bound\n * to, not what the widget is, and a radiogroup would owe a roving tab stop and\n * arrow-key selection — a different keyboard contract for every consumer who\n * already ships this. The group is what the field describes (`aria-describedby`\n * / `aria-invalid` land on the host), while the `<label for>` has to name a\n * labelable element, so the id goes on the first segment.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-segmented [options]=\"ranges\" [formField]=\"form.range\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-segmented\n * [options]=\"[\n * { value: 'day', label: 'Day' },\n * { value: 'week', label: 'Week' },\n * { value: 'month', label: 'Month' },\n * ]\"\n * [(value)]=\"range\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/segmented\n */\nexport type WrSegmentedSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'wr-segmented',\n templateUrl: './segmented.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n '[style]': 'thumbStyle()',\n role: 'group',\n '[attr.aria-describedby]': 'describedBy()',\n '[attr.aria-invalid]': 'ariaInvalid()',\n '(focusout)': 'onFocusOut($event)',\n },\n imports: [WrIcon],\n})\nexport class WrSegmented<T = unknown> implements FormValueControl<T | null> {\n /** The segments to render. */\n readonly options = input.required<readonly WrSegmentedOption<T>[]>();\n\n // The model keeps the component's own `T`, and the contract is satisfied as\n // `FormValueControl<T | null>` — legal because `FormField<T>` infers its type from\n // the FIELD it is handed and never from the host component, so the interface's\n // parameter describes this model rather than constraining the binding.\n // `wr-cascader` and `wr-tree` widen theirs to `unknown` because their value SHAPE\n // changes with a mode input (`TId | null` in single mode, a `readonly TId[]` in\n // multi) and no one type is true of both. A segmented control has one shape, so\n // widening here would only throw away what `[options]` already proved: every\n // `(valueChange)=\"pick($event)\"` in a typed app would start receiving `unknown`.\n // With the default type parameter this degenerates to exactly the catalog's\n // `FormValueControl<unknown>`.\n /**\n * The picked segment's `value`, `null` when nothing is selected. Bound by\n * `[formField]`, or two-way via `[(value)]`.\n */\n readonly value = model<T | null>(null);\n\n /** Emitted when focus leaves the strip, so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n /**\n * Disable the whole control. Bound automatically from the field's disabled\n * state when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Refuse changes while every segment stays focusable and the value still\n * submits. Bound automatically from the field's readonly state when used with\n * `[formField]`.\n *\n * NOTHING is mirrored into ARIA here, deliberately: the strip is a\n * `role=\"group\"` of `aria-pressed` buttons, and ARIA defines `aria-readonly`\n * for neither role — it is a state of `checkbox` / `radiogroup` / `textbox`\n * and their kin, and it is not global. `aria-disabled` is the only attribute\n * that would apply to a button, and it says the wrong thing: these segments\n * are still focusable and the value still submits. So the state is honest in\n * the DOM (`wr-segmented--readonly`) and silent in the accessibility tree\n * rather than announced with a word that means something else.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Control size — shares the `--wr-control-*` contract. @default 'md' */\n readonly size = input<WrSegmentedSize>('md');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly field = inject(WR_FORM_FIELD, { optional: true });\n\n /**\n * Id the surrounding `<wr-form-field>`'s `<label for>` points at, stamped on the\n * FIRST segment.\n *\n * The field renders its label before it can see what was projected into it, so the\n * id travels the other way and the control adopts it, exactly as `[wrInput]`,\n * `wr-slider` and `wr-select` do. Without this the `for` named an element that was\n * nowhere in the document: clicking the label did nothing.\n *\n * It cannot go on the host — only a labelable element can be a label's target, and\n * `<wr-segmented>` is not one — and there is no single control to put it on, so it\n * goes where clicking the label should land a user: the first segment, which is\n * also the strip's first tab stop. The same choice `wr-slider` makes with its low\n * thumb.\n *\n * What this does NOT change is that segment's NAME: it is named by its own label,\n * and a `<label for>` cannot outrank text content in the accname order anyway.\n *\n * The one shape this reads wrong is a strip NESTED inside another control that is\n * itself the field — `<wr-color-picker>`'s own format tabs are the only case in\n * the library — where the id lands on a tab instead of on the picker. That is the\n * same exposure `[wrInput]`, `wr-slider` and `wr-select` all carry, and the fix\n * belongs to the outer control (shield its subtree by providing `WR_FORM_FIELD`\n * as `null`), not to a strip that cannot tell whose field it is in.\n */\n protected readonly controlId = computed(() => this.field?.controlId() ?? null);\n\n /**\n * The field's error message, wired to the GROUP rather than to a segment.\n *\n * A segmented control is one field made of several buttons, so the description\n * belongs to the thing the field describes — repeated on every segment it would be\n * announced once per Tab. `role=\"group\"` supports neither state as a role-specific\n * attribute, but both are ARIA globals, so this is valid where an `aria-checked`\n * would not be.\n *\n * `aria-invalid` is keyed on the message EXISTING rather than on `errorKeys()`,\n * because the field only publishes an id once it is showing something — announcing\n * \"invalid\" while pointing at nothing is worse than staying quiet.\n */\n protected readonly describedBy = computed(() => this.field?.describedBy() ?? null);\n protected readonly ariaInvalid = computed(() => (this.field?.describedBy() ? 'true' : null));\n\n /** Index of the selected option, or `-1` when nothing is selected. */\n protected readonly selectedIndex = computed(() => {\n const v = this.value();\n return this.options().findIndex(o => o.value === v);\n });\n\n /** Flips true after the first paint so the thumb only animates user-driven changes, not the initial snap. */\n private readonly mounted = signal(false);\n\n constructor() {\n afterNextRender(() => this.mounted.set(true));\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-segmented'];\n const size = this.size();\n if (size !== 'md') parts.push(`wr-segmented--${size}`);\n if (this.disabled()) parts.push('wr-segmented--disabled');\n else if (this.readonly()) parts.push('wr-segmented--readonly');\n if (this.selectedIndex() < 0) parts.push('wr-segmented--unselected');\n if (this.mounted()) parts.push('wr-segmented--mounted');\n return parts.join(' ');\n });\n\n private readonly dir = inject(Directionality, { optional: true });\n\n /**\n * Reading direction of the strip. `Directionality` is root-provided, so this\n * always resolves — `optional` only guards a consumer who has deliberately\n * torn the provider out. A subtree that overrides the direction does it with\n * the CDK's `Dir` directive, which writes `valueSignal`, so `thumbStyle`\n * re-renders instead of leaving the thumb parked on the old segment.\n */\n private readonly isRtl = computed(() => this.dir?.valueSignal() === 'rtl');\n\n /**\n * Inline CSS vars driving the sliding thumb position.\n *\n * `--wr-segmented-thumb-index` is the SLOT the thumb parks in, counted from the\n * physical left, which is the selected index only in LTR. The stylesheet anchors\n * the thumb with a physical `left` and slides it with `translateX` — neither has\n * a logical form — while the options are a grid that mirrors, so under `dir=\"rtl\"`\n * the segment at logical index `i` occupies slot `count - 1 - i`. Left unsigned,\n * the pill sat under a different label than the one it marks (the whole strip's\n * worth of offset at the far end), which is the carousel's `trackStyle` problem\n * one component over.\n */\n protected readonly thumbStyle = computed<Record<string, string>>(() => {\n const i = Math.max(0, this.selectedIndex());\n const count = Math.max(1, this.options().length);\n const slot = this.isRtl() ? count - 1 - i : i;\n return {\n '--wr-segmented-thumb-index': `${slot}`,\n '--wr-segmented-thumb-count': `${count}`,\n };\n });\n\n protected isSelected(option: WrSegmentedOption<T>): boolean {\n return this.value() === option.value;\n }\n\n protected select(option: WrSegmentedOption<T>): void {\n if (this.disabled() || this.readonly() || option.disabled || this.isSelected(option)) return;\n this.value.set(option.value);\n }\n\n protected trackByValue(_: number, option: WrSegmentedOption<T>): unknown {\n return option.value;\n }\n\n /**\n * A bound field learns it may show its validation copy from `touch`, and a click\n * cannot be the source: a strip tabbed into and out of without a pick left the\n * field untouched forever — which is exactly the case `touched` exists for.\n *\n * `wr-knob` and `wr-rating` bind `(blur)` on the one element they own; this\n * component owns one button per option, so it listens for the bubbling `focusout`\n * on its host instead — and ignores the one that only moves focus from one segment\n * to the next, the way `wr-slider` ignores the hop between its two thumbs.\n */\n protected onFocusOut(event: FocusEvent): void {\n if (this.host.nativeElement.contains(event.relatedTarget as Node | null)) return;\n this.touch.emit();\n }\n}\n","<span class=\"wr-segmented__thumb\" aria-hidden=\"true\"></span>\n\n<!--\n `id` on the FIRST segment only: it is the id a surrounding `<wr-form-field>`\n already points its `<label for>` at, and a `for` may only name a labelable\n element — see `controlId` in the component. `null` without a field, so a bare\n strip stamps no document-global id on anything.\n-->\n@for (option of options(); track option.value) {\n <button\n type=\"button\"\n class=\"wr-segmented__option\"\n [attr.id]=\"$first ? controlId() : null\"\n [class.wr-segmented__option--selected]=\"isSelected(option)\"\n [disabled]=\"disabled() || !!option.disabled\"\n [attr.aria-pressed]=\"isSelected(option)\"\n [attr.aria-label]=\"option.label ? null : (option.ariaLabel ?? option.value)\"\n (click)=\"select(option)\"\n >\n @if (option.icon) {\n <wr-icon class=\"wr-segmented__icon\" [name]=\"option.icon\" />\n }\n @if (option.label) {\n <span class=\"wr-segmented__label\">{{ option.label }}</span>\n }\n </button>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-segmented.mjs","sources":["../../../projects/lib/segmented/segmented.ts","../../../projects/lib/segmented/segmented.html","../../../projects/lib/segmented/ngwr-segmented.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n ElementRef,\n ViewEncapsulation,\n afterNextRender,\n computed,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { WR_FORM_FIELD, useFormFieldAria } from 'ngwr/form';\nimport { WrIcon } from 'ngwr/icon';\n\nimport type { WrSegmentedOption } from './interfaces';\n\n/**\n * Single-choice picker rendered as a segmented control. Two-way binds\n * the selected `value`.\n *\n * A signal-forms native control: it implements `FormValueControl<T | null>`, so\n * `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone, and classic\n * `[(ngModel)]` / reactive forms keep working through Angular's bridge.\n *\n * It stays a `role=\"group\"` of `aria-pressed` toggle buttons rather than\n * becoming a `radiogroup`: being a form control changes what the value is bound\n * to, not what the widget is, and a radiogroup would owe a roving tab stop and\n * arrow-key selection — a different keyboard contract for every consumer who\n * already ships this. The group is what the field describes (`aria-describedby`\n * / `aria-invalid` land on the host), while the `<label for>` has to name a\n * labelable element, so the id goes on the first segment.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-segmented [options]=\"ranges\" [formField]=\"form.range\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-segmented\n * [options]=\"[\n * { value: 'day', label: 'Day' },\n * { value: 'week', label: 'Week' },\n * { value: 'month', label: 'Month' },\n * ]\"\n * [(value)]=\"range\"\n * />\n * ```\n *\n * @see https://ngwr.dev/reference/components/segmented\n */\nexport type WrSegmentedSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'wr-segmented',\n templateUrl: './segmented.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n '[style]': 'thumbStyle()',\n role: 'group',\n '[attr.aria-describedby]': 'describedBy()',\n '[attr.aria-invalid]': 'ariaInvalid()',\n '(focusout)': 'onFocusOut($event)',\n },\n imports: [WrIcon],\n})\nexport class WrSegmented<T = unknown> implements FormValueControl<T | null> {\n /** The segments to render. */\n readonly options = input.required<readonly WrSegmentedOption<T>[]>();\n\n // The model keeps the component's own `T`, and the contract is satisfied as\n // `FormValueControl<T | null>` — legal because `FormField<T>` infers its type from\n // the FIELD it is handed and never from the host component, so the interface's\n // parameter describes this model rather than constraining the binding.\n // `wr-cascader` and `wr-tree` widen theirs to `unknown` because their value SHAPE\n // changes with a mode input (`TId | null` in single mode, a `readonly TId[]` in\n // multi) and no one type is true of both. A segmented control has one shape, so\n // widening here would only throw away what `[options]` already proved: every\n // `(valueChange)=\"pick($event)\"` in a typed app would start receiving `unknown`.\n // With the default type parameter this degenerates to exactly the catalog's\n // `FormValueControl<unknown>`.\n /**\n * The picked segment's `value`, `null` when nothing is selected. Bound by\n * `[formField]`, or two-way via `[(value)]`.\n */\n readonly value = model<T | null>(null);\n\n /** Emitted when focus leaves the strip, so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n /**\n * Disable the whole control. Bound automatically from the field's disabled\n * state when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Refuse changes while every segment stays focusable and the value still\n * submits. Bound automatically from the field's readonly state when used with\n * `[formField]`.\n *\n * NOTHING is mirrored into ARIA here, deliberately: the strip is a\n * `role=\"group\"` of `aria-pressed` buttons, and ARIA defines `aria-readonly`\n * for neither role — it is a state of `checkbox` / `radiogroup` / `textbox`\n * and their kin, and it is not global. `aria-disabled` is the only attribute\n * that would apply to a button, and it says the wrong thing: these segments\n * are still focusable and the value still submits. So the state is honest in\n * the DOM (`wr-segmented--readonly`) and silent in the accessibility tree\n * rather than announced with a word that means something else.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Control size — shares the `--wr-control-*` contract. @default 'md' */\n readonly size = input<WrSegmentedSize>('md');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly field = inject(WR_FORM_FIELD, { optional: true });\n\n /**\n * Id the surrounding `<wr-form-field>`'s `<label for>` points at, stamped on the\n * FIRST segment.\n *\n * The field renders its label before it can see what was projected into it, so the\n * id travels the other way and the control adopts it, exactly as `[wrInput]`,\n * `wr-slider` and `wr-select` do. Without this the `for` named an element that was\n * nowhere in the document: clicking the label did nothing.\n *\n * It cannot go on the host — only a labelable element can be a label's target, and\n * `<wr-segmented>` is not one — and there is no single control to put it on, so it\n * goes where clicking the label should land a user: the first segment, which is\n * also the strip's first tab stop. The same choice `wr-slider` makes with its low\n * thumb.\n *\n * What this does NOT change is that segment's NAME: it is named by its own label,\n * and a `<label for>` cannot outrank text content in the accname order anyway.\n *\n * The one shape this reads wrong is a strip NESTED inside another control that is\n * itself the field — `<wr-color-picker>`'s own format tabs are the only case in\n * the library — where the id lands on a tab instead of on the picker. That is the\n * same exposure `[wrInput]`, `wr-slider` and `wr-select` all carry, and the fix\n * belongs to the outer control (shield its subtree by providing `WR_FORM_FIELD`\n * as `null`), not to a strip that cannot tell whose field it is in.\n */\n protected readonly controlId = computed(() => this.field?.controlId() ?? null);\n\n /**\n * The field's error message, wired to the GROUP rather than to a segment.\n *\n * A segmented control is one field made of several buttons, so the description\n * belongs to the thing the field describes — repeated on every segment it would be\n * announced once per Tab. `role=\"group\"` supports neither state as a role-specific\n * attribute, but both are ARIA globals, so this is valid where an `aria-checked`\n * would not be.\n *\n * `aria-invalid` is keyed on the message EXISTING rather than on `errorKeys()`,\n * because the field only publishes an id once it is showing something — announcing\n * \"invalid\" while pointing at nothing is worse than staying quiet.\n */\n // Through the shared helper, not a local pair: the hint is described by\n // `hintId`, which only `useFormFieldAria()` composes, and `aria-invalid` keys\n // on the error COUNT rather than on \"something is described\". Hand-rolled here,\n // these three announced the error and never the hint — and `<input wrInput>` is\n // the control the report was filed against.\n private readonly fieldAria = useFormFieldAria();\n protected readonly describedBy = this.fieldAria.describedBy;\n protected readonly ariaInvalid = this.fieldAria.ariaInvalid;\n\n /** Index of the selected option, or `-1` when nothing is selected. */\n protected readonly selectedIndex = computed(() => {\n const v = this.value();\n return this.options().findIndex(o => o.value === v);\n });\n\n /** Flips true after the first paint so the thumb only animates user-driven changes, not the initial snap. */\n private readonly mounted = signal(false);\n\n constructor() {\n afterNextRender(() => this.mounted.set(true));\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-segmented'];\n const size = this.size();\n if (size !== 'md') parts.push(`wr-segmented--${size}`);\n if (this.disabled()) parts.push('wr-segmented--disabled');\n else if (this.readonly()) parts.push('wr-segmented--readonly');\n if (this.selectedIndex() < 0) parts.push('wr-segmented--unselected');\n if (this.mounted()) parts.push('wr-segmented--mounted');\n return parts.join(' ');\n });\n\n private readonly dir = inject(Directionality, { optional: true });\n\n /**\n * Reading direction of the strip. `Directionality` is root-provided, so this\n * always resolves — `optional` only guards a consumer who has deliberately\n * torn the provider out. A subtree that overrides the direction does it with\n * the CDK's `Dir` directive, which writes `valueSignal`, so `thumbStyle`\n * re-renders instead of leaving the thumb parked on the old segment.\n */\n private readonly isRtl = computed(() => this.dir?.valueSignal() === 'rtl');\n\n /**\n * Inline CSS vars driving the sliding thumb position.\n *\n * `--wr-segmented-thumb-index` is the SLOT the thumb parks in, counted from the\n * physical left, which is the selected index only in LTR. The stylesheet anchors\n * the thumb with a physical `left` and slides it with `translateX` — neither has\n * a logical form — while the options are a grid that mirrors, so under `dir=\"rtl\"`\n * the segment at logical index `i` occupies slot `count - 1 - i`. Left unsigned,\n * the pill sat under a different label than the one it marks (the whole strip's\n * worth of offset at the far end), which is the carousel's `trackStyle` problem\n * one component over.\n */\n protected readonly thumbStyle = computed<Record<string, string>>(() => {\n const i = Math.max(0, this.selectedIndex());\n const count = Math.max(1, this.options().length);\n const slot = this.isRtl() ? count - 1 - i : i;\n return {\n '--wr-segmented-thumb-index': `${slot}`,\n '--wr-segmented-thumb-count': `${count}`,\n };\n });\n\n protected isSelected(option: WrSegmentedOption<T>): boolean {\n return this.value() === option.value;\n }\n\n protected select(option: WrSegmentedOption<T>): void {\n if (this.disabled() || this.readonly() || option.disabled || this.isSelected(option)) return;\n this.value.set(option.value);\n }\n\n protected trackByValue(_: number, option: WrSegmentedOption<T>): unknown {\n return option.value;\n }\n\n /**\n * A bound field learns it may show its validation copy from `touch`, and a click\n * cannot be the source: a strip tabbed into and out of without a pick left the\n * field untouched forever — which is exactly the case `touched` exists for.\n *\n * `wr-knob` and `wr-rating` bind `(blur)` on the one element they own; this\n * component owns one button per option, so it listens for the bubbling `focusout`\n * on its host instead — and ignores the one that only moves focus from one segment\n * to the next, the way `wr-slider` ignores the hop between its two thumbs.\n */\n protected onFocusOut(event: FocusEvent): void {\n if (this.host.nativeElement.contains(event.relatedTarget as Node | null)) return;\n this.touch.emit();\n }\n}\n","<span class=\"wr-segmented__thumb\" aria-hidden=\"true\"></span>\n\n<!--\n `id` on the FIRST segment only: it is the id a surrounding `<wr-form-field>`\n already points its `<label for>` at, and a `for` may only name a labelable\n element — see `controlId` in the component. `null` without a field, so a bare\n strip stamps no document-global id on anything.\n-->\n@for (option of options(); track option.value) {\n <button\n type=\"button\"\n class=\"wr-segmented__option\"\n [attr.id]=\"$first ? controlId() : null\"\n [class.wr-segmented__option--selected]=\"isSelected(option)\"\n [disabled]=\"disabled() || !!option.disabled\"\n [attr.aria-pressed]=\"isSelected(option)\"\n [attr.aria-label]=\"option.label ? null : (option.ariaLabel ?? option.value)\"\n (click)=\"select(option)\"\n >\n @if (option.icon) {\n <wr-icon class=\"wr-segmented__icon\" [name]=\"option.icon\" />\n }\n @if (option.label) {\n <span class=\"wr-segmented__label\">{{ option.label }}</span>\n }\n </button>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|