@ngneers/controls 0.0.1-next.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/fesm2022/ngneers-controls-accordion.mjs +234 -0
- package/fesm2022/ngneers-controls-accordion.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api-ng.mjs +959 -0
- package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api-resize.mjs +712 -0
- package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api.mjs +347 -0
- package/fesm2022/ngneers-controls-api.mjs.map +1 -0
- package/fesm2022/ngneers-controls-avatar.mjs +77 -0
- package/fesm2022/ngneers-controls-avatar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-base.mjs +620 -0
- package/fesm2022/ngneers-controls-base.mjs.map +1 -0
- package/fesm2022/ngneers-controls-breadcrumb.mjs +100 -0
- package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -0
- package/fesm2022/ngneers-controls-button-group.mjs +110 -0
- package/fesm2022/ngneers-controls-button-group.mjs.map +1 -0
- package/fesm2022/ngneers-controls-button.mjs +83 -0
- package/fesm2022/ngneers-controls-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs +74 -0
- package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs.map +1 -0
- package/fesm2022/ngneers-controls-calendar.mjs +801 -0
- package/fesm2022/ngneers-controls-calendar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-checkbox.mjs +63 -0
- package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -0
- package/fesm2022/ngneers-controls-chip.mjs +55 -0
- package/fesm2022/ngneers-controls-chip.mjs.map +1 -0
- package/fesm2022/ngneers-controls-default-icons.mjs +82 -0
- package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
- package/fesm2022/ngneers-controls-defer.mjs +72 -0
- package/fesm2022/ngneers-controls-defer.mjs.map +1 -0
- package/fesm2022/ngneers-controls-dialog.mjs +407 -0
- package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
- package/fesm2022/ngneers-controls-directives.mjs +498 -0
- package/fesm2022/ngneers-controls-directives.mjs.map +1 -0
- package/fesm2022/ngneers-controls-drawer.mjs +271 -0
- package/fesm2022/ngneers-controls-drawer.mjs.map +1 -0
- package/fesm2022/ngneers-controls-edit-inplace.mjs +127 -0
- package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -0
- package/fesm2022/ngneers-controls-errors.mjs +329 -0
- package/fesm2022/ngneers-controls-errors.mjs.map +1 -0
- package/fesm2022/ngneers-controls-filter.mjs +708 -0
- package/fesm2022/ngneers-controls-filter.mjs.map +1 -0
- package/fesm2022/ngneers-controls-hint.mjs +153 -0
- package/fesm2022/ngneers-controls-hint.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n-translations-de.mjs +170 -0
- package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n-translations-en.mjs +170 -0
- package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n.mjs +57 -0
- package/fesm2022/ngneers-controls-i18n.mjs.map +1 -0
- package/fesm2022/ngneers-controls-icon.mjs +195 -0
- package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
- package/fesm2022/ngneers-controls-inplace.mjs +150 -0
- package/fesm2022/ngneers-controls-inplace.mjs.map +1 -0
- package/fesm2022/ngneers-controls-input-field.mjs +165 -0
- package/fesm2022/ngneers-controls-input-field.mjs.map +1 -0
- package/fesm2022/ngneers-controls-input.mjs +72 -0
- package/fesm2022/ngneers-controls-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-item-view.mjs +473 -0
- package/fesm2022/ngneers-controls-item-view.mjs.map +1 -0
- package/fesm2022/ngneers-controls-list-box.mjs +281 -0
- package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
- package/fesm2022/ngneers-controls-mask-input.mjs +888 -0
- package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-menu.mjs +371 -0
- package/fesm2022/ngneers-controls-menu.mjs.map +1 -0
- package/fesm2022/ngneers-controls-message.mjs +30 -0
- package/fesm2022/ngneers-controls-message.mjs.map +1 -0
- package/fesm2022/ngneers-controls-number-input.mjs +366 -0
- package/fesm2022/ngneers-controls-number-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-paginator.mjs +129 -0
- package/fesm2022/ngneers-controls-paginator.mjs.map +1 -0
- package/fesm2022/ngneers-controls-popover.mjs +215 -0
- package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
- package/fesm2022/ngneers-controls-progress.mjs +98 -0
- package/fesm2022/ngneers-controls-progress.mjs.map +1 -0
- package/fesm2022/ngneers-controls-radio.mjs +152 -0
- package/fesm2022/ngneers-controls-radio.mjs.map +1 -0
- package/fesm2022/ngneers-controls-roving-focus.mjs +323 -0
- package/fesm2022/ngneers-controls-roving-focus.mjs.map +1 -0
- package/fesm2022/ngneers-controls-scroll-shadow.mjs +65 -0
- package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
- package/fesm2022/ngneers-controls-scroller.mjs +308 -0
- package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
- package/fesm2022/ngneers-controls-select-button.mjs +58 -0
- package/fesm2022/ngneers-controls-select-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-select.mjs +417 -0
- package/fesm2022/ngneers-controls-select.mjs.map +1 -0
- package/fesm2022/ngneers-controls-slider.mjs +167 -0
- package/fesm2022/ngneers-controls-slider.mjs.map +1 -0
- package/fesm2022/ngneers-controls-snackbar.mjs +414 -0
- package/fesm2022/ngneers-controls-snackbar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-spin-buttons.mjs +126 -0
- package/fesm2022/ngneers-controls-spin-buttons.mjs.map +1 -0
- package/fesm2022/ngneers-controls-spinner.mjs +184 -0
- package/fesm2022/ngneers-controls-spinner.mjs.map +1 -0
- package/fesm2022/ngneers-controls-splitter.mjs +604 -0
- package/fesm2022/ngneers-controls-splitter.mjs.map +1 -0
- package/fesm2022/ngneers-controls-state.mjs +185 -0
- package/fesm2022/ngneers-controls-state.mjs.map +1 -0
- package/fesm2022/ngneers-controls-switch.mjs +39 -0
- package/fesm2022/ngneers-controls-switch.mjs.map +1 -0
- package/fesm2022/ngneers-controls-table.mjs +2634 -0
- package/fesm2022/ngneers-controls-table.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tabs.mjs +336 -0
- package/fesm2022/ngneers-controls-tabs.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tag.mjs +30 -0
- package/fesm2022/ngneers-controls-tag.mjs.map +1 -0
- package/fesm2022/ngneers-controls-toast.mjs +350 -0
- package/fesm2022/ngneers-controls-toast.mjs.map +1 -0
- package/fesm2022/ngneers-controls-toggle-button.mjs +70 -0
- package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tooltip.mjs +506 -0
- package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tree.mjs +599 -0
- package/fesm2022/ngneers-controls-tree.mjs.map +1 -0
- package/fesm2022/ngneers-controls-upload.mjs +377 -0
- package/fesm2022/ngneers-controls-upload.mjs.map +1 -0
- package/fesm2022/ngneers-controls-utils-ng.mjs +411 -0
- package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -0
- package/fesm2022/ngneers-controls-utils.mjs +261 -0
- package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
- package/fesm2022/ngneers-controls.mjs +6 -0
- package/fesm2022/ngneers-controls.mjs.map +1 -0
- package/package.json +302 -0
- package/types/ngneers-controls-accordion.d.ts +139 -0
- package/types/ngneers-controls-api-ng.d.ts +364 -0
- package/types/ngneers-controls-api-resize.d.ts +318 -0
- package/types/ngneers-controls-api.d.ts +145 -0
- package/types/ngneers-controls-avatar.d.ts +54 -0
- package/types/ngneers-controls-base.d.ts +277 -0
- package/types/ngneers-controls-breadcrumb.d.ts +272 -0
- package/types/ngneers-controls-button-group.d.ts +46 -0
- package/types/ngneers-controls-button.d.ts +48 -0
- package/types/ngneers-controls-calendar.d.ts +466 -0
- package/types/ngneers-controls-checkbox.d.ts +40 -0
- package/types/ngneers-controls-chip.d.ts +202 -0
- package/types/ngneers-controls-default-icons.d.ts +5 -0
- package/types/ngneers-controls-defer.d.ts +48 -0
- package/types/ngneers-controls-dialog.d.ts +399 -0
- package/types/ngneers-controls-directives.d.ts +171 -0
- package/types/ngneers-controls-drawer.d.ts +308 -0
- package/types/ngneers-controls-edit-inplace.d.ts +263 -0
- package/types/ngneers-controls-errors.d.ts +155 -0
- package/types/ngneers-controls-filter.d.ts +609 -0
- package/types/ngneers-controls-hint.d.ts +88 -0
- package/types/ngneers-controls-i18n-translations-de.d.ts +164 -0
- package/types/ngneers-controls-i18n-translations-en.d.ts +164 -0
- package/types/ngneers-controls-i18n.d.ts +186 -0
- package/types/ngneers-controls-icon.d.ts +68 -0
- package/types/ngneers-controls-inplace.d.ts +98 -0
- package/types/ngneers-controls-input-field.d.ts +248 -0
- package/types/ngneers-controls-input.d.ts +30 -0
- package/types/ngneers-controls-item-view.d.ts +215 -0
- package/types/ngneers-controls-list-box.d.ts +334 -0
- package/types/ngneers-controls-mask-input.d.ts +434 -0
- package/types/ngneers-controls-menu.d.ts +192 -0
- package/types/ngneers-controls-message.d.ts +21 -0
- package/types/ngneers-controls-number-input.d.ts +146 -0
- package/types/ngneers-controls-paginator.d.ts +255 -0
- package/types/ngneers-controls-popover.d.ts +116 -0
- package/types/ngneers-controls-progress.d.ts +49 -0
- package/types/ngneers-controls-radio.d.ts +91 -0
- package/types/ngneers-controls-roving-focus.d.ts +107 -0
- package/types/ngneers-controls-scroll-shadow.d.ts +25 -0
- package/types/ngneers-controls-scroller.d.ts +151 -0
- package/types/ngneers-controls-select-button.d.ts +51 -0
- package/types/ngneers-controls-select.d.ts +409 -0
- package/types/ngneers-controls-slider.d.ts +57 -0
- package/types/ngneers-controls-snackbar.d.ts +330 -0
- package/types/ngneers-controls-spin-buttons.d.ts +69 -0
- package/types/ngneers-controls-spinner.d.ts +117 -0
- package/types/ngneers-controls-splitter.d.ts +465 -0
- package/types/ngneers-controls-state.d.ts +102 -0
- package/types/ngneers-controls-switch.d.ts +18 -0
- package/types/ngneers-controls-table.d.ts +3127 -0
- package/types/ngneers-controls-tabs.d.ts +118 -0
- package/types/ngneers-controls-tag.d.ts +21 -0
- package/types/ngneers-controls-toast.d.ts +298 -0
- package/types/ngneers-controls-toggle-button.d.ts +48 -0
- package/types/ngneers-controls-tooltip.d.ts +189 -0
- package/types/ngneers-controls-tree.d.ts +409 -0
- package/types/ngneers-controls-upload.d.ts +347 -0
- package/types/ngneers-controls-utils-ng.d.ts +129 -0
- package/types/ngneers-controls-utils.d.ts +130 -0
- package/types/ngneers-controls.d.ts +2 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, input, booleanAttribute, output, inject, ElementRef, signal, computed, effect, Directive } from '@angular/core';
|
|
3
|
+
import { domEventHandler } from '@ngneers/controls/api/ng';
|
|
4
|
+
import { generateElementId } from '@ngneers/controls/utils-ng';
|
|
5
|
+
|
|
6
|
+
const ROVING_GROUP = new InjectionToken('ROVING_GROUP');
|
|
7
|
+
class NgnRovingGroup {
|
|
8
|
+
/**
|
|
9
|
+
* Which arrow keys move the active item: `'horizontal'` uses Left/Right,
|
|
10
|
+
* `'vertical'` uses Up/Down. Home/End always jump to first/last.
|
|
11
|
+
* @default 'horizontal'
|
|
12
|
+
*/
|
|
13
|
+
orientation = input('horizontal', /* @ts-ignore */
|
|
14
|
+
...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
15
|
+
/**
|
|
16
|
+
* How the active item is exposed. `'tabindex'` moves the tab stop (and DOM
|
|
17
|
+
* focus) between items; `'activedescendant'` keeps focus on the group and
|
|
18
|
+
* points `aria-activedescendant` at the active item.
|
|
19
|
+
* @default 'tabindex'
|
|
20
|
+
*/
|
|
21
|
+
rovingMode = input('tabindex', /* @ts-ignore */
|
|
22
|
+
...(ngDevMode ? [{ debugName: "rovingMode" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/**
|
|
24
|
+
* Whether keyboard navigation wraps around the ends of the list instead of
|
|
25
|
+
* stopping at the first/last item.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
rovingWrap = input(false, { ...(ngDevMode ? { debugName: "rovingWrap" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
29
|
+
/**
|
|
30
|
+
* Emits the index of the newly active item whenever it changes via keyboard,
|
|
31
|
+
* pointer, or {@link setActive}. Not emitted by {@link syncActiveIndex}.
|
|
32
|
+
*/
|
|
33
|
+
activeItemChange = output();
|
|
34
|
+
_host = inject(ElementRef).nativeElement;
|
|
35
|
+
_items = signal([], /* @ts-ignore */
|
|
36
|
+
...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
|
|
37
|
+
/** Items in DOM order. */
|
|
38
|
+
items = computed(() => [...this._items()].sort((a, b) => {
|
|
39
|
+
if (a.element === b.element)
|
|
40
|
+
return 0;
|
|
41
|
+
return a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
42
|
+
? -1
|
|
43
|
+
: 1;
|
|
44
|
+
}), /* @ts-ignore */
|
|
45
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
46
|
+
activeIndex = signal(0, /* @ts-ignore */
|
|
47
|
+
...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
|
|
48
|
+
/**
|
|
49
|
+
* Tracks the previously active item's id so we can decide whether to call
|
|
50
|
+
* `.focus()`. Initialized to `null` — the first effect pass sets it without
|
|
51
|
+
* focusing; subsequent passes focus when the id actually changes.
|
|
52
|
+
*/
|
|
53
|
+
_prevActiveId = null;
|
|
54
|
+
/**
|
|
55
|
+
* When set, the next focus-mode effect pass updates the tab stop / attributes
|
|
56
|
+
* but does NOT move DOM focus. Consumed (reset) by that pass. Lets consumers
|
|
57
|
+
* sync the active item to a programmatic selection (e.g. a radio group's
|
|
58
|
+
* `value` change) without stealing focus into the group.
|
|
59
|
+
*/
|
|
60
|
+
_suppressFocus = false;
|
|
61
|
+
constructor() {
|
|
62
|
+
domEventHandler(this._host, 'keydown', e => this._onKeydown(e));
|
|
63
|
+
// Apply focus-mode side effects reactively.
|
|
64
|
+
effect(() => {
|
|
65
|
+
const items = this.items();
|
|
66
|
+
const mode = this.rovingMode();
|
|
67
|
+
// Clamp the active index LOCALLY (do not mutate the activeIndex signal)
|
|
68
|
+
// so that attributes/focus always reference a valid item even if
|
|
69
|
+
// activeIndex is stale after the items list shrinks.
|
|
70
|
+
const active = items.length
|
|
71
|
+
? Math.max(0, Math.min(this.activeIndex(), items.length - 1))
|
|
72
|
+
: this.activeIndex();
|
|
73
|
+
if (mode === 'tabindex') {
|
|
74
|
+
// Clean up activedescendant mode attributes.
|
|
75
|
+
this._host.removeAttribute('aria-activedescendant');
|
|
76
|
+
this._host.removeAttribute('aria-owns');
|
|
77
|
+
// Apply tabindex to all items.
|
|
78
|
+
items.forEach((it, i) => {
|
|
79
|
+
it.element.setAttribute('tabindex', i === active ? '0' : '-1');
|
|
80
|
+
});
|
|
81
|
+
// Move focus when the active item changes — but NOT on the first pass
|
|
82
|
+
// (where _prevActiveId is null) to avoid stealing focus on mount.
|
|
83
|
+
const activeEl = items[active]?.element;
|
|
84
|
+
const activeId = activeEl?.id ?? null;
|
|
85
|
+
if (activeEl &&
|
|
86
|
+
this._prevActiveId !== null &&
|
|
87
|
+
this._prevActiveId !== activeId &&
|
|
88
|
+
!this._suppressFocus) {
|
|
89
|
+
activeEl.focus();
|
|
90
|
+
}
|
|
91
|
+
this._prevActiveId = activeId;
|
|
92
|
+
this._suppressFocus = false;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// activedescendant mode: remove tabindex from all items.
|
|
96
|
+
items.forEach(it => it.element.removeAttribute('tabindex'));
|
|
97
|
+
// Guard the empty-items window (items register during effects, so the
|
|
98
|
+
// first effect pass may see an empty list).
|
|
99
|
+
if (!items.length) {
|
|
100
|
+
this._host.removeAttribute('aria-activedescendant');
|
|
101
|
+
this._host.removeAttribute('aria-owns');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this._host.setAttribute('aria-owns', items.map(i => i.id).join(' '));
|
|
105
|
+
const activeId = items[active]?.id;
|
|
106
|
+
if (activeId) {
|
|
107
|
+
this._host.setAttribute('aria-activedescendant', activeId);
|
|
108
|
+
}
|
|
109
|
+
// Reset the focus guard when in activedescendant mode so that switching
|
|
110
|
+
// back to tabindex mode doesn't immediately call focus.
|
|
111
|
+
this._prevActiveId = null;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
register(item) {
|
|
116
|
+
this._items.update(list => (list.includes(item) ? list : [...list, item]));
|
|
117
|
+
}
|
|
118
|
+
unregister(item) {
|
|
119
|
+
this._items.update(list => list.filter(i => i !== item));
|
|
120
|
+
// Normalize the stored active index so it never dangles past the end —
|
|
121
|
+
// otherwise NgnRovingItem.isActive() would be false for every item.
|
|
122
|
+
const n = this.items().length;
|
|
123
|
+
if (this.activeIndex() >= n) {
|
|
124
|
+
this.activeIndex.set(Math.max(0, n - 1));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
activate(target) {
|
|
128
|
+
if (target.disabled?.())
|
|
129
|
+
return;
|
|
130
|
+
const idx = this.items().indexOf(target);
|
|
131
|
+
if (idx >= 0)
|
|
132
|
+
this._setActive(idx);
|
|
133
|
+
}
|
|
134
|
+
next() {
|
|
135
|
+
this._move(1);
|
|
136
|
+
}
|
|
137
|
+
prev() {
|
|
138
|
+
this._move(-1);
|
|
139
|
+
}
|
|
140
|
+
first() {
|
|
141
|
+
const items = this.items();
|
|
142
|
+
const idx = items.findIndex(it => !it.disabled?.());
|
|
143
|
+
if (idx >= 0)
|
|
144
|
+
this._setActive(idx);
|
|
145
|
+
}
|
|
146
|
+
last() {
|
|
147
|
+
const items = this.items();
|
|
148
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
149
|
+
if (!items[i]?.disabled?.()) {
|
|
150
|
+
this._setActive(i);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Set the active item by index (clamped to range); emits activeItemChange. */
|
|
156
|
+
setActive(index) {
|
|
157
|
+
if (index >= 0 && index < this.items().length)
|
|
158
|
+
this._setActive(index);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Move the tab stop to `index` WITHOUT moving DOM focus or emitting
|
|
162
|
+
* `activeItemChange`. For syncing the roving state to a programmatic
|
|
163
|
+
* selection (e.g. a radio group's `value` changing from code) where stealing
|
|
164
|
+
* focus into the group would be wrong.
|
|
165
|
+
*/
|
|
166
|
+
syncActiveIndex(index) {
|
|
167
|
+
if (index < 0 || index >= this.items().length)
|
|
168
|
+
return;
|
|
169
|
+
if (index === this.activeIndex())
|
|
170
|
+
return;
|
|
171
|
+
this._suppressFocus = true;
|
|
172
|
+
this.activeIndex.set(index);
|
|
173
|
+
}
|
|
174
|
+
_onKeydown(e) {
|
|
175
|
+
if (e.ctrlKey || e.metaKey || e.altKey)
|
|
176
|
+
return;
|
|
177
|
+
const horizontal = this.orientation() === 'horizontal';
|
|
178
|
+
let consumed = true;
|
|
179
|
+
switch (e.key) {
|
|
180
|
+
case 'ArrowRight':
|
|
181
|
+
if (horizontal)
|
|
182
|
+
this.next();
|
|
183
|
+
else
|
|
184
|
+
consumed = false;
|
|
185
|
+
break;
|
|
186
|
+
case 'ArrowLeft':
|
|
187
|
+
if (horizontal)
|
|
188
|
+
this.prev();
|
|
189
|
+
else
|
|
190
|
+
consumed = false;
|
|
191
|
+
break;
|
|
192
|
+
case 'ArrowDown':
|
|
193
|
+
if (!horizontal)
|
|
194
|
+
this.next();
|
|
195
|
+
else
|
|
196
|
+
consumed = false;
|
|
197
|
+
break;
|
|
198
|
+
case 'ArrowUp':
|
|
199
|
+
if (!horizontal)
|
|
200
|
+
this.prev();
|
|
201
|
+
else
|
|
202
|
+
consumed = false;
|
|
203
|
+
break;
|
|
204
|
+
case 'Home':
|
|
205
|
+
this.first();
|
|
206
|
+
break;
|
|
207
|
+
case 'End':
|
|
208
|
+
this.last();
|
|
209
|
+
break;
|
|
210
|
+
default:
|
|
211
|
+
consumed = false;
|
|
212
|
+
}
|
|
213
|
+
if (consumed)
|
|
214
|
+
e.preventDefault();
|
|
215
|
+
}
|
|
216
|
+
_move(delta) {
|
|
217
|
+
const items = this.items();
|
|
218
|
+
const n = items.length;
|
|
219
|
+
if (!n)
|
|
220
|
+
return;
|
|
221
|
+
const start = this.activeIndex();
|
|
222
|
+
let i = start;
|
|
223
|
+
// Step in `delta` direction until we land on an enabled item, skipping
|
|
224
|
+
// disabled ones. When no enabled target exists in that direction we clamp
|
|
225
|
+
// to the current index and still emit — preserving the pre-existing
|
|
226
|
+
// clamp-and-emit contract. Bounded by `n` iterations so it terminates.
|
|
227
|
+
for (let step = 0; step < n; step++) {
|
|
228
|
+
i += delta;
|
|
229
|
+
if (this.rovingWrap()) {
|
|
230
|
+
i = ((i % n) + n) % n;
|
|
231
|
+
if (i === start)
|
|
232
|
+
break; // looped back — no other enabled item
|
|
233
|
+
}
|
|
234
|
+
else if (i < 0 || i >= n) {
|
|
235
|
+
break; // reached an edge without finding an enabled item
|
|
236
|
+
}
|
|
237
|
+
if (!items[i]?.disabled?.()) {
|
|
238
|
+
this._setActive(i);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
this._setActive(start);
|
|
243
|
+
}
|
|
244
|
+
_setActive(i) {
|
|
245
|
+
this.activeIndex.set(i);
|
|
246
|
+
this.activeItemChange.emit(i);
|
|
247
|
+
}
|
|
248
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnRovingGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
249
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: NgnRovingGroup, isStandalone: true, selector: "[ngnRovingGroup]", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, rovingMode: { classPropertyName: "rovingMode", publicName: "rovingMode", isSignal: true, isRequired: false, transformFunction: null }, rovingWrap: { classPropertyName: "rovingWrap", publicName: "rovingWrap", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeItemChange: "activeItemChange" }, providers: [{ provide: ROVING_GROUP, useExisting: NgnRovingGroup }], exportAs: ["ngnRovingGroup"], ngImport: i0 });
|
|
250
|
+
}
|
|
251
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnRovingGroup, decorators: [{
|
|
252
|
+
type: Directive,
|
|
253
|
+
args: [{
|
|
254
|
+
selector: '[ngnRovingGroup]',
|
|
255
|
+
providers: [{ provide: ROVING_GROUP, useExisting: NgnRovingGroup }],
|
|
256
|
+
exportAs: 'ngnRovingGroup',
|
|
257
|
+
}]
|
|
258
|
+
}], ctorParameters: () => [], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], rovingMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "rovingMode", required: false }] }], rovingWrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "rovingWrap", required: false }] }], activeItemChange: [{ type: i0.Output, args: ["activeItemChange"] }] } });
|
|
259
|
+
class NgnRovingItem {
|
|
260
|
+
_injectedGroup = inject(ROVING_GROUP, { optional: true });
|
|
261
|
+
/**
|
|
262
|
+
* The {@link NgnRovingGroup} this item belongs to. Pass a group reference when
|
|
263
|
+
* the item is not a DOM descendant of its group; leave empty/undefined to use
|
|
264
|
+
* the nearest ancestor group via dependency injection.
|
|
265
|
+
*/
|
|
266
|
+
ngnRovingItem = input(undefined, /* @ts-ignore */
|
|
267
|
+
...(ngDevMode ? [{ debugName: "ngnRovingItem" }] : /* istanbul ignore next */ []));
|
|
268
|
+
element = inject(ElementRef).nativeElement;
|
|
269
|
+
id;
|
|
270
|
+
/**
|
|
271
|
+
* Reactive disabled flag for this item. Consumers (e.g. `ngn-radio`) set it
|
|
272
|
+
* so the group's keyboard navigation and pointer `activate()` skip the item.
|
|
273
|
+
*/
|
|
274
|
+
disabled = signal(false, /* @ts-ignore */
|
|
275
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
276
|
+
_group = computed(() => {
|
|
277
|
+
const explicit = this.ngnRovingItem();
|
|
278
|
+
const group = explicit instanceof NgnRovingGroup ? explicit : this._injectedGroup;
|
|
279
|
+
if (!group) {
|
|
280
|
+
throw new Error('ngnRovingItem: no NgnRovingGroup found. Provide via [ngnRovingItem]="group" or nest inside one.');
|
|
281
|
+
}
|
|
282
|
+
return group;
|
|
283
|
+
}, /* @ts-ignore */
|
|
284
|
+
...(ngDevMode ? [{ debugName: "_group" }] : /* istanbul ignore next */ []));
|
|
285
|
+
/** Whether this item is currently the active item in its group. */
|
|
286
|
+
isActive = computed(() => {
|
|
287
|
+
const g = this._group();
|
|
288
|
+
return g.items()[g.activeIndex()] === this;
|
|
289
|
+
}, /* @ts-ignore */
|
|
290
|
+
...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
|
|
291
|
+
constructor() {
|
|
292
|
+
// generateElementId() must be called in injection context (constructor is fine).
|
|
293
|
+
this.id = this.element.id || generateElementId();
|
|
294
|
+
if (!this.element.id)
|
|
295
|
+
this.element.id = this.id;
|
|
296
|
+
// Use effect() rather than afterNextRender() so that registration is
|
|
297
|
+
// guaranteed to run synchronously during detectChanges() in Vitest/TestBed.
|
|
298
|
+
// effect() reads this._group() reactively and (re)registers whenever the
|
|
299
|
+
// resolved group changes; the onCleanup callback unregisters from the
|
|
300
|
+
// previous group before re-running, and on destroy. Equivalent to
|
|
301
|
+
// afterNextRender() in a browser environment.
|
|
302
|
+
effect(onCleanup => {
|
|
303
|
+
const group = this._group();
|
|
304
|
+
group.register(this);
|
|
305
|
+
onCleanup(() => group.unregister(this));
|
|
306
|
+
});
|
|
307
|
+
// Activate self in the group on pointerdown.
|
|
308
|
+
domEventHandler(this.element, 'pointerdown', () => this._group().activate(this));
|
|
309
|
+
}
|
|
310
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnRovingItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
311
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: NgnRovingItem, isStandalone: true, selector: "[ngnRovingItem]", inputs: { ngnRovingItem: { classPropertyName: "ngnRovingItem", publicName: "ngnRovingItem", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["ngnRovingItem"], ngImport: i0 });
|
|
312
|
+
}
|
|
313
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnRovingItem, decorators: [{
|
|
314
|
+
type: Directive,
|
|
315
|
+
args: [{ selector: '[ngnRovingItem]', exportAs: 'ngnRovingItem' }]
|
|
316
|
+
}], ctorParameters: () => [], propDecorators: { ngnRovingItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnRovingItem", required: false }] }] } });
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Generated bundle index. Do not edit.
|
|
320
|
+
*/
|
|
321
|
+
|
|
322
|
+
export { NgnRovingGroup, NgnRovingItem, ROVING_GROUP };
|
|
323
|
+
//# sourceMappingURL=ngneers-controls-roving-focus.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngneers-controls-roving-focus.mjs","sources":["../../src/roving-focus/roving-focus.ts","../../src/roving-focus/ngneers-controls-roving-focus.ts"],"sourcesContent":["import {\n booleanAttribute,\n computed,\n Directive,\n effect,\n ElementRef,\n inject,\n InjectionToken,\n input,\n output,\n signal,\n type Signal,\n} from '@angular/core';\nimport { domEventHandler } from '@ngneers/controls/api/ng';\nimport { generateElementId } from '@ngneers/controls/utils-ng';\n\nexport type RovingOrientation = 'horizontal' | 'vertical';\nexport type RovingMode = 'tabindex' | 'activedescendant';\n\nexport interface RovingItemRef {\n readonly id: string;\n readonly element: HTMLElement;\n /**\n * Optional reactive disabled flag. When it reads `true`, the item is skipped\n * by keyboard navigation (`next`/`prev`/`first`/`last`) and pointer\n * `activate()`. Absent (undefined) is treated as enabled.\n */\n readonly disabled?: Signal<boolean>;\n}\n\nexport const ROVING_GROUP = new InjectionToken<NgnRovingGroup>('ROVING_GROUP');\n\n@Directive({\n selector: '[ngnRovingGroup]',\n providers: [{ provide: ROVING_GROUP, useExisting: NgnRovingGroup }],\n exportAs: 'ngnRovingGroup',\n})\nexport class NgnRovingGroup {\n /**\n * Which arrow keys move the active item: `'horizontal'` uses Left/Right,\n * `'vertical'` uses Up/Down. Home/End always jump to first/last.\n * @default 'horizontal'\n */\n public readonly orientation = input<RovingOrientation>('horizontal');\n /**\n * How the active item is exposed. `'tabindex'` moves the tab stop (and DOM\n * focus) between items; `'activedescendant'` keeps focus on the group and\n * points `aria-activedescendant` at the active item.\n * @default 'tabindex'\n */\n public readonly rovingMode = input<RovingMode>('tabindex');\n /**\n * Whether keyboard navigation wraps around the ends of the list instead of\n * stopping at the first/last item.\n * @default false\n */\n public readonly rovingWrap = input(false, { transform: booleanAttribute });\n\n /**\n * Emits the index of the newly active item whenever it changes via keyboard,\n * pointer, or {@link setActive}. Not emitted by {@link syncActiveIndex}.\n */\n public readonly activeItemChange = output<number>();\n\n private readonly _host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly _items = signal<RovingItemRef[]>([]);\n /** Items in DOM order. */\n public readonly items = computed(() =>\n [...this._items()].sort((a, b) => {\n if (a.element === b.element) return 0;\n return a.element.compareDocumentPosition(b.element) & Node.DOCUMENT_POSITION_FOLLOWING\n ? -1\n : 1;\n })\n );\n\n public readonly activeIndex = signal(0);\n\n /**\n * Tracks the previously active item's id so we can decide whether to call\n * `.focus()`. Initialized to `null` — the first effect pass sets it without\n * focusing; subsequent passes focus when the id actually changes.\n */\n private _prevActiveId: string | null = null;\n\n /**\n * When set, the next focus-mode effect pass updates the tab stop / attributes\n * but does NOT move DOM focus. Consumed (reset) by that pass. Lets consumers\n * sync the active item to a programmatic selection (e.g. a radio group's\n * `value` change) without stealing focus into the group.\n */\n private _suppressFocus = false;\n\n constructor() {\n domEventHandler(this._host, 'keydown', e => this._onKeydown(e));\n\n // Apply focus-mode side effects reactively.\n effect(() => {\n const items = this.items();\n const mode = this.rovingMode();\n\n // Clamp the active index LOCALLY (do not mutate the activeIndex signal)\n // so that attributes/focus always reference a valid item even if\n // activeIndex is stale after the items list shrinks.\n const active = items.length\n ? Math.max(0, Math.min(this.activeIndex(), items.length - 1))\n : this.activeIndex();\n\n if (mode === 'tabindex') {\n // Clean up activedescendant mode attributes.\n this._host.removeAttribute('aria-activedescendant');\n this._host.removeAttribute('aria-owns');\n\n // Apply tabindex to all items.\n items.forEach((it, i) => {\n it.element.setAttribute('tabindex', i === active ? '0' : '-1');\n });\n\n // Move focus when the active item changes — but NOT on the first pass\n // (where _prevActiveId is null) to avoid stealing focus on mount.\n const activeEl = items[active]?.element;\n const activeId = activeEl?.id ?? null;\n if (\n activeEl &&\n this._prevActiveId !== null &&\n this._prevActiveId !== activeId &&\n !this._suppressFocus\n ) {\n activeEl.focus();\n }\n this._prevActiveId = activeId;\n this._suppressFocus = false;\n } else {\n // activedescendant mode: remove tabindex from all items.\n items.forEach(it => it.element.removeAttribute('tabindex'));\n\n // Guard the empty-items window (items register during effects, so the\n // first effect pass may see an empty list).\n if (!items.length) {\n this._host.removeAttribute('aria-activedescendant');\n this._host.removeAttribute('aria-owns');\n return;\n }\n\n this._host.setAttribute('aria-owns', items.map(i => i.id).join(' '));\n const activeId = items[active]?.id;\n if (activeId) {\n this._host.setAttribute('aria-activedescendant', activeId);\n }\n\n // Reset the focus guard when in activedescendant mode so that switching\n // back to tabindex mode doesn't immediately call focus.\n this._prevActiveId = null;\n }\n });\n }\n\n public register(item: RovingItemRef): void {\n this._items.update(list => (list.includes(item) ? list : [...list, item]));\n }\n\n public unregister(item: RovingItemRef): void {\n this._items.update(list => list.filter(i => i !== item));\n // Normalize the stored active index so it never dangles past the end —\n // otherwise NgnRovingItem.isActive() would be false for every item.\n const n = this.items().length;\n if (this.activeIndex() >= n) {\n this.activeIndex.set(Math.max(0, n - 1));\n }\n }\n\n public activate(target: RovingItemRef): void {\n if (target.disabled?.()) return;\n const idx = this.items().indexOf(target);\n if (idx >= 0) this._setActive(idx);\n }\n\n public next(): void {\n this._move(1);\n }\n\n public prev(): void {\n this._move(-1);\n }\n\n public first(): void {\n const items = this.items();\n const idx = items.findIndex(it => !it.disabled?.());\n if (idx >= 0) this._setActive(idx);\n }\n\n public last(): void {\n const items = this.items();\n for (let i = items.length - 1; i >= 0; i--) {\n if (!items[i]?.disabled?.()) {\n this._setActive(i);\n return;\n }\n }\n }\n\n /** Set the active item by index (clamped to range); emits activeItemChange. */\n public setActive(index: number): void {\n if (index >= 0 && index < this.items().length) this._setActive(index);\n }\n\n /**\n * Move the tab stop to `index` WITHOUT moving DOM focus or emitting\n * `activeItemChange`. For syncing the roving state to a programmatic\n * selection (e.g. a radio group's `value` changing from code) where stealing\n * focus into the group would be wrong.\n */\n public syncActiveIndex(index: number): void {\n if (index < 0 || index >= this.items().length) return;\n if (index === this.activeIndex()) return;\n this._suppressFocus = true;\n this.activeIndex.set(index);\n }\n\n private _onKeydown(e: KeyboardEvent): void {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const horizontal = this.orientation() === 'horizontal';\n let consumed = true;\n switch (e.key) {\n case 'ArrowRight':\n if (horizontal) this.next();\n else consumed = false;\n break;\n case 'ArrowLeft':\n if (horizontal) this.prev();\n else consumed = false;\n break;\n case 'ArrowDown':\n if (!horizontal) this.next();\n else consumed = false;\n break;\n case 'ArrowUp':\n if (!horizontal) this.prev();\n else consumed = false;\n break;\n case 'Home':\n this.first();\n break;\n case 'End':\n this.last();\n break;\n default:\n consumed = false;\n }\n if (consumed) e.preventDefault();\n }\n\n private _move(delta: number): void {\n const items = this.items();\n const n = items.length;\n if (!n) return;\n const start = this.activeIndex();\n let i = start;\n // Step in `delta` direction until we land on an enabled item, skipping\n // disabled ones. When no enabled target exists in that direction we clamp\n // to the current index and still emit — preserving the pre-existing\n // clamp-and-emit contract. Bounded by `n` iterations so it terminates.\n for (let step = 0; step < n; step++) {\n i += delta;\n if (this.rovingWrap()) {\n i = ((i % n) + n) % n;\n if (i === start) break; // looped back — no other enabled item\n } else if (i < 0 || i >= n) {\n break; // reached an edge without finding an enabled item\n }\n if (!items[i]?.disabled?.()) {\n this._setActive(i);\n return;\n }\n }\n this._setActive(start);\n }\n\n private _setActive(i: number): void {\n this.activeIndex.set(i);\n this.activeItemChange.emit(i);\n }\n}\n\n@Directive({ selector: '[ngnRovingItem]', exportAs: 'ngnRovingItem' })\nexport class NgnRovingItem implements RovingItemRef {\n private readonly _injectedGroup = inject(ROVING_GROUP, { optional: true });\n /**\n * The {@link NgnRovingGroup} this item belongs to. Pass a group reference when\n * the item is not a DOM descendant of its group; leave empty/undefined to use\n * the nearest ancestor group via dependency injection.\n */\n public readonly ngnRovingItem = input<NgnRovingGroup | '' | undefined>(undefined);\n\n public readonly element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n public readonly id: string;\n\n /**\n * Reactive disabled flag for this item. Consumers (e.g. `ngn-radio`) set it\n * so the group's keyboard navigation and pointer `activate()` skip the item.\n */\n public readonly disabled = signal(false);\n\n private readonly _group = computed<NgnRovingGroup>(() => {\n const explicit = this.ngnRovingItem();\n const group = explicit instanceof NgnRovingGroup ? explicit : this._injectedGroup;\n if (!group) {\n throw new Error(\n 'ngnRovingItem: no NgnRovingGroup found. Provide via [ngnRovingItem]=\"group\" or nest inside one.'\n );\n }\n return group;\n });\n\n /** Whether this item is currently the active item in its group. */\n public readonly isActive = computed(() => {\n const g = this._group();\n return g.items()[g.activeIndex()] === this;\n });\n\n constructor() {\n // generateElementId() must be called in injection context (constructor is fine).\n this.id = this.element.id || generateElementId();\n if (!this.element.id) this.element.id = this.id;\n\n // Use effect() rather than afterNextRender() so that registration is\n // guaranteed to run synchronously during detectChanges() in Vitest/TestBed.\n // effect() reads this._group() reactively and (re)registers whenever the\n // resolved group changes; the onCleanup callback unregisters from the\n // previous group before re-running, and on destroy. Equivalent to\n // afterNextRender() in a browser environment.\n effect(onCleanup => {\n const group = this._group();\n group.register(this);\n onCleanup(() => group.unregister(this));\n });\n\n // Activate self in the group on pointerdown.\n domEventHandler(this.element, 'pointerdown', () => this._group().activate(this));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, ElementRef, Injector, input, afterNextRender, Directive } from '@angular/core';
|
|
3
|
+
import { scrollShadowDirectiveTemplate } from '@ngneers/controls-themes/templates/api';
|
|
4
|
+
import { injectThemeTemplate, abortSignalOnDestroy } from '@ngneers/controls/api/ng';
|
|
5
|
+
import { toggleClass } from '@ngneers/controls/utils';
|
|
6
|
+
|
|
7
|
+
class NgnScrollShadow {
|
|
8
|
+
theme = injectThemeTemplate(scrollShadowDirectiveTemplate);
|
|
9
|
+
_el = inject(ElementRef);
|
|
10
|
+
_injector = inject(Injector);
|
|
11
|
+
/**
|
|
12
|
+
* Which scroll axis to track and add shadow classes for.
|
|
13
|
+
* @default 'horizontal'
|
|
14
|
+
*/
|
|
15
|
+
ngnScrollShadow = input('horizontal', /* @ts-ignore */
|
|
16
|
+
...(ngDevMode ? [{ debugName: "ngnScrollShadow" }] : /* istanbul ignore next */ []));
|
|
17
|
+
/**
|
|
18
|
+
* Element that receives the shadow classes. Defaults to the host element (which
|
|
19
|
+
* is also the scroll container being observed).
|
|
20
|
+
* @default undefined
|
|
21
|
+
*/
|
|
22
|
+
scrollShadowTarget = input(undefined, /* @ts-ignore */
|
|
23
|
+
...(ngDevMode ? [{ debugName: "scrollShadowTarget" }] : /* istanbul ignore next */ []));
|
|
24
|
+
constructor() {
|
|
25
|
+
afterNextRender(() => {
|
|
26
|
+
const scrollEl = this._el.nativeElement;
|
|
27
|
+
const abort = abortSignalOnDestroy({ injector: this._injector });
|
|
28
|
+
const update = () => {
|
|
29
|
+
const target = this.scrollShadowTarget() ?? scrollEl;
|
|
30
|
+
const dir = this.ngnScrollShadow();
|
|
31
|
+
if (dir === 'horizontal' || dir === 'both') {
|
|
32
|
+
const sl = scrollEl.scrollLeft;
|
|
33
|
+
const maxH = scrollEl.scrollWidth - scrollEl.clientWidth;
|
|
34
|
+
toggleClass(target, this.theme.class('scrolled-start'), sl > 0);
|
|
35
|
+
toggleClass(target, this.theme.class('scrolled-end'), maxH > 0 && sl < maxH - 1);
|
|
36
|
+
}
|
|
37
|
+
if (dir === 'vertical' || dir === 'both') {
|
|
38
|
+
const st = scrollEl.scrollTop;
|
|
39
|
+
const maxV = scrollEl.scrollHeight - scrollEl.clientHeight;
|
|
40
|
+
toggleClass(target, this.theme.class('scrolled-top'), st > 0);
|
|
41
|
+
toggleClass(target, this.theme.class('scrolled-bottom'), maxV > 0 && st < maxV - 1);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const ro = new ResizeObserver(update);
|
|
45
|
+
ro.observe(scrollEl);
|
|
46
|
+
abort.addEventListener('abort', () => ro.disconnect());
|
|
47
|
+
scrollEl.addEventListener('scroll', update, { passive: true, signal: abort });
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnScrollShadow, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
51
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: NgnScrollShadow, isStandalone: true, selector: "[ngnScrollShadow]", inputs: { ngnScrollShadow: { classPropertyName: "ngnScrollShadow", publicName: "ngnScrollShadow", isSignal: true, isRequired: false, transformFunction: null }, scrollShadowTarget: { classPropertyName: "scrollShadowTarget", publicName: "scrollShadowTarget", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
52
|
+
}
|
|
53
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnScrollShadow, decorators: [{
|
|
54
|
+
type: Directive,
|
|
55
|
+
args: [{
|
|
56
|
+
selector: '[ngnScrollShadow]',
|
|
57
|
+
}]
|
|
58
|
+
}], ctorParameters: () => [], propDecorators: { ngnScrollShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnScrollShadow", required: false }] }], scrollShadowTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollShadowTarget", required: false }] }] } });
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generated bundle index. Do not edit.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
export { NgnScrollShadow };
|
|
65
|
+
//# sourceMappingURL=ngneers-controls-scroll-shadow.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngneers-controls-scroll-shadow.mjs","sources":["../../src/scroll-shadow/scroll-shadow.ts","../../src/scroll-shadow/ngneers-controls-scroll-shadow.ts"],"sourcesContent":["import { afterNextRender, Directive, ElementRef, inject, Injector, input } from '@angular/core';\nimport { scrollShadowDirectiveTemplate } from '@ngneers/controls-themes/templates/api';\n\nimport { abortSignalOnDestroy, injectThemeTemplate } from '@ngneers/controls/api/ng';\nimport { toggleClass } from '@ngneers/controls/utils';\n\n@Directive({\n selector: '[ngnScrollShadow]',\n})\nexport class NgnScrollShadow {\n protected readonly theme = injectThemeTemplate(scrollShadowDirectiveTemplate);\n private readonly _el = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly _injector = inject(Injector);\n\n /**\n * Which scroll axis to track and add shadow classes for.\n * @default 'horizontal'\n */\n public readonly ngnScrollShadow = input<'horizontal' | 'vertical' | 'both'>('horizontal');\n\n /**\n * Element that receives the shadow classes. Defaults to the host element (which\n * is also the scroll container being observed).\n * @default undefined\n */\n public readonly scrollShadowTarget = input<HTMLElement | undefined>(undefined);\n\n constructor() {\n afterNextRender(() => {\n const scrollEl = this._el.nativeElement;\n const abort = abortSignalOnDestroy({ injector: this._injector });\n\n const update = () => {\n const target = this.scrollShadowTarget() ?? scrollEl;\n const dir = this.ngnScrollShadow();\n\n if (dir === 'horizontal' || dir === 'both') {\n const sl = scrollEl.scrollLeft;\n const maxH = scrollEl.scrollWidth - scrollEl.clientWidth;\n toggleClass(target, this.theme.class('scrolled-start'), sl > 0);\n toggleClass(target, this.theme.class('scrolled-end'), maxH > 0 && sl < maxH - 1);\n }\n\n if (dir === 'vertical' || dir === 'both') {\n const st = scrollEl.scrollTop;\n const maxV = scrollEl.scrollHeight - scrollEl.clientHeight;\n toggleClass(target, this.theme.class('scrolled-top'), st > 0);\n toggleClass(target, this.theme.class('scrolled-bottom'), maxV > 0 && st < maxV - 1);\n }\n };\n\n const ro = new ResizeObserver(update);\n ro.observe(scrollEl);\n abort.addEventListener('abort', () => ro.disconnect());\n scrollEl.addEventListener('scroll', update, { passive: true, signal: abort });\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|