@tailng-ui/primitives 0.14.0 → 0.15.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/package.json +3 -2
- package/src/index.d.ts +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/form/datepicker/__tests__/tng-datepicker.test-helpers.d.ts +25 -0
- package/src/lib/form/datepicker/__tests__/tng-datepicker.test-helpers.d.ts.map +1 -0
- package/src/lib/form/datepicker/__tests__/tng-datepicker.test-helpers.js +105 -0
- package/src/lib/form/datepicker/__tests__/tng-datepicker.test-helpers.js.map +1 -0
- package/src/lib/form/datepicker/datepicker.adapters.d.ts +5 -0
- package/src/lib/form/datepicker/datepicker.adapters.d.ts.map +1 -0
- package/src/lib/form/datepicker/datepicker.adapters.js +194 -0
- package/src/lib/form/datepicker/datepicker.adapters.js.map +1 -0
- package/src/lib/form/datepicker/datepicker.state.d.ts +10 -0
- package/src/lib/form/datepicker/datepicker.state.d.ts.map +1 -0
- package/src/lib/form/datepicker/datepicker.state.js +97 -0
- package/src/lib/form/datepicker/datepicker.state.js.map +1 -0
- package/src/lib/form/datepicker/datepicker.types.d.ts +269 -0
- package/src/lib/form/datepicker/datepicker.types.d.ts.map +1 -0
- package/src/lib/form/datepicker/datepicker.types.js +1 -0
- package/src/lib/form/datepicker/datepicker.types.js.map +1 -0
- package/src/lib/form/datepicker/datepicker.utils.d.ts +75 -0
- package/src/lib/form/datepicker/datepicker.utils.d.ts.map +1 -0
- package/src/lib/form/datepicker/datepicker.utils.js +238 -0
- package/src/lib/form/datepicker/datepicker.utils.js.map +1 -0
- package/src/lib/form/datepicker/index.d.ts +3 -0
- package/src/lib/form/datepicker/index.d.ts.map +1 -0
- package/src/lib/form/datepicker/index.js +3 -0
- package/src/lib/form/datepicker/index.js.map +1 -0
- package/src/lib/form/datepicker/tng-datepicker.d.ts +5 -0
- package/src/lib/form/datepicker/tng-datepicker.d.ts.map +1 -0
- package/src/lib/form/datepicker/tng-datepicker.js +1948 -0
- package/src/lib/form/datepicker/tng-datepicker.js.map +1 -0
- package/src/lib/form/datepicker/tng-datepicker.overlay.d.ts +60 -0
- package/src/lib/form/datepicker/tng-datepicker.overlay.d.ts.map +1 -0
- package/src/lib/form/datepicker/tng-datepicker.overlay.js +275 -0
- package/src/lib/form/datepicker/tng-datepicker.overlay.js.map +1 -0
- package/src/lib/layout/grid/__tests__/tng-grid.test-harness.d.ts +32 -0
- package/src/lib/layout/grid/__tests__/tng-grid.test-harness.d.ts.map +1 -0
- package/src/lib/layout/grid/__tests__/tng-grid.test-harness.js +312 -0
- package/src/lib/layout/grid/__tests__/tng-grid.test-harness.js.map +1 -0
- package/src/lib/layout/grid/tng-grid.d.ts +144 -2
- package/src/lib/layout/grid/tng-grid.d.ts.map +1 -1
- package/src/lib/layout/grid/tng-grid.js +669 -2
- package/src/lib/layout/grid/tng-grid.js.map +1 -1
- package/src/lib/layout/tree/__tests__/tng-tree.test-harness.d.ts.map +1 -1
- package/src/lib/layout/tree/__tests__/tng-tree.test-harness.js +0 -1
- package/src/lib/layout/tree/__tests__/tng-tree.test-harness.js.map +1 -1
- package/src/lib/navigation/tabs/tng-tabs.d.ts +13 -6
- package/src/lib/navigation/tabs/tng-tabs.d.ts.map +1 -1
- package/src/lib/navigation/tabs/tng-tabs.js +77 -35
- package/src/lib/navigation/tabs/tng-tabs.js.map +1 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { DestroyRef, Directive, ElementRef, HostBinding, effect, inject, input, signal, } from '@angular/core';
|
|
2
|
+
import { computeOverlayPosition, } from '@tailng-ui/cdk';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
const OVERLAY_VIEWPORT_MARGIN = 12;
|
|
5
|
+
const OVERLAY_OFFSET = 9;
|
|
6
|
+
function rectFromClientRect(rect) {
|
|
7
|
+
return {
|
|
8
|
+
height: rect.height,
|
|
9
|
+
left: rect.left,
|
|
10
|
+
top: rect.top,
|
|
11
|
+
width: rect.width,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function viewportRect(windowRef) {
|
|
15
|
+
return {
|
|
16
|
+
height: windowRef.innerHeight || 768,
|
|
17
|
+
left: 0,
|
|
18
|
+
top: 0,
|
|
19
|
+
width: windowRef.innerWidth || 1024,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function resolveAnchorElement(anchor) {
|
|
23
|
+
if (anchor instanceof ElementRef) {
|
|
24
|
+
return anchor.nativeElement;
|
|
25
|
+
}
|
|
26
|
+
return anchor instanceof HTMLElement ? anchor : null;
|
|
27
|
+
}
|
|
28
|
+
export class TngDatepickerOverlay {
|
|
29
|
+
elRef = inject((ElementRef));
|
|
30
|
+
destroyRef = inject(DestroyRef);
|
|
31
|
+
ownerDocument = this.elRef.nativeElement.ownerDocument ?? null;
|
|
32
|
+
ownerWindow = this.ownerDocument?.defaultView ?? null;
|
|
33
|
+
renderVersion = signal(0, ...(ngDevMode ? [{ debugName: "renderVersion" }] : []));
|
|
34
|
+
resolvedPlacement = signal('bottom', ...(ngDevMode ? [{ debugName: "resolvedPlacement" }] : []));
|
|
35
|
+
overlayPlaceholder = null;
|
|
36
|
+
overlayOriginalParent = null;
|
|
37
|
+
overlayLayoutFrame = null;
|
|
38
|
+
removeResizeListener = null;
|
|
39
|
+
removeScrollListener = null;
|
|
40
|
+
resizeObserver = null;
|
|
41
|
+
controller = input.required({ ...(ngDevMode ? { debugName: "controller" } : {}), alias: 'tngDatepickerOverlay' });
|
|
42
|
+
anchor = input(undefined, { ...(ngDevMode ? { debugName: "anchor" } : {}), alias: 'tngDatepickerOverlayAnchor' });
|
|
43
|
+
placement = input(undefined, { ...(ngDevMode ? { debugName: "placement" } : {}), alias: 'tngDatepickerOverlayPlacement' });
|
|
44
|
+
offset = input(undefined, { ...(ngDevMode ? { debugName: "offset" } : {}), alias: 'tngDatepickerOverlayOffset' });
|
|
45
|
+
collision = input(undefined, { ...(ngDevMode ? { debugName: "collision" } : {}), alias: 'tngDatepickerOverlayCollision' });
|
|
46
|
+
get hidden() {
|
|
47
|
+
this.renderVersion();
|
|
48
|
+
return this.controller().getOutputs().open ? null : '';
|
|
49
|
+
}
|
|
50
|
+
get display() {
|
|
51
|
+
this.renderVersion();
|
|
52
|
+
return this.controller().getOutputs().open ? null : 'none';
|
|
53
|
+
}
|
|
54
|
+
get dataPlacement() {
|
|
55
|
+
this.renderVersion();
|
|
56
|
+
return this.resolvedPlacement();
|
|
57
|
+
}
|
|
58
|
+
constructor() {
|
|
59
|
+
this.initializeOverlayPortal();
|
|
60
|
+
effect((onCleanup) => {
|
|
61
|
+
const controller = this.controller();
|
|
62
|
+
controller.registerOverlay(this.elRef.nativeElement);
|
|
63
|
+
const unsubscribe = controller.subscribe(() => {
|
|
64
|
+
this.renderVersion.update((value) => value + 1);
|
|
65
|
+
});
|
|
66
|
+
onCleanup(() => {
|
|
67
|
+
unsubscribe();
|
|
68
|
+
controller.registerOverlay(null);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
effect(() => {
|
|
72
|
+
const open = this.controller().getOutputs().open;
|
|
73
|
+
this.renderVersion();
|
|
74
|
+
this.placement();
|
|
75
|
+
this.offset();
|
|
76
|
+
this.collision();
|
|
77
|
+
this.anchor();
|
|
78
|
+
if (open) {
|
|
79
|
+
this.mountToBodyAndPosition();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.restoreToPlaceholder();
|
|
83
|
+
});
|
|
84
|
+
this.destroyRef.onDestroy(() => {
|
|
85
|
+
if (this.overlayLayoutFrame !== null && this.ownerWindow !== null) {
|
|
86
|
+
this.ownerWindow.cancelAnimationFrame(this.overlayLayoutFrame);
|
|
87
|
+
this.overlayLayoutFrame = null;
|
|
88
|
+
}
|
|
89
|
+
this.teardownRepositionListeners();
|
|
90
|
+
this.restoreToPlaceholder(true);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
initializeOverlayPortal() {
|
|
94
|
+
if (this.overlayPlaceholder !== null) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const placeholderDocument = this.ownerDocument ?? document;
|
|
98
|
+
const overlay = this.elRef.nativeElement;
|
|
99
|
+
this.overlayPlaceholder = placeholderDocument.createComment('tng-datepicker-overlay-anchor');
|
|
100
|
+
this.overlayOriginalParent = overlay.parentNode;
|
|
101
|
+
const placeholder = this.overlayPlaceholder;
|
|
102
|
+
if (this.overlayOriginalParent !== null && placeholder !== null) {
|
|
103
|
+
this.overlayOriginalParent.insertBefore(placeholder, overlay);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
findAnchorEl() {
|
|
107
|
+
const explicitAnchor = resolveAnchorElement(this.anchor());
|
|
108
|
+
if (explicitAnchor !== null) {
|
|
109
|
+
return explicitAnchor;
|
|
110
|
+
}
|
|
111
|
+
const scope = this.overlayPlaceholder?.parentNode instanceof HTMLElement
|
|
112
|
+
? this.overlayPlaceholder.parentNode
|
|
113
|
+
: this.overlayOriginalParent instanceof HTMLElement
|
|
114
|
+
? this.overlayOriginalParent
|
|
115
|
+
: null;
|
|
116
|
+
return (scope?.querySelector('[data-slot="datepicker-input-shell"]') ??
|
|
117
|
+
scope?.querySelector('[data-slot="datepicker-trigger"]'));
|
|
118
|
+
}
|
|
119
|
+
scheduleReposition() {
|
|
120
|
+
if (!this.controller().getOutputs().open || this.ownerWindow === null) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (this.overlayLayoutFrame !== null) {
|
|
124
|
+
this.ownerWindow.cancelAnimationFrame(this.overlayLayoutFrame);
|
|
125
|
+
}
|
|
126
|
+
this.overlayLayoutFrame = this.ownerWindow.requestAnimationFrame(() => {
|
|
127
|
+
this.overlayLayoutFrame = null;
|
|
128
|
+
this.positionOverlay();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
positionOverlay() {
|
|
132
|
+
const overlay = this.elRef.nativeElement;
|
|
133
|
+
const anchor = this.findAnchorEl();
|
|
134
|
+
if (anchor === null || this.ownerWindow === null) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const anchorRect = rectFromClientRect(anchor.getBoundingClientRect());
|
|
138
|
+
const viewport = viewportRect(this.ownerWindow);
|
|
139
|
+
const width = Math.max(0, Math.min(anchorRect.width, viewport.width - OVERLAY_VIEWPORT_MARGIN * 2));
|
|
140
|
+
overlay.style.width = `${width}px`;
|
|
141
|
+
overlay.style.maxWidth = `${Math.max(0, viewport.width - OVERLAY_VIEWPORT_MARGIN * 2)}px`;
|
|
142
|
+
overlay.style.maxHeight = '';
|
|
143
|
+
const overlayRect = rectFromClientRect(overlay.getBoundingClientRect());
|
|
144
|
+
const result = computeOverlayPosition({
|
|
145
|
+
anchorRect,
|
|
146
|
+
collision: this.resolveCollision(),
|
|
147
|
+
direction: this.resolveDirection(),
|
|
148
|
+
offset: this.resolveOffset(),
|
|
149
|
+
overlayRect,
|
|
150
|
+
placement: this.resolvePlacement(),
|
|
151
|
+
viewportRect: viewport,
|
|
152
|
+
});
|
|
153
|
+
overlay.style.left = `${result.x}px`;
|
|
154
|
+
overlay.style.top = `${result.y}px`;
|
|
155
|
+
const anchorBottom = anchorRect.top + anchorRect.height;
|
|
156
|
+
const availableHeight = result.side === 'top'
|
|
157
|
+
? Math.max(0, Math.floor(anchorRect.top - OVERLAY_VIEWPORT_MARGIN - OVERLAY_OFFSET))
|
|
158
|
+
: Math.max(0, Math.floor(viewport.height - anchorBottom - OVERLAY_VIEWPORT_MARGIN - OVERLAY_OFFSET));
|
|
159
|
+
if (availableHeight > 0) {
|
|
160
|
+
overlay.style.maxHeight = `${availableHeight}px`;
|
|
161
|
+
}
|
|
162
|
+
this.resolvedPlacement.set(result.side === 'top' ? 'top' : 'bottom');
|
|
163
|
+
overlay.style.visibility = '';
|
|
164
|
+
}
|
|
165
|
+
setupRepositionListeners() {
|
|
166
|
+
if (this.ownerWindow === null || this.removeResizeListener !== null || this.removeScrollListener !== null) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const schedule = () => {
|
|
170
|
+
this.scheduleReposition();
|
|
171
|
+
};
|
|
172
|
+
this.ownerWindow.addEventListener('resize', schedule);
|
|
173
|
+
this.ownerWindow.addEventListener('scroll', schedule, true);
|
|
174
|
+
this.removeResizeListener = () => this.ownerWindow?.removeEventListener('resize', schedule);
|
|
175
|
+
this.removeScrollListener = () => this.ownerWindow?.removeEventListener('scroll', schedule, true);
|
|
176
|
+
if ('ResizeObserver' in this.ownerWindow) {
|
|
177
|
+
const ResizeObserverCtor = this.ownerWindow.ResizeObserver;
|
|
178
|
+
this.resizeObserver = new ResizeObserverCtor(() => {
|
|
179
|
+
this.scheduleReposition();
|
|
180
|
+
});
|
|
181
|
+
const anchor = this.findAnchorEl();
|
|
182
|
+
if (anchor !== null && this.resizeObserver !== null) {
|
|
183
|
+
this.resizeObserver.observe(anchor);
|
|
184
|
+
}
|
|
185
|
+
this.resizeObserver?.observe(this.elRef.nativeElement);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
teardownRepositionListeners() {
|
|
189
|
+
this.removeResizeListener?.();
|
|
190
|
+
this.removeScrollListener?.();
|
|
191
|
+
this.removeResizeListener = null;
|
|
192
|
+
this.removeScrollListener = null;
|
|
193
|
+
this.resizeObserver?.disconnect();
|
|
194
|
+
this.resizeObserver = null;
|
|
195
|
+
}
|
|
196
|
+
mountToBodyAndPosition() {
|
|
197
|
+
const overlay = this.elRef.nativeElement;
|
|
198
|
+
if (this.ownerDocument === null) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
this.setupRepositionListeners();
|
|
202
|
+
if (overlay.parentNode !== this.ownerDocument.body) {
|
|
203
|
+
this.ownerDocument.body.appendChild(overlay);
|
|
204
|
+
}
|
|
205
|
+
overlay.style.position = 'fixed';
|
|
206
|
+
overlay.style.left = '0px';
|
|
207
|
+
overlay.style.top = '0px';
|
|
208
|
+
overlay.style.visibility = 'hidden';
|
|
209
|
+
overlay.style.zIndex = '1000';
|
|
210
|
+
queueMicrotask(() => {
|
|
211
|
+
if (!this.controller().getOutputs().open) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.positionOverlay();
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
restoreToPlaceholder(force = false) {
|
|
218
|
+
const overlay = this.elRef.nativeElement;
|
|
219
|
+
if (!force && overlay.parentNode !== this.ownerDocument?.body) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const placeholder = this.overlayPlaceholder;
|
|
223
|
+
if (placeholder?.parentNode !== null && placeholder !== null) {
|
|
224
|
+
placeholder.parentNode.insertBefore(overlay, placeholder);
|
|
225
|
+
}
|
|
226
|
+
else if (this.overlayOriginalParent !== null) {
|
|
227
|
+
this.overlayOriginalParent.appendChild(overlay);
|
|
228
|
+
}
|
|
229
|
+
this.teardownRepositionListeners();
|
|
230
|
+
this.resolvedPlacement.set(this.resolvePlacement().side === 'top' ? 'top' : 'bottom');
|
|
231
|
+
overlay.style.left = '';
|
|
232
|
+
overlay.style.maxHeight = '';
|
|
233
|
+
overlay.style.maxWidth = '';
|
|
234
|
+
overlay.style.position = '';
|
|
235
|
+
overlay.style.top = '';
|
|
236
|
+
overlay.style.visibility = '';
|
|
237
|
+
overlay.style.width = '';
|
|
238
|
+
overlay.style.zIndex = '';
|
|
239
|
+
}
|
|
240
|
+
resolvePlacement() {
|
|
241
|
+
return this.placement() ?? { align: 'start', side: 'bottom' };
|
|
242
|
+
}
|
|
243
|
+
resolveOffset() {
|
|
244
|
+
return this.offset() ?? { side: OVERLAY_OFFSET };
|
|
245
|
+
}
|
|
246
|
+
resolveCollision() {
|
|
247
|
+
return this.collision() ?? {
|
|
248
|
+
flip: true,
|
|
249
|
+
padding: OVERLAY_VIEWPORT_MARGIN,
|
|
250
|
+
shift: true,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
resolveDirection() {
|
|
254
|
+
return this.controller().getOutputs().getHostAttributes()['dir'] === 'rtl' ? 'rtl' : 'ltr';
|
|
255
|
+
}
|
|
256
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TngDatepickerOverlay, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
257
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: TngDatepickerOverlay, isStandalone: true, selector: "[tngDatepickerOverlay]", inputs: { controller: { classPropertyName: "controller", publicName: "tngDatepickerOverlay", isSignal: true, isRequired: true, transformFunction: null }, anchor: { classPropertyName: "anchor", publicName: "tngDatepickerOverlayAnchor", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "tngDatepickerOverlayPlacement", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "tngDatepickerOverlayOffset", isSignal: true, isRequired: false, transformFunction: null }, collision: { classPropertyName: "collision", publicName: "tngDatepickerOverlayCollision", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.hidden": "this.hidden", "style.display": "this.display", "attr.data-placement": "this.dataPlacement" } }, exportAs: ["tngDatepickerOverlay"], ngImport: i0 });
|
|
258
|
+
}
|
|
259
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TngDatepickerOverlay, decorators: [{
|
|
260
|
+
type: Directive,
|
|
261
|
+
args: [{
|
|
262
|
+
selector: '[tngDatepickerOverlay]',
|
|
263
|
+
exportAs: 'tngDatepickerOverlay',
|
|
264
|
+
}]
|
|
265
|
+
}], ctorParameters: () => [], propDecorators: { controller: [{ type: i0.Input, args: [{ isSignal: true, alias: "tngDatepickerOverlay", required: true }] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "tngDatepickerOverlayAnchor", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "tngDatepickerOverlayPlacement", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "tngDatepickerOverlayOffset", required: false }] }], collision: [{ type: i0.Input, args: [{ isSignal: true, alias: "tngDatepickerOverlayCollision", required: false }] }], hidden: [{
|
|
266
|
+
type: HostBinding,
|
|
267
|
+
args: ['attr.hidden']
|
|
268
|
+
}], display: [{
|
|
269
|
+
type: HostBinding,
|
|
270
|
+
args: ['style.display']
|
|
271
|
+
}], dataPlacement: [{
|
|
272
|
+
type: HostBinding,
|
|
273
|
+
args: ['attr.data-placement']
|
|
274
|
+
}] } });
|
|
275
|
+
//# sourceMappingURL=tng-datepicker.overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tng-datepicker.overlay.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/datepicker/tng-datepicker.overlay.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DestroyRef,\n Directive,\n ElementRef,\n HostBinding,\n effect,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport {\n computeOverlayPosition,\n type TngOverlayCollisionOptions,\n type TngOverlayOffset,\n type TngOverlayPlacement,\n} from '@tailng-ui/cdk';\nimport type {\n TngDatepickerAttributeMap,\n} from './datepicker.types';\n\ntype TngDatepickerOverlayController = Readonly<{\n getOutputs: () => Readonly<{\n getHostAttributes: () => TngDatepickerAttributeMap;\n open: boolean;\n }>;\n registerOverlay: (element: HTMLElement | null) => void;\n subscribe: (listener: (event: unknown) => void) => () => void;\n}>;\n\ntype MaybeRect = Readonly<{\n height: number;\n left: number;\n top: number;\n width: number;\n}>;\n\ntype OverlayAnchorInput =\n | ElementRef<HTMLElement>\n | HTMLElement\n | null\n | undefined;\n\nconst OVERLAY_VIEWPORT_MARGIN = 12;\nconst OVERLAY_OFFSET = 9;\n\nfunction rectFromClientRect(rect: DOMRect | ClientRect): MaybeRect {\n return {\n height: rect.height,\n left: rect.left,\n top: rect.top,\n width: rect.width,\n };\n}\n\nfunction viewportRect(windowRef: Window): MaybeRect {\n return {\n height: windowRef.innerHeight || 768,\n left: 0,\n top: 0,\n width: windowRef.innerWidth || 1024,\n };\n}\n\nfunction resolveAnchorElement(anchor: OverlayAnchorInput): HTMLElement | null {\n if (anchor instanceof ElementRef) {\n return anchor.nativeElement;\n }\n\n return anchor instanceof HTMLElement ? anchor : null;\n}\n\n@Directive({\n selector: '[tngDatepickerOverlay]',\n exportAs: 'tngDatepickerOverlay',\n})\nexport class TngDatepickerOverlay {\n private readonly elRef = inject(ElementRef<HTMLElement>);\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly ownerDocument = this.elRef.nativeElement.ownerDocument ?? null;\n private readonly ownerWindow = this.ownerDocument?.defaultView ?? null;\n private readonly renderVersion = signal(0);\n private readonly resolvedPlacement = signal<'bottom' | 'top'>('bottom');\n\n private overlayPlaceholder: Comment | null = null;\n private overlayOriginalParent: Node | null = null;\n private overlayLayoutFrame: number | null = null;\n private removeResizeListener: (() => void) | null = null;\n private removeScrollListener: (() => void) | null = null;\n private resizeObserver: ResizeObserver | null = null;\n\n public readonly controller = input.required<TngDatepickerOverlayController>({\n alias: 'tngDatepickerOverlay',\n });\n public readonly anchor = input<OverlayAnchorInput>(undefined, {\n alias: 'tngDatepickerOverlayAnchor',\n });\n public readonly placement = input<TngOverlayPlacement | undefined>(undefined, {\n alias: 'tngDatepickerOverlayPlacement',\n });\n public readonly offset = input<TngOverlayOffset | undefined>(undefined, {\n alias: 'tngDatepickerOverlayOffset',\n });\n public readonly collision = input<TngOverlayCollisionOptions | undefined>(undefined, {\n alias: 'tngDatepickerOverlayCollision',\n });\n\n @HostBinding('attr.hidden')\n protected get hidden(): '' | null {\n this.renderVersion();\n return this.controller().getOutputs().open ? null : '';\n }\n\n @HostBinding('style.display')\n protected get display(): string | null {\n this.renderVersion();\n return this.controller().getOutputs().open ? null : 'none';\n }\n\n @HostBinding('attr.data-placement')\n protected get dataPlacement(): 'bottom' | 'top' {\n this.renderVersion();\n return this.resolvedPlacement();\n }\n\n public constructor() {\n this.initializeOverlayPortal();\n\n effect((onCleanup) => {\n const controller = this.controller();\n controller.registerOverlay(this.elRef.nativeElement);\n const unsubscribe = controller.subscribe(() => {\n this.renderVersion.update((value) => value + 1);\n });\n\n onCleanup(() => {\n unsubscribe();\n controller.registerOverlay(null);\n });\n });\n\n effect(() => {\n const open = this.controller().getOutputs().open;\n this.renderVersion();\n this.placement();\n this.offset();\n this.collision();\n this.anchor();\n\n if (open) {\n this.mountToBodyAndPosition();\n return;\n }\n\n this.restoreToPlaceholder();\n });\n\n this.destroyRef.onDestroy(() => {\n if (this.overlayLayoutFrame !== null && this.ownerWindow !== null) {\n this.ownerWindow.cancelAnimationFrame(this.overlayLayoutFrame);\n this.overlayLayoutFrame = null;\n }\n\n this.teardownRepositionListeners();\n this.restoreToPlaceholder(true);\n });\n }\n\n private initializeOverlayPortal(): void {\n if (this.overlayPlaceholder !== null) {\n return;\n }\n\n const placeholderDocument = this.ownerDocument ?? document;\n const overlay = this.elRef.nativeElement;\n this.overlayPlaceholder = placeholderDocument.createComment('tng-datepicker-overlay-anchor');\n this.overlayOriginalParent = overlay.parentNode;\n const placeholder = this.overlayPlaceholder;\n if (this.overlayOriginalParent !== null && placeholder !== null) {\n this.overlayOriginalParent.insertBefore(placeholder, overlay);\n }\n }\n\n private findAnchorEl(): HTMLElement | null {\n const explicitAnchor = resolveAnchorElement(this.anchor());\n if (explicitAnchor !== null) {\n return explicitAnchor;\n }\n\n const scope =\n this.overlayPlaceholder?.parentNode instanceof HTMLElement\n ? this.overlayPlaceholder.parentNode\n : this.overlayOriginalParent instanceof HTMLElement\n ? this.overlayOriginalParent\n : null;\n\n return (\n scope?.querySelector('[data-slot=\"datepicker-input-shell\"]') ??\n scope?.querySelector('[data-slot=\"datepicker-trigger\"]')\n ) as HTMLElement | null;\n }\n\n private scheduleReposition(): void {\n if (!this.controller().getOutputs().open || this.ownerWindow === null) {\n return;\n }\n\n if (this.overlayLayoutFrame !== null) {\n this.ownerWindow.cancelAnimationFrame(this.overlayLayoutFrame);\n }\n\n this.overlayLayoutFrame = this.ownerWindow.requestAnimationFrame(() => {\n this.overlayLayoutFrame = null;\n this.positionOverlay();\n });\n }\n\n private positionOverlay(): void {\n const overlay = this.elRef.nativeElement;\n const anchor = this.findAnchorEl();\n if (anchor === null || this.ownerWindow === null) {\n return;\n }\n\n const anchorRect = rectFromClientRect(anchor.getBoundingClientRect());\n const viewport = viewportRect(this.ownerWindow);\n const width = Math.max(\n 0,\n Math.min(anchorRect.width, viewport.width - OVERLAY_VIEWPORT_MARGIN * 2),\n );\n\n overlay.style.width = `${width}px`;\n overlay.style.maxWidth = `${Math.max(0, viewport.width - OVERLAY_VIEWPORT_MARGIN * 2)}px`;\n overlay.style.maxHeight = '';\n\n const overlayRect = rectFromClientRect(overlay.getBoundingClientRect());\n const result = computeOverlayPosition({\n anchorRect,\n collision: this.resolveCollision(),\n direction: this.resolveDirection(),\n offset: this.resolveOffset(),\n overlayRect,\n placement: this.resolvePlacement(),\n viewportRect: viewport,\n });\n\n overlay.style.left = `${result.x}px`;\n overlay.style.top = `${result.y}px`;\n\n const anchorBottom = anchorRect.top + anchorRect.height;\n const availableHeight =\n result.side === 'top'\n ? Math.max(0, Math.floor(anchorRect.top - OVERLAY_VIEWPORT_MARGIN - OVERLAY_OFFSET))\n : Math.max(\n 0,\n Math.floor(viewport.height - anchorBottom - OVERLAY_VIEWPORT_MARGIN - OVERLAY_OFFSET),\n );\n\n if (availableHeight > 0) {\n overlay.style.maxHeight = `${availableHeight}px`;\n }\n\n this.resolvedPlacement.set(result.side === 'top' ? 'top' : 'bottom');\n overlay.style.visibility = '';\n }\n\n private setupRepositionListeners(): void {\n if (this.ownerWindow === null || this.removeResizeListener !== null || this.removeScrollListener !== null) {\n return;\n }\n\n const schedule = (): void => {\n this.scheduleReposition();\n };\n\n this.ownerWindow.addEventListener('resize', schedule);\n this.ownerWindow.addEventListener('scroll', schedule, true);\n this.removeResizeListener = () => this.ownerWindow?.removeEventListener('resize', schedule);\n this.removeScrollListener = () => this.ownerWindow?.removeEventListener('scroll', schedule, true);\n\n if ('ResizeObserver' in this.ownerWindow) {\n const ResizeObserverCtor = this.ownerWindow.ResizeObserver;\n this.resizeObserver = new ResizeObserverCtor(() => {\n this.scheduleReposition();\n });\n\n const anchor = this.findAnchorEl();\n if (anchor !== null && this.resizeObserver !== null) {\n this.resizeObserver.observe(anchor);\n }\n\n this.resizeObserver?.observe(this.elRef.nativeElement);\n }\n }\n\n private teardownRepositionListeners(): void {\n this.removeResizeListener?.();\n this.removeScrollListener?.();\n this.removeResizeListener = null;\n this.removeScrollListener = null;\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n }\n\n private mountToBodyAndPosition(): void {\n const overlay = this.elRef.nativeElement;\n if (this.ownerDocument === null) {\n return;\n }\n\n this.setupRepositionListeners();\n\n if (overlay.parentNode !== this.ownerDocument.body) {\n this.ownerDocument.body.appendChild(overlay);\n }\n\n overlay.style.position = 'fixed';\n overlay.style.left = '0px';\n overlay.style.top = '0px';\n overlay.style.visibility = 'hidden';\n overlay.style.zIndex = '1000';\n\n queueMicrotask(() => {\n if (!this.controller().getOutputs().open) {\n return;\n }\n\n this.positionOverlay();\n });\n }\n\n private restoreToPlaceholder(force = false): void {\n const overlay = this.elRef.nativeElement;\n if (!force && overlay.parentNode !== this.ownerDocument?.body) {\n return;\n }\n\n const placeholder = this.overlayPlaceholder;\n if (placeholder?.parentNode !== null && placeholder !== null) {\n placeholder.parentNode.insertBefore(overlay, placeholder);\n } else if (this.overlayOriginalParent !== null) {\n this.overlayOriginalParent.appendChild(overlay);\n }\n\n this.teardownRepositionListeners();\n this.resolvedPlacement.set(this.resolvePlacement().side === 'top' ? 'top' : 'bottom');\n overlay.style.left = '';\n overlay.style.maxHeight = '';\n overlay.style.maxWidth = '';\n overlay.style.position = '';\n overlay.style.top = '';\n overlay.style.visibility = '';\n overlay.style.width = '';\n overlay.style.zIndex = '';\n }\n\n private resolvePlacement(): TngOverlayPlacement {\n return this.placement() ?? { align: 'start', side: 'bottom' };\n }\n\n private resolveOffset(): TngOverlayOffset {\n return this.offset() ?? { side: OVERLAY_OFFSET };\n }\n\n private resolveCollision(): TngOverlayCollisionOptions {\n return this.collision() ?? {\n flip: true,\n padding: OVERLAY_VIEWPORT_MARGIN,\n shift: true,\n };\n }\n\n private resolveDirection(): 'ltr' | 'rtl' {\n return this.controller().getOutputs().getHostAttributes()['dir'] === 'rtl' ? 'rtl' : 'ltr';\n }\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ComponentFixture } from '@angular/core/testing';
|
|
2
|
+
import type { DebugElement } from '@angular/core';
|
|
3
|
+
import { type TngGridActivateEvent, type TngGridCellValue, type TngGridFocusChangeEvent } from '../tng-grid';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
export declare class GridHarnessComponent {
|
|
6
|
+
ariaLabel: string;
|
|
7
|
+
ariaColcount: number | null;
|
|
8
|
+
ariaRowcount: number | null;
|
|
9
|
+
dir: 'ltr' | 'rtl';
|
|
10
|
+
focusRow: number | null | undefined;
|
|
11
|
+
focusCol: number | null | undefined;
|
|
12
|
+
selectionMode: 'none' | 'single';
|
|
13
|
+
value: TngGridCellValue | null | undefined;
|
|
14
|
+
defaultValue: TngGridCellValue | null | undefined;
|
|
15
|
+
wrap: boolean;
|
|
16
|
+
disableCell01: boolean;
|
|
17
|
+
readonly focusRowChanges: Array<number | null>;
|
|
18
|
+
readonly focusColChanges: Array<number | null>;
|
|
19
|
+
readonly focusEvents: TngGridFocusChangeEvent[];
|
|
20
|
+
readonly valueChanges: Array<TngGridCellValue | null>;
|
|
21
|
+
readonly activateEvents: TngGridActivateEvent[];
|
|
22
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<GridHarnessComponent, never>;
|
|
23
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<GridHarnessComponent, "ng-component", never, {}, {}, never, never, true, never>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createGridHarnessFixture(overrides?: Partial<GridHarnessComponent>): Promise<ComponentFixture<GridHarnessComponent>>;
|
|
26
|
+
export declare function dispatchGridKeydown(element: HTMLElement, key: string, init?: KeyboardEventInit): KeyboardEvent;
|
|
27
|
+
export declare function getGrid(fixture: ComponentFixture<GridHarnessComponent>): HTMLElement;
|
|
28
|
+
export declare function getOutsideButton(fixture: ComponentFixture<GridHarnessComponent>): HTMLButtonElement;
|
|
29
|
+
export declare function getCell(fixture: ComponentFixture<GridHarnessComponent>, row: number, col: number): HTMLButtonElement;
|
|
30
|
+
export declare function getRow(fixture: ComponentFixture<GridHarnessComponent>, row: number): HTMLElement;
|
|
31
|
+
export declare function getCellDebugElement(fixture: ComponentFixture<GridHarnessComponent>, row: number, col: number): DebugElement;
|
|
32
|
+
//# sourceMappingURL=tng-grid.test-harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tng-grid.test-harness.d.ts","sourceRoot":"","sources":["../../../../../../../../../libs/tailng-ui/primitives/src/lib/layout/grid/__tests__/tng-grid.test-harness.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE7B,MAAM,aAAa,CAAC;;AAErB,qBAyHa,oBAAoB;IACxB,SAAS,SAAkB;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IACnC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IACnC,GAAG,EAAE,KAAK,GAAG,KAAK,CAAS;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAa;IAChD,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAa;IAChD,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAU;IAC1C,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,CAAa;IACvD,YAAY,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,CAAa;IAC9D,IAAI,UAAS;IACb,aAAa,UAAQ;IAE5B,SAAgB,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAM;IAC3D,SAAgB,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAM;IAC3D,SAAgB,WAAW,EAAE,uBAAuB,EAAE,CAAM;IAC5D,SAAgB,YAAY,EAAE,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAM;IAClE,SAAgB,cAAc,EAAE,oBAAoB,EAAE,CAAM;yCAjBjD,oBAAoB;2CAApB,oBAAoB;CAkBhC;AAED,wBAAsB,wBAAwB,CAC5C,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,OAAO,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CASjD;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,iBAAsB,GAC3B,aAAa,CASf;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,GAAG,WAAW,CAEpF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,GAAG,iBAAiB,CAEnG;AAED,wBAAgB,OAAO,CACrB,OAAO,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,EAC/C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,iBAAiB,CAEnB;AAED,wBAAgB,MAAM,CACpB,OAAO,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,EAC/C,GAAG,EAAE,MAAM,GACV,WAAW,CAEb;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,EAC/C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,YAAY,CAOd"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import { TestBed } from '@angular/core/testing';
|
|
3
|
+
import { By } from '@angular/platform-browser';
|
|
4
|
+
import { TngGrid, TngGridCell, TngGridRow, } from '../tng-grid';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
export class GridHarnessComponent {
|
|
7
|
+
ariaLabel = 'Harness grid';
|
|
8
|
+
ariaColcount = null;
|
|
9
|
+
ariaRowcount = null;
|
|
10
|
+
dir = 'ltr';
|
|
11
|
+
focusRow = undefined;
|
|
12
|
+
focusCol = undefined;
|
|
13
|
+
selectionMode = 'none';
|
|
14
|
+
value = undefined;
|
|
15
|
+
defaultValue = undefined;
|
|
16
|
+
wrap = false;
|
|
17
|
+
disableCell01 = true;
|
|
18
|
+
focusRowChanges = [];
|
|
19
|
+
focusColChanges = [];
|
|
20
|
+
focusEvents = [];
|
|
21
|
+
valueChanges = [];
|
|
22
|
+
activateEvents = [];
|
|
23
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GridHarnessComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
24
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.1", type: GridHarnessComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
|
25
|
+
<div
|
|
26
|
+
tngGrid
|
|
27
|
+
[ariaLabel]="ariaLabel"
|
|
28
|
+
[ariaColcount]="ariaColcount"
|
|
29
|
+
[ariaRowcount]="ariaRowcount"
|
|
30
|
+
[dir]="dir"
|
|
31
|
+
[focusRow]="focusRow"
|
|
32
|
+
[focusCol]="focusCol"
|
|
33
|
+
[selectionMode]="selectionMode"
|
|
34
|
+
[value]="value"
|
|
35
|
+
[defaultValue]="defaultValue"
|
|
36
|
+
[wrap]="wrap"
|
|
37
|
+
(focusRowChange)="focusRowChanges.push($event)"
|
|
38
|
+
(focusColChange)="focusColChanges.push($event)"
|
|
39
|
+
(focusChange)="focusEvents.push($event)"
|
|
40
|
+
(valueChange)="valueChanges.push($event)"
|
|
41
|
+
(cellActivate)="activateEvents.push($event)"
|
|
42
|
+
data-testid="grid"
|
|
43
|
+
>
|
|
44
|
+
<div tngGridRow data-testid="row-0">
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
tngGridCell
|
|
48
|
+
data-testid="cell-0-0"
|
|
49
|
+
cellRole="columnheader"
|
|
50
|
+
[rowIndex]="0"
|
|
51
|
+
[colIndex]="0"
|
|
52
|
+
>
|
|
53
|
+
Header
|
|
54
|
+
</button>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
tngGridCell
|
|
58
|
+
data-testid="cell-0-1"
|
|
59
|
+
[disabled]="disableCell01"
|
|
60
|
+
[rowIndex]="0"
|
|
61
|
+
[colIndex]="1"
|
|
62
|
+
>
|
|
63
|
+
Alpha
|
|
64
|
+
</button>
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
tngGridCell
|
|
68
|
+
data-testid="cell-0-2"
|
|
69
|
+
[rowIndex]="0"
|
|
70
|
+
[colIndex]="2"
|
|
71
|
+
>
|
|
72
|
+
Beta
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div tngGridRow data-testid="row-1">
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
tngGridCell
|
|
80
|
+
data-testid="cell-1-0"
|
|
81
|
+
[rowIndex]="1"
|
|
82
|
+
[colIndex]="0"
|
|
83
|
+
>
|
|
84
|
+
Gamma
|
|
85
|
+
</button>
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
tngGridCell
|
|
89
|
+
data-testid="cell-1-1"
|
|
90
|
+
[rowIndex]="1"
|
|
91
|
+
[colIndex]="1"
|
|
92
|
+
[rowSpan]="2"
|
|
93
|
+
[colSpan]="2"
|
|
94
|
+
>
|
|
95
|
+
Delta
|
|
96
|
+
</button>
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
tngGridCell
|
|
100
|
+
data-testid="cell-1-2"
|
|
101
|
+
[rowIndex]="1"
|
|
102
|
+
[colIndex]="2"
|
|
103
|
+
>
|
|
104
|
+
Epsilon
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div tngGridRow data-testid="row-2">
|
|
109
|
+
<button
|
|
110
|
+
type="button"
|
|
111
|
+
tngGridCell
|
|
112
|
+
data-testid="cell-2-0"
|
|
113
|
+
[rowIndex]="2"
|
|
114
|
+
[colIndex]="0"
|
|
115
|
+
>
|
|
116
|
+
Zeta
|
|
117
|
+
</button>
|
|
118
|
+
<button
|
|
119
|
+
type="button"
|
|
120
|
+
tngGridCell
|
|
121
|
+
data-testid="cell-2-1"
|
|
122
|
+
[rowIndex]="2"
|
|
123
|
+
[colIndex]="1"
|
|
124
|
+
>
|
|
125
|
+
Eta
|
|
126
|
+
</button>
|
|
127
|
+
<button
|
|
128
|
+
type="button"
|
|
129
|
+
tngGridCell
|
|
130
|
+
data-testid="cell-2-2"
|
|
131
|
+
[rowIndex]="2"
|
|
132
|
+
[colIndex]="2"
|
|
133
|
+
>
|
|
134
|
+
Theta
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<button type="button" data-testid="outside-button">Outside</button>
|
|
140
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: TngGrid, selector: "[tngGrid]", inputs: ["dir", "focusCol", "focusRow", "selectionMode", "value", "defaultValue", "wrap", "ariaLabel", "ariaColcount", "ariaRowcount"], outputs: ["focusRowChange", "focusColChange", "focusChange", "valueChange", "cellActivate"], exportAs: ["tngGrid"] }, { kind: "directive", type: TngGridRow, selector: "[tngGridRow]", exportAs: ["tngGridRow"] }, { kind: "directive", type: TngGridCell, selector: "[tngGridCell]", inputs: ["rowIndex", "colIndex", "rowSpan", "colSpan", "disabled", "selected", "cellRole"], exportAs: ["tngGridCell"] }] });
|
|
141
|
+
}
|
|
142
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GridHarnessComponent, decorators: [{
|
|
143
|
+
type: Component,
|
|
144
|
+
args: [{
|
|
145
|
+
standalone: true,
|
|
146
|
+
imports: [TngGrid, TngGridRow, TngGridCell],
|
|
147
|
+
template: `
|
|
148
|
+
<div
|
|
149
|
+
tngGrid
|
|
150
|
+
[ariaLabel]="ariaLabel"
|
|
151
|
+
[ariaColcount]="ariaColcount"
|
|
152
|
+
[ariaRowcount]="ariaRowcount"
|
|
153
|
+
[dir]="dir"
|
|
154
|
+
[focusRow]="focusRow"
|
|
155
|
+
[focusCol]="focusCol"
|
|
156
|
+
[selectionMode]="selectionMode"
|
|
157
|
+
[value]="value"
|
|
158
|
+
[defaultValue]="defaultValue"
|
|
159
|
+
[wrap]="wrap"
|
|
160
|
+
(focusRowChange)="focusRowChanges.push($event)"
|
|
161
|
+
(focusColChange)="focusColChanges.push($event)"
|
|
162
|
+
(focusChange)="focusEvents.push($event)"
|
|
163
|
+
(valueChange)="valueChanges.push($event)"
|
|
164
|
+
(cellActivate)="activateEvents.push($event)"
|
|
165
|
+
data-testid="grid"
|
|
166
|
+
>
|
|
167
|
+
<div tngGridRow data-testid="row-0">
|
|
168
|
+
<button
|
|
169
|
+
type="button"
|
|
170
|
+
tngGridCell
|
|
171
|
+
data-testid="cell-0-0"
|
|
172
|
+
cellRole="columnheader"
|
|
173
|
+
[rowIndex]="0"
|
|
174
|
+
[colIndex]="0"
|
|
175
|
+
>
|
|
176
|
+
Header
|
|
177
|
+
</button>
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
tngGridCell
|
|
181
|
+
data-testid="cell-0-1"
|
|
182
|
+
[disabled]="disableCell01"
|
|
183
|
+
[rowIndex]="0"
|
|
184
|
+
[colIndex]="1"
|
|
185
|
+
>
|
|
186
|
+
Alpha
|
|
187
|
+
</button>
|
|
188
|
+
<button
|
|
189
|
+
type="button"
|
|
190
|
+
tngGridCell
|
|
191
|
+
data-testid="cell-0-2"
|
|
192
|
+
[rowIndex]="0"
|
|
193
|
+
[colIndex]="2"
|
|
194
|
+
>
|
|
195
|
+
Beta
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div tngGridRow data-testid="row-1">
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
tngGridCell
|
|
203
|
+
data-testid="cell-1-0"
|
|
204
|
+
[rowIndex]="1"
|
|
205
|
+
[colIndex]="0"
|
|
206
|
+
>
|
|
207
|
+
Gamma
|
|
208
|
+
</button>
|
|
209
|
+
<button
|
|
210
|
+
type="button"
|
|
211
|
+
tngGridCell
|
|
212
|
+
data-testid="cell-1-1"
|
|
213
|
+
[rowIndex]="1"
|
|
214
|
+
[colIndex]="1"
|
|
215
|
+
[rowSpan]="2"
|
|
216
|
+
[colSpan]="2"
|
|
217
|
+
>
|
|
218
|
+
Delta
|
|
219
|
+
</button>
|
|
220
|
+
<button
|
|
221
|
+
type="button"
|
|
222
|
+
tngGridCell
|
|
223
|
+
data-testid="cell-1-2"
|
|
224
|
+
[rowIndex]="1"
|
|
225
|
+
[colIndex]="2"
|
|
226
|
+
>
|
|
227
|
+
Epsilon
|
|
228
|
+
</button>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div tngGridRow data-testid="row-2">
|
|
232
|
+
<button
|
|
233
|
+
type="button"
|
|
234
|
+
tngGridCell
|
|
235
|
+
data-testid="cell-2-0"
|
|
236
|
+
[rowIndex]="2"
|
|
237
|
+
[colIndex]="0"
|
|
238
|
+
>
|
|
239
|
+
Zeta
|
|
240
|
+
</button>
|
|
241
|
+
<button
|
|
242
|
+
type="button"
|
|
243
|
+
tngGridCell
|
|
244
|
+
data-testid="cell-2-1"
|
|
245
|
+
[rowIndex]="2"
|
|
246
|
+
[colIndex]="1"
|
|
247
|
+
>
|
|
248
|
+
Eta
|
|
249
|
+
</button>
|
|
250
|
+
<button
|
|
251
|
+
type="button"
|
|
252
|
+
tngGridCell
|
|
253
|
+
data-testid="cell-2-2"
|
|
254
|
+
[rowIndex]="2"
|
|
255
|
+
[colIndex]="2"
|
|
256
|
+
>
|
|
257
|
+
Theta
|
|
258
|
+
</button>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<button type="button" data-testid="outside-button">Outside</button>
|
|
263
|
+
`,
|
|
264
|
+
}]
|
|
265
|
+
}] });
|
|
266
|
+
export async function createGridHarnessFixture(overrides = {}) {
|
|
267
|
+
await TestBed.configureTestingModule({
|
|
268
|
+
imports: [GridHarnessComponent],
|
|
269
|
+
}).compileComponents();
|
|
270
|
+
const fixture = TestBed.createComponent(GridHarnessComponent);
|
|
271
|
+
Object.assign(fixture.componentInstance, overrides);
|
|
272
|
+
fixture.detectChanges();
|
|
273
|
+
return fixture;
|
|
274
|
+
}
|
|
275
|
+
export function dispatchGridKeydown(element, key, init = {}) {
|
|
276
|
+
const event = new KeyboardEvent('keydown', {
|
|
277
|
+
bubbles: true,
|
|
278
|
+
cancelable: true,
|
|
279
|
+
key,
|
|
280
|
+
...init,
|
|
281
|
+
});
|
|
282
|
+
element.dispatchEvent(event);
|
|
283
|
+
return event;
|
|
284
|
+
}
|
|
285
|
+
export function getGrid(fixture) {
|
|
286
|
+
return queryByTestId(fixture, 'grid');
|
|
287
|
+
}
|
|
288
|
+
export function getOutsideButton(fixture) {
|
|
289
|
+
return queryByTestId(fixture, 'outside-button');
|
|
290
|
+
}
|
|
291
|
+
export function getCell(fixture, row, col) {
|
|
292
|
+
return queryByTestId(fixture, `cell-${row}-${col}`);
|
|
293
|
+
}
|
|
294
|
+
export function getRow(fixture, row) {
|
|
295
|
+
return queryByTestId(fixture, `row-${row}`);
|
|
296
|
+
}
|
|
297
|
+
export function getCellDebugElement(fixture, row, col) {
|
|
298
|
+
const de = fixture.debugElement.query(By.css(`[data-testid="cell-${row}-${col}"]`));
|
|
299
|
+
if (de === null) {
|
|
300
|
+
throw new Error(`Missing cell ${row}:${col}`);
|
|
301
|
+
}
|
|
302
|
+
return de;
|
|
303
|
+
}
|
|
304
|
+
function queryByTestId(fixture, testId) {
|
|
305
|
+
const hostElement = fixture.nativeElement;
|
|
306
|
+
const element = hostElement.querySelector(`[data-testid="${testId}"]`);
|
|
307
|
+
if (element === null) {
|
|
308
|
+
throw new Error(`Missing element with data-testid="${testId}"`);
|
|
309
|
+
}
|
|
310
|
+
return element;
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=tng-grid.test-harness.js.map
|