@spartan-ng/brain 0.0.1-alpha.719 → 1.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/README.md +3 -3
- package/fesm2022/spartan-ng-brain-accordion.mjs +7 -58
- package/fesm2022/spartan-ng-brain-accordion.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-alert-dialog.mjs +1 -2
- package/fesm2022/spartan-ng-brain-alert-dialog.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-autocomplete.mjs +3 -1
- package/fesm2022/spartan-ng-brain-autocomplete.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-collapsible.mjs +8 -56
- package/fesm2022/spartan-ng-brain-collapsible.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-combobox.mjs +22 -6
- package/fesm2022/spartan-ng-brain-combobox.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-core.mjs +89 -16
- package/fesm2022/spartan-ng-brain-core.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-dialog.mjs +8 -7
- package/fesm2022/spartan-ng-brain-dialog.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-drawer.mjs +8 -8
- package/fesm2022/spartan-ng-brain-drawer.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-field.mjs +7 -2
- package/fesm2022/spartan-ng-brain-field.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-navigation-menu.mjs +124 -34
- package/fesm2022/spartan-ng-brain-navigation-menu.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-overlay.mjs +27 -14
- package/fesm2022/spartan-ng-brain-overlay.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-select.mjs +3 -1
- package/fesm2022/spartan-ng-brain-select.mjs.map +1 -1
- package/fesm2022/spartan-ng-brain-tooltip.mjs +97 -14
- package/fesm2022/spartan-ng-brain-tooltip.mjs.map +1 -1
- package/package.json +6 -2
- package/types/spartan-ng-brain-accordion.d.ts +4 -22
- package/types/spartan-ng-brain-autocomplete.d.ts +1 -0
- package/types/spartan-ng-brain-collapsible.d.ts +5 -22
- package/types/spartan-ng-brain-combobox.d.ts +11 -2
- package/types/spartan-ng-brain-core.d.ts +40 -9
- package/types/spartan-ng-brain-dialog.d.ts +1 -3
- package/types/spartan-ng-brain-drawer.d.ts +3 -3
- package/types/spartan-ng-brain-navigation-menu.d.ts +9 -2
- package/types/spartan-ng-brain-overlay.d.ts +4 -5
- package/types/spartan-ng-brain-select.d.ts +1 -0
- package/types/spartan-ng-brain-tooltip.d.ts +38 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spartan-ng-brain-field.mjs","sources":["../../../../libs/brain/field/src/lib/brn-field-aria.service.ts","../../../../libs/brain/field/src/lib/brn-field.ts","../../../../libs/brain/field/src/lib/brn-labelable.ts","../../../../libs/brain/field/src/lib/brn-field-control.ts","../../../../libs/brain/field/src/lib/brn-field-control-described-by.ts","../../../../libs/brain/field/src/index.ts","../../../../libs/brain/field/src/spartan-ng-brain-field.ts"],"sourcesContent":["import { computed, Injectable, signal } from '@angular/core';\n\n@Injectable()\nexport class BrnFieldA11yService {\n\tprivate readonly _descriptions = signal<string[]>([]);\n\tprivate readonly _errors = signal<string[]>([]);\n\n\tpublic readonly describedBy = computed(() => {\n\t\tconst ids = [...this._descriptions(), ...this._errors()].filter(Boolean);\n\t\tconst uniqueIds = [...new Set(ids)];\n\t\treturn uniqueIds.length ? uniqueIds.join(' ') : null;\n\t});\n\n\tpublic registerDescription(id: string) {\n\t\tthis._descriptions.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterDescription(id: string) {\n\t\tthis._descriptions.update((ids) => ids.filter((value) => value !== id));\n\t}\n\n\tpublic registerError(id: string) {\n\t\tthis._errors.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterError(id: string) {\n\t\tthis._errors.update((ids) => ids.filter((value) => value !== id));\n\t}\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { booleanAttribute, computed, Directive, input, signal } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\nimport { BrnFieldControl } from './brn-field-control';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive({\n\tselector: '[brnField],brn-field',\n\tproviders: [BrnFieldA11yService],\n\thost: {\n\t\t'[attr.data-invalid]': '_invalid() ? \"true\" : null',\n\t\t'[attr.data-matches-spartan-invalid]': '_spartanInvalid() ? \"true\" : null',\n\t\t'[attr.data-touched]': '_touched() ? \"true\" : null',\n\t\t'[attr.data-dirty]': '_dirty() ? \"true\" : null',\n\t},\n})\nexport class BrnField {\n\tprivate readonly _brnFieldControl = signal<BrnFieldControl | null>(null);\n\tprivate readonly _labelable = signal<BrnLabelable | null>(null);\n\n\t/** Whether the field is invalid. Overrides the `data-invalid` attribute. */\n\tpublic readonly dataInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t\talias: 'data-invalid',\n\t});\n\n\t/**\n\t * Whether to force the field into an invalid state, regardless of the form control's state.\n\t * Overrides both the `data-invalid` and `data-matches-spartan-invalid` attributes.\n\t */\n\tpublic readonly forceInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t});\n\n\tprotected readonly _invalid = computed(() => {\n\t\tif (this.forceInvalid() || this.dataInvalid()) return true;\n\n\t\tconst control = this._brnFieldControl();\n\t\tif (!control || !control.ngControl) return false;\n\n\t\treturn control.controlState()?.invalid;\n\t});\n\n\tprotected readonly _spartanInvalid = computed(() => {\n\t\treturn this.forceInvalid() || (this._brnFieldControl()?.controlState()?.spartanInvalid ?? null);\n\t});\n\n\tprotected readonly _dirty = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.dirty ?? null;\n\t});\n\n\tprotected readonly _touched = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.touched ?? null;\n\t});\n\n\tpublic readonly labelableId = computed(() => this._brnFieldControl()?.id?.() ?? this._labelable()?.labelableId());\n\n\tpublic readonly errors = computed(() => this._brnFieldControl()?.errors() ?? null);\n\tpublic readonly controlState = computed(() => this._brnFieldControl()?.controlState() ?? null);\n\n\tpublic registerFieldControl(fieldControl: BrnFieldControl) {\n\t\tthis._brnFieldControl.set(fieldControl);\n\t}\n\n\tpublic registerLabelable(labelable: BrnLabelable) {\n\t\tthis._labelable.set(labelable);\n\t}\n}\n","import { type ExistingProvider, inject, InjectionToken, type Signal, type Type } from '@angular/core';\n\nexport interface BrnLabelable {\n\tlabelableId: Signal<string | null | undefined>;\n}\n\nexport const BrnLabelable = new InjectionToken<BrnLabelable>('BrnLabelable');\n\nexport function provideBrnLabelable(labelable: Type<BrnLabelable>): ExistingProvider {\n\treturn { provide: BrnLabelable, useExisting: labelable };\n}\n\nexport function injectBrnLabelable(): BrnLabelable | null {\n\treturn inject(BrnLabelable, { optional: true });\n}\n","import { computed, DestroyRef, Directive, DoCheck, effect, inject, Injector, OnInit, signal } from '@angular/core';\nimport { type AbstractControl, FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport { createStateTracker, ErrorStateMatcher, type StateTracker } from '@spartan-ng/brain/forms';\nimport { BrnField } from './brn-field';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive()\nexport class BrnFieldControl implements OnInit, DoCheck {\n\tprivate readonly _injector = inject(Injector);\n\tprivate readonly _errorStateMatcher = inject(ErrorStateMatcher);\n\tprivate readonly _parentForm = inject(NgForm, { optional: true });\n\tprivate readonly _parentFormGroup = inject(FormGroupDirective, { optional: true });\n\tprivate readonly _field = inject(BrnField, { optional: true });\n\tprivate readonly _destroyRef = inject(DestroyRef);\n\n\tprivate _idEffectRef?: ReturnType<typeof effect>;\n\tprivate readonly _stateTracker = signal<StateTracker | null>(null);\n\t/** Sentinel value to differentiate \"never checked\" from \"control is null\". */\n\tprivate _lastControl: AbstractControl | null = null;\n\n\t/** Gets the AbstractControlDirective for this control. */\n\tpublic ngControl: NgControl | null = null;\n\n\tpublic readonly id = signal<string | null | undefined>(undefined);\n\n\tpublic readonly controlState = computed(() => this._stateTracker()?.controlState() ?? null);\n\tpublic readonly errors = computed(() => this._stateTracker()?.errors() ?? null);\n\tpublic readonly dirty = computed(() => this._stateTracker()?.dirty() ?? null);\n\tpublic readonly invalid = computed(() => this._stateTracker()?.invalid() ?? null);\n\tpublic readonly spartanInvalid = computed(() => this._stateTracker()?.spartanInvalid() ?? null);\n\tpublic readonly touched = computed(() => this._stateTracker()?.touched() ?? null);\n\n\tconstructor() {\n\t\tthis._field?.registerFieldControl(this);\n\t\tthis._destroyRef.onDestroy(() => {\n\t\t\tthis._idEffectRef?.destroy();\n\t\t\tthis._stateTracker()?.destroy();\n\t\t});\n\t}\n\n\tngOnInit(): void {\n\t\tthis.ngControl = this._injector.get(NgControl, null);\n\n\t\t// Try to sync the tracker eagerly.\n\t\tthis._syncTracker();\n\n\t\t// For template-driven forms and FormControlName, the control is often resolved\n\t\t// asynchronously by Angular's directives after our ngOnInit/ngDoCheck.\n\t\t// Schedule a one-time microtask to catch the initial resolution.\n\t\tif (this.ngControl) {\n\t\t\tPromise.resolve().then(() => {\n\t\t\t\tthis._syncTracker();\n\t\t\t});\n\t\t}\n\n\t\tconst labelable = this._injector.get(BrnLabelable, null);\n\t\tif (labelable) {\n\t\t\tthis._idEffectRef = effect(\n\t\t\t\t() => {\n\t\t\t\t\tthis.id.set(labelable.labelableId());\n\t\t\t\t},\n\t\t\t\t{ injector: this._injector },\n\t\t\t);\n\t\t}\n\t}\n\n\t// Re-evaluate the control reference on every change detection cycle because\n\t// the underlying AbstractControl may change when [formControl] rebinds to a new instance.\n\t// When the instance changes we tear down the old tracker and create a fresh one.\n\tngDoCheck(): void {\n\t\tthis._syncTracker();\n\t}\n\n\t/** @returns true if the control reference changed */\n\tprivate _syncTracker(): void {\n\t\tif (!this.ngControl) return;\n\t\tconst currentControl = this.ngControl.control ?? null;\n\t\tif (currentControl === this._lastControl) return;\n\t\tthis._lastControl = currentControl;\n\t\tthis._stateTracker()?.destroy();\n\t\tthis._stateTracker.set(\n\t\t\tcurrentControl\n\t\t\t\t? createStateTracker(this.ngControl, this._errorStateMatcher, this._parentFormGroup, this._parentForm)\n\t\t\t\t: null,\n\t\t);\n\t}\n}\n","import { computed, Directive, inject, input } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\n\n@Directive({\n\tselector: '[brnFieldControlDescribedBy]',\n\thost: {\n\t\t'[attr.aria-describedby]': '_computedDescribedBy()',\n\t},\n})\nexport class BrnFieldControlDescribedBy {\n\tpublic readonly describedBy = input<string | null>(null, { alias: 'aria-describedby' });\n\tprivate readonly _a11y = inject(BrnFieldA11yService, { optional: true });\n\n\tprotected readonly _computedDescribedBy = computed(() => {\n\t\tconst manual = this.describedBy();\n\t\tconst manualList = manual ? manual.split(/\\s+/).filter(Boolean) : [];\n\t\tconst fieldIds = this._a11y?.describedBy() ?? null;\n\t\tconst fieldList = fieldIds ? fieldIds.split(/\\s+/).filter(Boolean) : [];\n\n\t\tconst combined = [...new Set([...manualList, ...fieldList])];\n\t\treturn combined.length ? combined.join(' ') : null;\n\t});\n}\n","import { BrnField } from './lib/brn-field';\nimport { BrnFieldControl } from './lib/brn-field-control';\nimport { BrnFieldControlDescribedBy } from './lib/brn-field-control-described-by';\n\nexport * from './lib/brn-field';\nexport * from './lib/brn-field-aria.service';\nexport * from './lib/brn-field-control';\nexport * from './lib/brn-field-control-described-by';\nexport * from './lib/brn-labelable';\n\nexport const BrnFieldImports = [BrnField, BrnFieldControl, BrnFieldControlDescribedBy] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"spartan-ng-brain-field.mjs","sources":["../../../../libs/brain/field/src/lib/brn-field-aria.service.ts","../../../../libs/brain/field/src/lib/brn-field.ts","../../../../libs/brain/field/src/lib/brn-labelable.ts","../../../../libs/brain/field/src/lib/brn-field-control.ts","../../../../libs/brain/field/src/lib/brn-field-control-described-by.ts","../../../../libs/brain/field/src/index.ts","../../../../libs/brain/field/src/spartan-ng-brain-field.ts"],"sourcesContent":["import { computed, Injectable, signal } from '@angular/core';\n\n@Injectable()\nexport class BrnFieldA11yService {\n\tprivate readonly _descriptions = signal<string[]>([]);\n\tprivate readonly _errors = signal<string[]>([]);\n\n\tpublic readonly describedBy = computed(() => {\n\t\tconst ids = [...this._descriptions(), ...this._errors()].filter(Boolean);\n\t\tconst uniqueIds = [...new Set(ids)];\n\t\treturn uniqueIds.length ? uniqueIds.join(' ') : null;\n\t});\n\n\tpublic registerDescription(id: string) {\n\t\tthis._descriptions.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterDescription(id: string) {\n\t\tthis._descriptions.update((ids) => ids.filter((value) => value !== id));\n\t}\n\n\tpublic registerError(id: string) {\n\t\tthis._errors.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterError(id: string) {\n\t\tthis._errors.update((ids) => ids.filter((value) => value !== id));\n\t}\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { booleanAttribute, computed, Directive, input, signal } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\nimport { BrnFieldControl } from './brn-field-control';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive({\n\tselector: '[brnField],brn-field',\n\tproviders: [BrnFieldA11yService],\n\thost: {\n\t\t'[attr.data-invalid]': '_invalid() ? \"true\" : null',\n\t\t'[attr.data-matches-spartan-invalid]': '_spartanInvalid() ? \"true\" : null',\n\t\t'[attr.data-touched]': '_touched() ? \"true\" : null',\n\t\t'[attr.data-dirty]': '_dirty() ? \"true\" : null',\n\t},\n})\nexport class BrnField {\n\tprivate readonly _brnFieldControl = signal<BrnFieldControl | null>(null);\n\tprivate readonly _labelable = signal<BrnLabelable | null>(null);\n\n\t/** Whether the field is invalid. Overrides the `data-invalid` attribute. */\n\tpublic readonly dataInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t\talias: 'data-invalid',\n\t});\n\n\t/**\n\t * Whether to force the field into an invalid state, regardless of the form control's state.\n\t * Overrides both the `data-invalid` and `data-matches-spartan-invalid` attributes.\n\t */\n\tpublic readonly forceInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t});\n\n\tprotected readonly _invalid = computed(() => {\n\t\tif (this.forceInvalid() || this.dataInvalid()) return true;\n\n\t\tconst control = this._brnFieldControl();\n\t\tif (!control || !control.ngControl) return false;\n\n\t\treturn control.controlState()?.invalid;\n\t});\n\n\tprotected readonly _spartanInvalid = computed(() => {\n\t\treturn this.forceInvalid() || (this._brnFieldControl()?.controlState()?.spartanInvalid ?? null);\n\t});\n\n\tprotected readonly _dirty = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.dirty ?? null;\n\t});\n\n\tprotected readonly _touched = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.touched ?? null;\n\t});\n\n\tpublic readonly labelableId = computed(() => this._brnFieldControl()?.id?.() ?? this._labelable()?.labelableId());\n\n\tpublic readonly errors = computed(() => this._brnFieldControl()?.errors() ?? null);\n\tpublic readonly controlState = computed(() => this._brnFieldControl()?.controlState() ?? null);\n\n\tpublic registerFieldControl(fieldControl: BrnFieldControl) {\n\t\tthis._brnFieldControl.set(fieldControl);\n\t}\n\n\tpublic registerLabelable(labelable: BrnLabelable) {\n\t\tthis._labelable.set(labelable);\n\t}\n}\n","import { type ExistingProvider, inject, InjectionToken, type Signal, type Type } from '@angular/core';\n\nexport interface BrnLabelable {\n\tlabelableId: Signal<string | null | undefined>;\n}\n\nexport const BrnLabelable = new InjectionToken<BrnLabelable>('BrnLabelable');\n\nexport function provideBrnLabelable(labelable: Type<BrnLabelable>): ExistingProvider {\n\treturn { provide: BrnLabelable, useExisting: labelable };\n}\n\nexport function injectBrnLabelable(): BrnLabelable | null {\n\treturn inject(BrnLabelable, { optional: true });\n}\n","import { computed, DestroyRef, Directive, DoCheck, effect, inject, Injector, OnInit, signal } from '@angular/core';\nimport { type AbstractControl, FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport { createStateTracker, ErrorStateMatcher, type StateTracker } from '@spartan-ng/brain/forms';\nimport { BrnField } from './brn-field';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive()\nexport class BrnFieldControl implements OnInit, DoCheck {\n\tprivate readonly _injector = inject(Injector);\n\tprivate readonly _errorStateMatcher = inject(ErrorStateMatcher);\n\tprivate readonly _parentForm = inject(NgForm, { optional: true });\n\tprivate readonly _parentFormGroup = inject(FormGroupDirective, { optional: true });\n\tprivate readonly _field = inject(BrnField, { optional: true });\n\tprivate readonly _destroyRef = inject(DestroyRef);\n\n\tprivate _idEffectRef?: ReturnType<typeof effect>;\n\tprivate readonly _stateTracker = signal<StateTracker | null>(null);\n\t/** Sentinel value to differentiate \"never checked\" from \"control is null\". */\n\tprivate _lastControl: AbstractControl | null = null;\n\n\t/** Gets the AbstractControlDirective for this control. */\n\tpublic ngControl: NgControl | null = null;\n\n\tpublic readonly id = signal<string | null | undefined>(undefined);\n\n\tpublic readonly controlState = computed(() => this._stateTracker()?.controlState() ?? null);\n\tpublic readonly errors = computed(() => this._stateTracker()?.errors() ?? null);\n\tpublic readonly dirty = computed(() => this._stateTracker()?.dirty() ?? null);\n\tpublic readonly invalid = computed(() => this._stateTracker()?.invalid() ?? null);\n\tpublic readonly spartanInvalid = computed(() => this._stateTracker()?.spartanInvalid() ?? null);\n\tpublic readonly touched = computed(() => this._stateTracker()?.touched() ?? null);\n\n\tconstructor() {\n\t\tthis._destroyRef.onDestroy(() => {\n\t\t\tthis._idEffectRef?.destroy();\n\t\t\tthis._stateTracker()?.destroy();\n\t\t});\n\t}\n\n\tngOnInit(): void {\n\t\t// `self` prevents descendants (e.g. calendar selects) from inheriting an ancestor's NgControl.\n\t\tthis.ngControl = this._injector.get(NgControl, null, { optional: true, self: true });\n\n\t\t// Only register with BrnField when this control has its own NgControl, otherwise descendant\n\t\t// field controls rendered in portals (e.g. calendar selects) overwrite the real control's registration.\n\t\tif (this.ngControl) {\n\t\t\tthis._field?.registerFieldControl(this);\n\t\t}\n\n\t\t// Try to sync the tracker eagerly.\n\t\tthis._syncTracker();\n\n\t\t// For template-driven forms and FormControlName, the control is often resolved\n\t\t// asynchronously by Angular's directives after our ngOnInit/ngDoCheck.\n\t\t// Schedule a one-time microtask to catch the initial resolution.\n\t\tif (this.ngControl) {\n\t\t\tPromise.resolve().then(() => {\n\t\t\t\tthis._syncTracker();\n\t\t\t});\n\t\t}\n\n\t\tconst labelable = this._injector.get(BrnLabelable, null);\n\t\tif (labelable) {\n\t\t\tthis._idEffectRef = effect(\n\t\t\t\t() => {\n\t\t\t\t\tthis.id.set(labelable.labelableId());\n\t\t\t\t},\n\t\t\t\t{ injector: this._injector },\n\t\t\t);\n\t\t}\n\t}\n\n\t// Re-evaluate the control reference on every change detection cycle because\n\t// the underlying AbstractControl may change when [formControl] rebinds to a new instance.\n\t// When the instance changes we tear down the old tracker and create a fresh one.\n\tngDoCheck(): void {\n\t\tthis._syncTracker();\n\t}\n\n\t/** @returns true if the control reference changed */\n\tprivate _syncTracker(): void {\n\t\tif (!this.ngControl) return;\n\t\tconst currentControl = this.ngControl.control ?? null;\n\t\tif (currentControl === this._lastControl) return;\n\t\tthis._lastControl = currentControl;\n\t\tthis._stateTracker()?.destroy();\n\t\tthis._stateTracker.set(\n\t\t\tcurrentControl\n\t\t\t\t? createStateTracker(this.ngControl, this._errorStateMatcher, this._parentFormGroup, this._parentForm)\n\t\t\t\t: null,\n\t\t);\n\t}\n}\n","import { computed, Directive, inject, input } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\n\n@Directive({\n\tselector: '[brnFieldControlDescribedBy]',\n\thost: {\n\t\t'[attr.aria-describedby]': '_computedDescribedBy()',\n\t},\n})\nexport class BrnFieldControlDescribedBy {\n\tpublic readonly describedBy = input<string | null>(null, { alias: 'aria-describedby' });\n\tprivate readonly _a11y = inject(BrnFieldA11yService, { optional: true });\n\n\tprotected readonly _computedDescribedBy = computed(() => {\n\t\tconst manual = this.describedBy();\n\t\tconst manualList = manual ? manual.split(/\\s+/).filter(Boolean) : [];\n\t\tconst fieldIds = this._a11y?.describedBy() ?? null;\n\t\tconst fieldList = fieldIds ? fieldIds.split(/\\s+/).filter(Boolean) : [];\n\n\t\tconst combined = [...new Set([...manualList, ...fieldList])];\n\t\treturn combined.length ? combined.join(' ') : null;\n\t});\n}\n","import { BrnField } from './lib/brn-field';\nimport { BrnFieldControl } from './lib/brn-field-control';\nimport { BrnFieldControlDescribedBy } from './lib/brn-field-control-described-by';\n\nexport * from './lib/brn-field';\nexport * from './lib/brn-field-aria.service';\nexport * from './lib/brn-field-control';\nexport * from './lib/brn-field-control-described-by';\nexport * from './lib/brn-labelable';\n\nexport const BrnFieldImports = [BrnField, BrnFieldControl, BrnFieldControlDescribedBy] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -3,9 +3,9 @@ import { Directionality } from '@angular/cdk/bidi';
|
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { inject, NgZone, Renderer2, signal, Injectable, InjectionToken, ElementRef, input, computed, contentChild, Directive, booleanAttribute, model, contentChildren, effect, TemplateRef, untracked, PLATFORM_ID, ViewContainerRef } from '@angular/core';
|
|
5
5
|
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
|
|
6
|
-
import { createHoverObservable, waitForElementAnimations, createMenuPosition, computedPrevious, isElement } from '@spartan-ng/brain/core';
|
|
6
|
+
import { createHoverObservable, waitForElementAnimations, createMenuPosition, injectSkipDelay, computedPrevious, isElement } from '@spartan-ng/brain/core';
|
|
7
7
|
import { Subject, BehaviorSubject, of, fromEvent, merge, combineLatest } from 'rxjs';
|
|
8
|
-
import { switchMap, share,
|
|
8
|
+
import { switchMap, share, map, takeUntil, pairwise, filter, startWith, debounceTime, delay, tap, distinctUntilChanged } from 'rxjs/operators';
|
|
9
9
|
import * as i1 from '@spartan-ng/brain/button';
|
|
10
10
|
import { BrnButton } from '@spartan-ng/brain/button';
|
|
11
11
|
import { hasModifierKey } from '@angular/cdk/keycodes';
|
|
@@ -51,19 +51,12 @@ class BrnNavigationMenuContentService {
|
|
|
51
51
|
_contentEl = signal(undefined, ...(ngDevMode ? [{ debugName: "_contentEl" }] : /* istanbul ignore next */ []));
|
|
52
52
|
contentEl = this._contentEl.asReadonly();
|
|
53
53
|
_overlayHoveredObservables$ = new BehaviorSubject(undefined);
|
|
54
|
-
|
|
54
|
+
_overlayTabObservables$ = new BehaviorSubject(undefined);
|
|
55
55
|
_overlayEscapeObservables$ = new BehaviorSubject(undefined);
|
|
56
56
|
hovered$ = this._overlayHoveredObservables$.pipe(switchMap((overlayHoveredObservable) => (overlayHoveredObservable !== undefined ? overlayHoveredObservable : of())), share());
|
|
57
|
-
|
|
57
|
+
/** Emits `Tab`/`Shift+Tab` keydowns bubbling up the content panel so the trigger can manage focus. */
|
|
58
|
+
tabPressed$ = this._overlayTabObservables$.pipe(switchMap((tab$) => (tab$ !== undefined ? tab$ : of())));
|
|
58
59
|
escapePressed$ = this._overlayEscapeObservables$.pipe(switchMap((contentFocused$) => (contentFocused$ !== undefined ? contentFocused$ : of())));
|
|
59
|
-
constructor() {
|
|
60
|
-
this._shiftTabPressed$.pipe(takeUntil(this._destroyed$)).subscribe((e) => {
|
|
61
|
-
if (this._config.attachTo?.nativeElement) {
|
|
62
|
-
e.preventDefault();
|
|
63
|
-
this._config.attachTo.nativeElement.focus();
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
60
|
setConfig(config) {
|
|
68
61
|
this._config = config;
|
|
69
62
|
if (config.attachTo) {
|
|
@@ -127,7 +120,9 @@ class BrnNavigationMenuContentService {
|
|
|
127
120
|
}
|
|
128
121
|
this._contentEl.set(contentEl);
|
|
129
122
|
this._overlayHoveredObservables$.next(createHoverObservable(this._overlayRef.hostElement, this._zone, this._destroyed$).pipe(map((e) => e.hover)));
|
|
130
|
-
this.
|
|
123
|
+
this._overlayTabObservables$.next(fromEvent(contentEl, 'keydown').pipe(
|
|
124
|
+
// Forward Tab/Shift+Tab without meta modifiers; the trigger decides how to keep focus in the menu
|
|
125
|
+
switchMap((e) => (e.key === 'Tab' && !e.altKey && !e.ctrlKey && !e.metaKey ? of(e) : of())), takeUntil(this._destroyed$)));
|
|
131
126
|
this._overlayEscapeObservables$.next(fromEvent(contentEl, 'keydown').pipe(switchMap((e) => (e.key === 'Escape' && !hasModifierKey(e) ? of(e) : of())), takeUntil(this._destroyed$)));
|
|
132
127
|
}
|
|
133
128
|
async hide() {
|
|
@@ -177,7 +172,7 @@ class BrnNavigationMenuContentService {
|
|
|
177
172
|
}
|
|
178
173
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BrnNavigationMenuContentService, decorators: [{
|
|
179
174
|
type: Injectable
|
|
180
|
-
}]
|
|
175
|
+
}] });
|
|
181
176
|
|
|
182
177
|
const BrnNavigationMenuFocusable = new InjectionToken('BrnNavigationMenuFocusable');
|
|
183
178
|
function provideBrnNavigationMenuFocusable(focusable) {
|
|
@@ -348,9 +343,8 @@ class BrnNavigationMenu {
|
|
|
348
343
|
* The orientation of the menu.
|
|
349
344
|
*/
|
|
350
345
|
orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
351
|
-
|
|
352
|
-
isOpenDelayed = this.
|
|
353
|
-
_skipDelayTimerRef;
|
|
346
|
+
_skipDelay = injectSkipDelay(() => this.skipDelayDuration());
|
|
347
|
+
isOpenDelayed = this._skipDelay.isOpenDelayed;
|
|
354
348
|
_navAndSubnavMenuItems = contentChildren(BrnNavigationMenuItem, { ...(ngDevMode ? { debugName: "_navAndSubnavMenuItems" } : /* istanbul ignore next */ {}), descendants: true });
|
|
355
349
|
menuItems = computed(() => this._navAndSubnavMenuItems().filter((mi) => mi.navMenuElRef === this.el), { ...(ngDevMode ? { debugName: "menuItems" } : /* istanbul ignore next */ {}), equal: areArraysSameByElRef });
|
|
356
350
|
menuItemIds = computed(() => this.menuItems().map((mi) => mi.id()), ...(ngDevMode ? [{ debugName: "menuItemIds" }] : /* istanbul ignore next */ []));
|
|
@@ -371,19 +365,10 @@ class BrnNavigationMenu {
|
|
|
371
365
|
context = computed(() => ({ orientation: this.orientation(), dir: this.direction() }), ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
372
366
|
constructor() {
|
|
373
367
|
effect(() => {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (hasSkipDelayDuration)
|
|
379
|
-
this._isOpenDelayed.set(false);
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
clearTimeout(this._skipDelayTimerRef);
|
|
383
|
-
this._skipDelayTimerRef = setTimeout(() => {
|
|
384
|
-
this._isOpenDelayed.set(true);
|
|
385
|
-
}, this.skipDelayDuration());
|
|
386
|
-
}
|
|
368
|
+
if (this.value() !== undefined)
|
|
369
|
+
this._skipDelay.open();
|
|
370
|
+
else
|
|
371
|
+
this._skipDelay.close();
|
|
387
372
|
});
|
|
388
373
|
combineLatest([this._hovered$, this._contentHovered$, this._anyTriggerHovered$])
|
|
389
374
|
.pipe(debounceTime(0), filter(([hovered, contentHovered]) => !(hovered === undefined && contentHovered === undefined)), switchMap(([hovered, contentHovered, triggerHovered]) => {
|
|
@@ -413,13 +398,27 @@ class BrnNavigationMenu {
|
|
|
413
398
|
setActiveItem(item) {
|
|
414
399
|
this._keyManager().setActiveItem(item);
|
|
415
400
|
}
|
|
401
|
+
/** Focuses the sibling trigger/link `delta` steps from `current`, skipping disabled ones. @internal */
|
|
402
|
+
focusSibling(current, delta) {
|
|
403
|
+
const items = this._triggersAndLinks();
|
|
404
|
+
const idx = items.indexOf(current);
|
|
405
|
+
if (idx === -1)
|
|
406
|
+
return false;
|
|
407
|
+
for (let i = idx + delta; i >= 0 && i < items.length; i += delta) {
|
|
408
|
+
const candidate = items[i];
|
|
409
|
+
if (!candidate.disabled) {
|
|
410
|
+
this._keyManager().setActiveItem(candidate);
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
416
|
handleKeydown(event) {
|
|
417
417
|
this._keyManager().onKeydown(event);
|
|
418
418
|
}
|
|
419
419
|
ngOnDestroy() {
|
|
420
420
|
this._destroy$.next();
|
|
421
421
|
this._destroy$.complete();
|
|
422
|
-
clearTimeout(this._skipDelayTimerRef);
|
|
423
422
|
}
|
|
424
423
|
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BrnNavigationMenu, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
425
424
|
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.2.16", type: BrnNavigationMenu, isStandalone: true, selector: "nav[brnNavigationMenu]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, delayDuration: { classPropertyName: "delayDuration", publicName: "delayDuration", isSignal: true, isRequired: false, transformFunction: null }, skipDelayDuration: { classPropertyName: "skipDelayDuration", publicName: "skipDelayDuration", isSignal: true, isRequired: false, transformFunction: null }, openOn: { classPropertyName: "openOn", publicName: "openOn", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { attributes: { "aria-label": "Main", "data-slot": "navigation-menu" }, listeners: { "keydown": "handleKeydown($event)" }, properties: { "attr.data-orientation": "orientation()", "attr.dir": "direction()" } }, providers: [provideBrnNavigationMenu(BrnNavigationMenu)], queries: [{ propertyName: "_navAndSubnavMenuItems", predicate: BrnNavigationMenuItem, descendants: true, isSignal: true }], ngImport: i0 });
|
|
@@ -536,6 +535,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
536
535
|
}]
|
|
537
536
|
}] });
|
|
538
537
|
|
|
538
|
+
/** Returns the tabbable elements inside `container`, in DOM order. Ported from Radix's navigation menu. */
|
|
539
|
+
function getTabbableCandidates(container) {
|
|
540
|
+
const nodes = [];
|
|
541
|
+
// `.tabIndex` reflects the runtime's view of tabbability, so it covers links/buttons/[tabindex] alike.
|
|
542
|
+
const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
|
|
543
|
+
acceptNode: (node) => {
|
|
544
|
+
const el = node;
|
|
545
|
+
const isHiddenInput = el.tagName === 'INPUT' && el.type === 'hidden';
|
|
546
|
+
if (el.disabled || el.hidden || isHiddenInput)
|
|
547
|
+
return NodeFilter.FILTER_SKIP;
|
|
548
|
+
return el.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
while (walker.nextNode())
|
|
552
|
+
nodes.push(walker.currentNode);
|
|
553
|
+
return nodes;
|
|
554
|
+
}
|
|
555
|
+
/** Focuses the first candidate that can take focus; returns whether focus landed on a candidate. */
|
|
556
|
+
function focusFirst(candidates) {
|
|
557
|
+
const previouslyFocused = document.activeElement;
|
|
558
|
+
return candidates.some((candidate) => {
|
|
559
|
+
// already where we want to be - stop walking
|
|
560
|
+
if (candidate === previouslyFocused)
|
|
561
|
+
return true;
|
|
562
|
+
candidate.focus();
|
|
563
|
+
return document.activeElement !== previouslyFocused;
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
539
567
|
class BrnNavigationMenuTrigger {
|
|
540
568
|
static _id = 0;
|
|
541
569
|
_isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
@@ -550,6 +578,8 @@ class BrnNavigationMenuTrigger {
|
|
|
550
578
|
_id = `brn-navigation-menu-trigger-${++BrnNavigationMenuTrigger._id}`;
|
|
551
579
|
_parentNavMenu = this._navigationMenu.parentNavMenu;
|
|
552
580
|
_isActive = this._navigationMenuItem.isActive;
|
|
581
|
+
// Only reference the content panel while it is open, matching Radix's aria-controls behaviour.
|
|
582
|
+
_ariaControls = computed(() => (this._isActive() ? this._contentId : null), ...(ngDevMode ? [{ debugName: "_ariaControls" }] : /* istanbul ignore next */ []));
|
|
553
583
|
_contentId = this._contentService.id;
|
|
554
584
|
_state = this._navigationMenuItem.state;
|
|
555
585
|
_dir = computed(() => this._navigationMenu.context().dir, ...(ngDevMode ? [{ debugName: "_dir" }] : /* istanbul ignore next */ []));
|
|
@@ -654,6 +684,7 @@ class BrnNavigationMenuTrigger {
|
|
|
654
684
|
this._el.nativeElement.focus();
|
|
655
685
|
this._deactivate();
|
|
656
686
|
});
|
|
687
|
+
this._contentService.tabPressed$.pipe(takeUntil(this._destroy$)).subscribe((e) => this._handleContentTab(e));
|
|
657
688
|
}
|
|
658
689
|
ngOnDestroy() {
|
|
659
690
|
this._destroy$.next();
|
|
@@ -671,13 +702,69 @@ class BrnNavigationMenuTrigger {
|
|
|
671
702
|
const contentEl = this._contentService.contentEl();
|
|
672
703
|
if (contentEl && !hasModifierKey(e)) {
|
|
673
704
|
e.preventDefault();
|
|
674
|
-
|
|
705
|
+
this._focusFirstContent();
|
|
675
706
|
}
|
|
676
707
|
}
|
|
708
|
+
/** Arrow-key entry into the open content: ArrowDown (horizontal) or ArrowRight/Left (vertical, dir-aware). */
|
|
709
|
+
onEntryKey(e, orientation, dir) {
|
|
710
|
+
if (!this._navigationMenuItem.isActive())
|
|
711
|
+
return;
|
|
712
|
+
if (this._orientation() !== orientation)
|
|
713
|
+
return;
|
|
714
|
+
if (orientation === 'vertical' && dir && this._dir() !== dir)
|
|
715
|
+
return;
|
|
716
|
+
e.preventDefault();
|
|
717
|
+
// Stop the event from reaching the nav's CDK key manager, which would otherwise
|
|
718
|
+
// read event.keyCode and move focus to the next trigger, overriding the entry.
|
|
719
|
+
e.stopPropagation();
|
|
720
|
+
this._focusFirstContent();
|
|
721
|
+
}
|
|
677
722
|
onEscape(e) {
|
|
678
723
|
e.preventDefault();
|
|
679
724
|
this._deactivate();
|
|
680
725
|
}
|
|
726
|
+
_focusFirstContent() {
|
|
727
|
+
const contentEl = this._contentService.contentEl();
|
|
728
|
+
if (!contentEl)
|
|
729
|
+
return;
|
|
730
|
+
const candidates = getTabbableCandidates(contentEl);
|
|
731
|
+
if (candidates.length) {
|
|
732
|
+
focusFirst(candidates);
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
contentEl.focus();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
/** Keeps keyboard focus inside the menu when tabbing across the content edges (issue #1484). */
|
|
739
|
+
_handleContentTab(event) {
|
|
740
|
+
const contentEl = this._contentService.contentEl();
|
|
741
|
+
if (!contentEl)
|
|
742
|
+
return;
|
|
743
|
+
const candidates = getTabbableCandidates(contentEl);
|
|
744
|
+
const focused = document.activeElement;
|
|
745
|
+
const index = focused ? candidates.indexOf(focused) : -1;
|
|
746
|
+
if (event.shiftKey) {
|
|
747
|
+
const previous = index > 0 ? candidates.slice(0, index).reverse() : [];
|
|
748
|
+
if (previous.length && focusFirst(previous)) {
|
|
749
|
+
event.preventDefault();
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
// at the first item or on the content container - return to the trigger, keep content open
|
|
753
|
+
event.preventDefault();
|
|
754
|
+
this._el.nativeElement.focus();
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const next = index >= 0 ? candidates.slice(index + 1) : candidates;
|
|
758
|
+
if (next.length && focusFirst(next)) {
|
|
759
|
+
event.preventDefault();
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
// past the last item - hand focus to the next top-level item and close the panel
|
|
763
|
+
if (this._navigationMenu.focusSibling(this, 1)) {
|
|
764
|
+
event.preventDefault();
|
|
765
|
+
}
|
|
766
|
+
this._deactivate();
|
|
767
|
+
}
|
|
681
768
|
_activate() {
|
|
682
769
|
this._navigationMenu.value.set(this._navigationMenuItem.id());
|
|
683
770
|
}
|
|
@@ -696,7 +783,7 @@ class BrnNavigationMenuTrigger {
|
|
|
696
783
|
.find((ref) => ref.el.nativeElement === node || ref.el.nativeElement.contains(node));
|
|
697
784
|
}
|
|
698
785
|
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BrnNavigationMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
699
|
-
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.16", type: BrnNavigationMenuTrigger, isStandalone: true, selector: "button[brnNavigationMenuTrigger]", inputs: { align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "navigation-menu-trigger" }, listeners: { "keydown.escape": "onEscape($event)", "keydown.tab": "onTab($event)", "focus": "handleFocus()" }, properties: { "id": "_id", "attr.data-state": "_state()", "attr.aria-expanded": "_isActive()", "attr.aria-controls": "
|
|
786
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.16", type: BrnNavigationMenuTrigger, isStandalone: true, selector: "button[brnNavigationMenuTrigger]", inputs: { align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "navigation-menu-trigger" }, listeners: { "keydown.escape": "onEscape($event)", "keydown.tab": "onTab($event)", "keydown.arrowDown": "onEntryKey($event, \"horizontal\")", "keydown.arrowRight": "onEntryKey($event, \"vertical\", \"ltr\")", "keydown.arrowLeft": "onEntryKey($event, \"vertical\", \"rtl\")", "focus": "handleFocus()" }, properties: { "id": "_id", "attr.data-state": "_state()", "attr.aria-expanded": "_isActive()", "attr.aria-controls": "_ariaControls()" } }, providers: [provideBrnNavigationMenuFocusable(BrnNavigationMenuTrigger)], hostDirectives: [{ directive: i1.BrnButton, inputs: ["disabled", "disabled"] }], ngImport: i0 });
|
|
700
787
|
}
|
|
701
788
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BrnNavigationMenuTrigger, decorators: [{
|
|
702
789
|
type: Directive,
|
|
@@ -712,11 +799,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
712
799
|
host: {
|
|
713
800
|
'(keydown.escape)': 'onEscape($event)',
|
|
714
801
|
'(keydown.tab)': 'onTab($event)',
|
|
802
|
+
'(keydown.arrowDown)': 'onEntryKey($event, "horizontal")',
|
|
803
|
+
'(keydown.arrowRight)': 'onEntryKey($event, "vertical", "ltr")',
|
|
804
|
+
'(keydown.arrowLeft)': 'onEntryKey($event, "vertical", "rtl")',
|
|
715
805
|
'(focus)': 'handleFocus()',
|
|
716
806
|
'[id]': '_id',
|
|
717
807
|
'[attr.data-state]': '_state()',
|
|
718
808
|
'[attr.aria-expanded]': '_isActive()',
|
|
719
|
-
'[attr.aria-controls]': '
|
|
809
|
+
'[attr.aria-controls]': '_ariaControls()',
|
|
720
810
|
'data-slot': 'navigation-menu-trigger',
|
|
721
811
|
},
|
|
722
812
|
}]
|