@tailng-ui/primitives 0.50.0 → 0.52.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 +2 -2
- package/src/lib/form/_shared/select/tng-select.overlay.shared.d.ts +6 -0
- package/src/lib/form/_shared/select/tng-select.overlay.shared.d.ts.map +1 -1
- package/src/lib/form/_shared/select/tng-select.overlay.shared.js +65 -11
- package/src/lib/form/_shared/select/tng-select.overlay.shared.js.map +1 -1
- package/src/lib/form/autocomplete/tng-autocomplete.overlay.d.ts +6 -1
- package/src/lib/form/autocomplete/tng-autocomplete.overlay.d.ts.map +1 -1
- package/src/lib/form/autocomplete/tng-autocomplete.overlay.js +59 -3
- package/src/lib/form/autocomplete/tng-autocomplete.overlay.js.map +1 -1
- package/src/lib/form/datepicker/tng-datepicker.overlay.d.ts +11 -0
- package/src/lib/form/datepicker/tng-datepicker.overlay.d.ts.map +1 -1
- package/src/lib/form/datepicker/tng-datepicker.overlay.js +73 -3
- package/src/lib/form/datepicker/tng-datepicker.overlay.js.map +1 -1
- package/src/lib/form/index.d.ts +1 -0
- package/src/lib/form/index.d.ts.map +1 -1
- package/src/lib/form/index.js +1 -0
- package/src/lib/form/index.js.map +1 -1
- package/src/lib/form/input/tng-adornment.d.ts +6 -6
- package/src/lib/form/input/tng-adornment.d.ts.map +1 -1
- package/src/lib/form/input/tng-adornment.js +12 -12
- package/src/lib/form/input/tng-adornment.js.map +1 -1
- package/src/lib/form/input/tng-input-group.d.ts +4 -4
- package/src/lib/form/input/tng-input-group.d.ts.map +1 -1
- package/src/lib/form/input/tng-input-group.js +8 -8
- package/src/lib/form/input/tng-input-group.js.map +1 -1
- package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.overlay.d.ts +5 -0
- package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.overlay.d.ts.map +1 -1
- package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.overlay.js +51 -4
- package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.overlay.js.map +1 -1
|
@@ -41,6 +41,48 @@ function viewportRect() {
|
|
|
41
41
|
function isInside(target, container) {
|
|
42
42
|
return !!target && target instanceof Node && container.contains(target);
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* When the overlay's host element lives inside a `tng-form-field`, the form-field
|
|
46
|
+
* is the visible frame the consumer sees, so the overlay should align with it
|
|
47
|
+
* (width + left/right edges). For the `left` label layout the form-field's root
|
|
48
|
+
* spans the label column too, so anchor on the inner control-row instead.
|
|
49
|
+
*/
|
|
50
|
+
function findFormFieldAnchor(host) {
|
|
51
|
+
const formField = host.closest('[data-slot="form-field"]');
|
|
52
|
+
if (!formField)
|
|
53
|
+
return null;
|
|
54
|
+
if (formField.getAttribute('data-label-position') === 'left') {
|
|
55
|
+
const row = formField.querySelector('.tng-form-field__control-row');
|
|
56
|
+
return row ?? formField;
|
|
57
|
+
}
|
|
58
|
+
return formField;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Rect to use for overlay positioning. When the anchor is a form-field root, the
|
|
62
|
+
* horizontal extent is taken from the form-field (so the overlay spans the field
|
|
63
|
+
* frame) but the vertical extent is taken from the inner fieldset (the input row)
|
|
64
|
+
* so the overlay opens directly under the input rather than below the messages
|
|
65
|
+
* region beneath the frame.
|
|
66
|
+
*/
|
|
67
|
+
function anchorRectFor(anchorEl) {
|
|
68
|
+
const widthRect = anchorEl.getBoundingClientRect();
|
|
69
|
+
if (!anchorEl.matches('[data-slot="form-field"]')) {
|
|
70
|
+
return rectFromClientRect(widthRect);
|
|
71
|
+
}
|
|
72
|
+
const labelPosition = anchorEl.getAttribute('data-label-position');
|
|
73
|
+
const fieldset = anchorEl.querySelector('[data-slot="form-field-control-row"]');
|
|
74
|
+
const innerRow = anchorEl.querySelector('.tng-form-field__control-row');
|
|
75
|
+
const positionEl = labelPosition === 'outline' ? (fieldset ?? innerRow) : (innerRow ?? fieldset);
|
|
76
|
+
if (!positionEl)
|
|
77
|
+
return rectFromClientRect(widthRect);
|
|
78
|
+
const positionRect = positionEl.getBoundingClientRect();
|
|
79
|
+
return {
|
|
80
|
+
left: widthRect.left,
|
|
81
|
+
width: widthRect.width,
|
|
82
|
+
top: positionRect.top,
|
|
83
|
+
height: positionRect.height,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
44
86
|
export class TngMultiAutocompleteOverlay {
|
|
45
87
|
multi = inject(TNG_MULTI_AUTOCOMPLETE);
|
|
46
88
|
elRef = inject((ElementRef));
|
|
@@ -78,15 +120,20 @@ export class TngMultiAutocompleteOverlay {
|
|
|
78
120
|
this.originalParent = null;
|
|
79
121
|
});
|
|
80
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Anchor for overlay positioning, width, and dismiss boundary.
|
|
125
|
+
* Prefer the enclosing form-field (so the overlay aligns with the visible
|
|
126
|
+
* field frame), else the multi-autocomplete host element itself.
|
|
127
|
+
*/
|
|
81
128
|
findAnchorEl() {
|
|
82
|
-
return this.multi.hostElement;
|
|
129
|
+
return findFormFieldAnchor(this.multi.hostElement) ?? this.multi.hostElement;
|
|
83
130
|
}
|
|
84
131
|
reposition() {
|
|
85
132
|
if (!this.multi.open())
|
|
86
133
|
return;
|
|
87
134
|
const panel = this.elRef.nativeElement;
|
|
88
135
|
const anchorEl = this.findAnchorEl();
|
|
89
|
-
const anchor =
|
|
136
|
+
const anchor = anchorRectFor(anchorEl);
|
|
90
137
|
const overlay = rectFromClientRect(panel.getBoundingClientRect());
|
|
91
138
|
const viewport = viewportRect();
|
|
92
139
|
const result = computeOverlayPosition({
|
|
@@ -136,7 +183,7 @@ export class TngMultiAutocompleteOverlay {
|
|
|
136
183
|
const panel = this.elRef.nativeElement;
|
|
137
184
|
if (isInside(event.target, panel))
|
|
138
185
|
return;
|
|
139
|
-
if (isInside(event.target, this.
|
|
186
|
+
if (isInside(event.target, this.findAnchorEl()))
|
|
140
187
|
return;
|
|
141
188
|
if (event.target && event.target.closest?.('[data-slot="multi-autocomplete-option"]')) {
|
|
142
189
|
return;
|
|
@@ -195,7 +242,7 @@ export class TngMultiAutocompleteOverlay {
|
|
|
195
242
|
queueMicrotask(() => {
|
|
196
243
|
if (!this.multi.open())
|
|
197
244
|
return;
|
|
198
|
-
const anchor =
|
|
245
|
+
const anchor = anchorRectFor(this.findAnchorEl());
|
|
199
246
|
const viewportWidth = viewportRect().width;
|
|
200
247
|
const inlineSize = Math.max(0, Math.min(anchor.width, viewportWidth - 16));
|
|
201
248
|
panel.style.width = `${inlineSize}px`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tng-multi-autocomplete.overlay.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/multi-autocomplete/tng-multi-autocomplete.overlay.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,UAAU,EACV,WAAW,EACX,MAAM,EACN,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;;AAKzE,MAAM,uCAAuC,GAAG;IAC9C,iCAAiC;IACjC,kCAAkC;IAClC,qCAAqC;IACrC,qCAAqC;IACrC,kCAAkC;IAClC,kCAAkC;IAClC,oCAAoC;IACpC,oCAAoC;IACpC,oCAAoC;IACpC,0CAA0C;IAC1C,iBAAiB;IACjB,iCAAiC;IACjC,wCAAwC;IACxC,6BAA6B;IAC7B,kCAAkC;IAClC,6BAA6B;IAC7B,gCAAgC;IAChC,gCAAgC;IAChC,iCAAiC;IACjC,qCAAqC;IACrC,+BAA+B;IAC/B,iCAAiC;IACjC,uCAAuC;CAC/B,CAAC;AAEX,SAAS,kBAAkB,CAAC,CAAuB;IACjD,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACxE,CAAC;AAED,SAAS,YAAY;IACnB,OAAO;QACL,IAAI,EAAE,CAAC;QACP,GAAG,EAAE,CAAC;QACN,KAAK,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI;QAChC,MAAM,EAAE,MAAM,CAAC,WAAW,IAAI,GAAG;KAClC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,MAA0B,EAAE,SAAsB;IAClE,OAAO,CAAC,CAAC,MAAM,IAAI,MAAM,YAAY,IAAI,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1E,CAAC;AAMD,MAAM,OAAO,2BAA2B;IACrB,KAAK,GAAG,MAAM,CAAuB,sBAAsB,CAAC,CAAC;IAC7D,KAAK,GAAG,MAAM,CAAC,CAAA,UAAuB,CAAA,CAAC,CAAC;IACxC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAEzC,oBAAoB,GAAwB,IAAI,CAAC;IACjD,oBAAoB,GAAwB,IAAI,CAAC;IACjD,cAAc,GAA0B,IAAI,CAAC;IAC7C,wBAAwB,GAAwB,IAAI,CAAC;IACrD,WAAW,GAAmB,IAAI,CAAC;IACnC,cAAc,GAAgB,IAAI,CAAC;IAGxB,QAAQ,GAAG,4BAAqC,CAAC;IAEpE,IACc,MAAM;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,CAAC;IAED;QACE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;QACxC,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC,aAAa,CAAC,uCAAuC,CAAC,CAAC;QACnF,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;QACxC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAErC,MAAM,CAAC,GAAG,EAAE;YACV,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YAC/B,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE;YAC7B,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC9B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,YAAY;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;IAChC,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YAAE,OAAO;QAE/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,sBAAsB,CAAC;YACpC,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,OAAO;YACpB,YAAY,EAAE,QAAQ;SACvB,CAAC,CAAC;QAEH,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;QACnC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;IACpC,CAAC;IAEO,wBAAwB;QAC9B,IAAI,KAAK,GAAkB,IAAI,CAAC;QAEhC,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO;YAE3B,KAAK,GAAG,qBAAqB,CAAC,GAAG,EAAE;gBACjC,KAAK,GAAG,IAAI,CAAC;gBACb,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAElC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC5C,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,oBAAoB,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjF,IAAI,CAAC,oBAAoB,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEvF,IAAI,gBAAgB,IAAI,MAAM,EAAE,CAAC;YAC/B,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAEO,2BAA2B;QACjC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,cAAc,EAAE,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC7B,CAAC;IAEO,mBAAmB;QACzB,IAAI,IAAI,CAAC,wBAAwB;YAAE,OAAO;QAE1C,MAAM,aAAa,GAAG,CAAC,KAAmB,EAAE,EAAE;YAC5C,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;gBAAE,OAAO;YAE/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;YACvC,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC;gBAAE,OAAO;YAC1C,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;gBAAE,OAAO;YAC3D,IAAI,KAAK,CAAC,MAAM,IAAK,KAAK,CAAC,MAAkB,CAAC,OAAO,EAAE,CAAC,yCAAyC,CAAC,EAAE,CAAC;gBACnG,OAAO;YACT,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,CAAC;QAEF,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QAC9D,IAAI,CAAC,wBAAwB,GAAG,GAAG,EAAE,CACnC,QAAQ,CAAC,mBAAmB,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;IACrE,CAAC;IAEO,sBAAsB;QAC5B,IAAI,CAAC,wBAAwB,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC;IACvC,CAAC;IAEO,sBAAsB;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;QACvC,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAE5D,KAAK,MAAM,MAAM,IAAI,uCAAuC,EAAE,CAAC;YAC7D,MAAM,KAAK,GAAG,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACzD,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACzC,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAG,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;QACnD,IAAI,WAAW,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;YAC5C,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAEO,sBAAsB;QAC5B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM;YACnC,iHAAiH,CAAC;IACtH,CAAC;IAEO,uBAAuB;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;QAEvC,KAAK,MAAM,MAAM,IAAI,uCAAuC,EAAE,CAAC;YAC7D,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;IAC7C,CAAC;IAEO,sBAAsB;QAC5B,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAEhC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;QACvC,IAAI,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,IAAI,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC;QAC/B,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;QACzB,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAE9B,cAAc,CAAC,GAAG,EAAE;YAClB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;gBAAE,OAAO;YAE/B,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,qBAAqB,EAAE,CAAC,CAAC;YAC/E,MAAM,aAAa,GAAG,YAAY,EAAE,CAAC,KAAK,CAAC;YAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,EAAE,CAAC,CAAC,CAAC;YAC3E,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,UAAU,IAAI,CAAC;YACtC,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,UAAU,IAAI,CAAC;YACzC,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC7B,CAAC;IAEO,oBAAoB,CAAC,KAAK,GAAG,KAAK;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;QACvC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,IAAI,EAAE,CAAC;YACjD,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC;YACjC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACpE,CAAC;aAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/B,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,CAAC,2BAA2B,EAAE,CAAC;QACnC,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC;QAC1B,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC;QACtB,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;QACrB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;QACxB,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;QACvB,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC/B,IAAI,CAAC,sBAAsB,EAAE,CAAC;IAChC,CAAC;uGAvNU,2BAA2B;2FAA3B,2BAA2B;;2FAA3B,2BAA2B;kBAJvC,SAAS;mBAAC;oBACT,QAAQ,EAAE,+BAA+B;oBACzC,QAAQ,EAAE,6BAA6B;iBACxC;;sBAaE,WAAW;uBAAC,gBAAgB;;sBAG5B,WAAW;uBAAC,aAAa","sourcesContent":["import {\n DestroyRef,\n Directive,\n ElementRef,\n HostBinding,\n effect,\n inject,\n} from '@angular/core';\nimport { computeOverlayPosition } from '@tailng-ui/cdk';\nimport { TNG_MULTI_AUTOCOMPLETE } from './tng-multi-autocomplete.tokens';\nimport type { TngMultiAutocomplete } from './tng-multi-autocomplete';\n\ntype MaybeRect = Readonly<{ left: number; top: number; width: number; height: number }>;\n\nconst PORTALLED_MULTI_AUTOCOMPLETE_THEME_VARS = [\n '--tng-multi-autocomplete-radius',\n '--tng-multi-autocomplete-padding',\n '--tng-multi-autocomplete-trigger-py',\n '--tng-multi-autocomplete-trigger-px',\n '--tng-multi-autocomplete-chip-py',\n '--tng-multi-autocomplete-chip-px',\n '--tng-multi-autocomplete-option-py',\n '--tng-multi-autocomplete-option-px',\n '--tng-multi-autocomplete-z-overlay',\n '--tng-multi-autocomplete-overlay-z-index',\n '--tng-z-overlay',\n '--tng-multi-autocomplete-border',\n '--tng-multi-autocomplete-border-strong',\n '--tng-multi-autocomplete-bg',\n '--tng-multi-autocomplete-surface',\n '--tng-multi-autocomplete-fg',\n '--tng-multi-autocomplete-muted',\n '--tng-multi-autocomplete-brand',\n '--tng-multi-autocomplete-danger',\n '--tng-multi-autocomplete-focus-ring',\n '--tng-multi-autocomplete-ease',\n '--tng-multi-autocomplete-shadow',\n '--tng-multi-autocomplete-shadow-focus',\n] as const;\n\nfunction rectFromClientRect(r: DOMRect | ClientRect): MaybeRect {\n return { left: r.left, top: r.top, width: r.width, height: r.height };\n}\n\nfunction viewportRect(): MaybeRect {\n return {\n left: 0,\n top: 0,\n width: window.innerWidth || 1024,\n height: window.innerHeight || 768,\n };\n}\n\nfunction isInside(target: EventTarget | null, container: HTMLElement): boolean {\n return !!target && target instanceof Node && container.contains(target);\n}\n\n@Directive({\n selector: '[tngMultiAutocompleteOverlay]',\n exportAs: 'tngMultiAutocompleteOverlay',\n})\nexport class TngMultiAutocompleteOverlay {\n private readonly multi = inject<TngMultiAutocomplete>(TNG_MULTI_AUTOCOMPLETE);\n private readonly elRef = inject(ElementRef<HTMLElement>);\n private readonly destroyRef = inject(DestroyRef);\n\n private removeResizeListener: (() => void) | null = null;\n private removeScrollListener: (() => void) | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private removeDocPointerListener: (() => void) | null = null;\n private placeholder: Comment | null = null;\n private originalParent: Node | null = null;\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'multi-autocomplete-overlay' as const;\n\n @HostBinding('attr.hidden')\n protected get hidden(): '' | null {\n return this.multi.open() ? null : '';\n }\n\n constructor() {\n const hostEl = this.elRef.nativeElement;\n this.placeholder = document.createComment('tng-multi-autocomplete-overlay-anchor');\n this.originalParent = hostEl.parentNode;\n this.originalParent?.insertBefore(this.placeholder, hostEl);\n this.multi.setOverlayElement(hostEl);\n\n effect(() => {\n const open = this.multi.open();\n if (open) {\n this.mountToBodyAndPosition();\n } else {\n this.restoreToPlaceholder();\n }\n });\n\n this.destroyRef.onDestroy(() => {\n this.teardownOutsidePointer();\n this.restoreToPlaceholder(true);\n this.multi.setOverlayElement(null);\n this.placeholder = null;\n this.originalParent = null;\n });\n }\n\n private findAnchorEl(): HTMLElement {\n return this.multi.hostElement;\n }\n\n private reposition(): void {\n if (!this.multi.open()) return;\n\n const panel = this.elRef.nativeElement;\n const anchorEl = this.findAnchorEl();\n const anchor = rectFromClientRect(anchorEl.getBoundingClientRect());\n const overlay = rectFromClientRect(panel.getBoundingClientRect());\n const viewport = viewportRect();\n const result = computeOverlayPosition({\n anchorRect: anchor,\n overlayRect: overlay,\n viewportRect: viewport,\n });\n\n panel.style.left = `${result.x}px`;\n panel.style.top = `${result.y}px`;\n }\n\n private setupRepositionListeners(): void {\n let rafId: number | null = null;\n\n const schedule = () => {\n if (rafId !== null) return;\n\n rafId = requestAnimationFrame(() => {\n rafId = null;\n this.reposition();\n });\n };\n\n const onResize = () => schedule();\n const onScroll = () => schedule();\n\n window.addEventListener('resize', onResize);\n window.addEventListener('scroll', onScroll, true);\n this.removeResizeListener = () => window.removeEventListener('resize', onResize);\n this.removeScrollListener = () => window.removeEventListener('scroll', onScroll, true);\n\n if ('ResizeObserver' in window) {\n this.resizeObserver = new ResizeObserver(() => schedule());\n this.resizeObserver.observe(this.findAnchorEl());\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 setupOutsidePointer(): void {\n if (this.removeDocPointerListener) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (!this.multi.open()) return;\n\n const panel = this.elRef.nativeElement;\n if (isInside(event.target, panel)) return;\n if (isInside(event.target, this.multi.hostElement)) return;\n if (event.target && (event.target as Element).closest?.('[data-slot=\"multi-autocomplete-option\"]')) {\n return;\n }\n\n this.multi.close();\n };\n\n document.addEventListener('pointerdown', onPointerDown, true);\n this.removeDocPointerListener = () =>\n document.removeEventListener('pointerdown', onPointerDown, true);\n }\n\n private teardownOutsidePointer(): void {\n this.removeDocPointerListener?.();\n this.removeDocPointerListener = null;\n }\n\n private syncPortalledThemeVars(): void {\n const panel = this.elRef.nativeElement;\n const hostStyles = getComputedStyle(this.multi.hostElement);\n\n for (const cssVar of PORTALLED_MULTI_AUTOCOMPLETE_THEME_VARS) {\n const value = hostStyles.getPropertyValue(cssVar).trim();\n if (value) {\n panel.style.setProperty(cssVar, value);\n } else {\n panel.style.removeProperty(cssVar);\n }\n }\n\n const colorScheme = hostStyles.colorScheme?.trim();\n if (colorScheme && colorScheme !== 'normal') {\n panel.style.colorScheme = colorScheme;\n } else {\n panel.style.removeProperty('color-scheme');\n }\n }\n\n private applyPortalledStacking(): void {\n this.elRef.nativeElement.style.zIndex =\n 'var(--tng-multi-autocomplete-z-overlay, var(--tng-multi-autocomplete-overlay-z-index, var(--tng-z-overlay, 2)))';\n }\n\n private clearPortalledThemeVars(): void {\n const panel = this.elRef.nativeElement;\n\n for (const cssVar of PORTALLED_MULTI_AUTOCOMPLETE_THEME_VARS) {\n panel.style.removeProperty(cssVar);\n }\n\n panel.style.removeProperty('color-scheme');\n }\n\n private mountToBodyAndPosition(): void {\n this.setupRepositionListeners();\n\n const panel = this.elRef.nativeElement;\n if (panel.parentNode !== document.body) {\n document.body.appendChild(panel);\n }\n\n panel.style.position = 'fixed';\n panel.style.left = '0px';\n panel.style.top = '0px';\n this.syncPortalledThemeVars();\n this.applyPortalledStacking();\n\n queueMicrotask(() => {\n if (!this.multi.open()) return;\n\n const anchor = rectFromClientRect(this.findAnchorEl().getBoundingClientRect());\n const viewportWidth = viewportRect().width;\n const inlineSize = Math.max(0, Math.min(anchor.width, viewportWidth - 16));\n panel.style.width = `${inlineSize}px`;\n panel.style.minWidth = `${inlineSize}px`;\n this.reposition();\n });\n\n this.setupOutsidePointer();\n }\n\n private restoreToPlaceholder(force = false): void {\n const panel = this.elRef.nativeElement;\n if (!force && panel.parentNode !== document.body) {\n this.teardownOutsidePointer();\n return;\n }\n\n if (this.placeholder?.parentNode) {\n this.placeholder.parentNode.insertBefore(panel, this.placeholder);\n } else if (this.originalParent) {\n this.originalParent.appendChild(panel);\n }\n\n this.teardownRepositionListeners();\n panel.style.position = '';\n panel.style.left = '';\n panel.style.top = '';\n panel.style.zIndex = '';\n panel.style.width = '';\n panel.style.minWidth = '';\n this.clearPortalledThemeVars();\n this.teardownOutsidePointer();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tng-multi-autocomplete.overlay.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/multi-autocomplete/tng-multi-autocomplete.overlay.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DestroyRef,\n Directive,\n ElementRef,\n HostBinding,\n effect,\n inject,\n} from '@angular/core';\nimport { computeOverlayPosition } from '@tailng-ui/cdk';\nimport { TNG_MULTI_AUTOCOMPLETE } from './tng-multi-autocomplete.tokens';\nimport type { TngMultiAutocomplete } from './tng-multi-autocomplete';\n\ntype MaybeRect = Readonly<{ left: number; top: number; width: number; height: number }>;\n\nconst PORTALLED_MULTI_AUTOCOMPLETE_THEME_VARS = [\n '--tng-multi-autocomplete-radius',\n '--tng-multi-autocomplete-padding',\n '--tng-multi-autocomplete-trigger-py',\n '--tng-multi-autocomplete-trigger-px',\n '--tng-multi-autocomplete-chip-py',\n '--tng-multi-autocomplete-chip-px',\n '--tng-multi-autocomplete-option-py',\n '--tng-multi-autocomplete-option-px',\n '--tng-multi-autocomplete-z-overlay',\n '--tng-multi-autocomplete-overlay-z-index',\n '--tng-z-overlay',\n '--tng-multi-autocomplete-border',\n '--tng-multi-autocomplete-border-strong',\n '--tng-multi-autocomplete-bg',\n '--tng-multi-autocomplete-surface',\n '--tng-multi-autocomplete-fg',\n '--tng-multi-autocomplete-muted',\n '--tng-multi-autocomplete-brand',\n '--tng-multi-autocomplete-danger',\n '--tng-multi-autocomplete-focus-ring',\n '--tng-multi-autocomplete-ease',\n '--tng-multi-autocomplete-shadow',\n '--tng-multi-autocomplete-shadow-focus',\n] as const;\n\nfunction rectFromClientRect(r: DOMRect | ClientRect): MaybeRect {\n return { left: r.left, top: r.top, width: r.width, height: r.height };\n}\n\nfunction viewportRect(): MaybeRect {\n return {\n left: 0,\n top: 0,\n width: window.innerWidth || 1024,\n height: window.innerHeight || 768,\n };\n}\n\nfunction isInside(target: EventTarget | null, container: HTMLElement): boolean {\n return !!target && target instanceof Node && container.contains(target);\n}\n\n/**\n * When the overlay's host element lives inside a `tng-form-field`, the form-field\n * is the visible frame the consumer sees, so the overlay should align with it\n * (width + left/right edges). For the `left` label layout the form-field's root\n * spans the label column too, so anchor on the inner control-row instead.\n */\nfunction findFormFieldAnchor(host: HTMLElement): HTMLElement | null {\n const formField = host.closest('[data-slot=\"form-field\"]') as HTMLElement | null;\n if (!formField) return null;\n if (formField.getAttribute('data-label-position') === 'left') {\n const row = formField.querySelector('.tng-form-field__control-row') as HTMLElement | null;\n return row ?? formField;\n }\n return formField;\n}\n\n/**\n * Rect to use for overlay positioning. When the anchor is a form-field root, the\n * horizontal extent is taken from the form-field (so the overlay spans the field\n * frame) but the vertical extent is taken from the inner fieldset (the input row)\n * so the overlay opens directly under the input rather than below the messages\n * region beneath the frame.\n */\nfunction anchorRectFor(anchorEl: HTMLElement): MaybeRect {\n const widthRect = anchorEl.getBoundingClientRect();\n if (!anchorEl.matches('[data-slot=\"form-field\"]')) {\n return rectFromClientRect(widthRect);\n }\n const labelPosition = anchorEl.getAttribute('data-label-position');\n const fieldset = anchorEl.querySelector('[data-slot=\"form-field-control-row\"]') as HTMLElement | null;\n const innerRow = anchorEl.querySelector('.tng-form-field__control-row') as HTMLElement | null;\n const positionEl = labelPosition === 'outline' ? (fieldset ?? innerRow) : (innerRow ?? fieldset);\n if (!positionEl) return rectFromClientRect(widthRect);\n const positionRect = positionEl.getBoundingClientRect();\n return {\n left: widthRect.left,\n width: widthRect.width,\n top: positionRect.top,\n height: positionRect.height,\n };\n}\n\n@Directive({\n selector: '[tngMultiAutocompleteOverlay]',\n exportAs: 'tngMultiAutocompleteOverlay',\n})\nexport class TngMultiAutocompleteOverlay {\n private readonly multi = inject<TngMultiAutocomplete>(TNG_MULTI_AUTOCOMPLETE);\n private readonly elRef = inject(ElementRef<HTMLElement>);\n private readonly destroyRef = inject(DestroyRef);\n\n private removeResizeListener: (() => void) | null = null;\n private removeScrollListener: (() => void) | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private removeDocPointerListener: (() => void) | null = null;\n private placeholder: Comment | null = null;\n private originalParent: Node | null = null;\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'multi-autocomplete-overlay' as const;\n\n @HostBinding('attr.hidden')\n protected get hidden(): '' | null {\n return this.multi.open() ? null : '';\n }\n\n constructor() {\n const hostEl = this.elRef.nativeElement;\n this.placeholder = document.createComment('tng-multi-autocomplete-overlay-anchor');\n this.originalParent = hostEl.parentNode;\n this.originalParent?.insertBefore(this.placeholder, hostEl);\n this.multi.setOverlayElement(hostEl);\n\n effect(() => {\n const open = this.multi.open();\n if (open) {\n this.mountToBodyAndPosition();\n } else {\n this.restoreToPlaceholder();\n }\n });\n\n this.destroyRef.onDestroy(() => {\n this.teardownOutsidePointer();\n this.restoreToPlaceholder(true);\n this.multi.setOverlayElement(null);\n this.placeholder = null;\n this.originalParent = null;\n });\n }\n\n /**\n * Anchor for overlay positioning, width, and dismiss boundary.\n * Prefer the enclosing form-field (so the overlay aligns with the visible\n * field frame), else the multi-autocomplete host element itself.\n */\n private findAnchorEl(): HTMLElement {\n return findFormFieldAnchor(this.multi.hostElement) ?? this.multi.hostElement;\n }\n\n private reposition(): void {\n if (!this.multi.open()) return;\n\n const panel = this.elRef.nativeElement;\n const anchorEl = this.findAnchorEl();\n const anchor = anchorRectFor(anchorEl);\n const overlay = rectFromClientRect(panel.getBoundingClientRect());\n const viewport = viewportRect();\n const result = computeOverlayPosition({\n anchorRect: anchor,\n overlayRect: overlay,\n viewportRect: viewport,\n });\n\n panel.style.left = `${result.x}px`;\n panel.style.top = `${result.y}px`;\n }\n\n private setupRepositionListeners(): void {\n let rafId: number | null = null;\n\n const schedule = () => {\n if (rafId !== null) return;\n\n rafId = requestAnimationFrame(() => {\n rafId = null;\n this.reposition();\n });\n };\n\n const onResize = () => schedule();\n const onScroll = () => schedule();\n\n window.addEventListener('resize', onResize);\n window.addEventListener('scroll', onScroll, true);\n this.removeResizeListener = () => window.removeEventListener('resize', onResize);\n this.removeScrollListener = () => window.removeEventListener('scroll', onScroll, true);\n\n if ('ResizeObserver' in window) {\n this.resizeObserver = new ResizeObserver(() => schedule());\n this.resizeObserver.observe(this.findAnchorEl());\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 setupOutsidePointer(): void {\n if (this.removeDocPointerListener) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (!this.multi.open()) return;\n\n const panel = this.elRef.nativeElement;\n if (isInside(event.target, panel)) return;\n if (isInside(event.target, this.findAnchorEl())) return;\n if (event.target && (event.target as Element).closest?.('[data-slot=\"multi-autocomplete-option\"]')) {\n return;\n }\n\n this.multi.close();\n };\n\n document.addEventListener('pointerdown', onPointerDown, true);\n this.removeDocPointerListener = () =>\n document.removeEventListener('pointerdown', onPointerDown, true);\n }\n\n private teardownOutsidePointer(): void {\n this.removeDocPointerListener?.();\n this.removeDocPointerListener = null;\n }\n\n private syncPortalledThemeVars(): void {\n const panel = this.elRef.nativeElement;\n const hostStyles = getComputedStyle(this.multi.hostElement);\n\n for (const cssVar of PORTALLED_MULTI_AUTOCOMPLETE_THEME_VARS) {\n const value = hostStyles.getPropertyValue(cssVar).trim();\n if (value) {\n panel.style.setProperty(cssVar, value);\n } else {\n panel.style.removeProperty(cssVar);\n }\n }\n\n const colorScheme = hostStyles.colorScheme?.trim();\n if (colorScheme && colorScheme !== 'normal') {\n panel.style.colorScheme = colorScheme;\n } else {\n panel.style.removeProperty('color-scheme');\n }\n }\n\n private applyPortalledStacking(): void {\n this.elRef.nativeElement.style.zIndex =\n 'var(--tng-multi-autocomplete-z-overlay, var(--tng-multi-autocomplete-overlay-z-index, var(--tng-z-overlay, 2)))';\n }\n\n private clearPortalledThemeVars(): void {\n const panel = this.elRef.nativeElement;\n\n for (const cssVar of PORTALLED_MULTI_AUTOCOMPLETE_THEME_VARS) {\n panel.style.removeProperty(cssVar);\n }\n\n panel.style.removeProperty('color-scheme');\n }\n\n private mountToBodyAndPosition(): void {\n this.setupRepositionListeners();\n\n const panel = this.elRef.nativeElement;\n if (panel.parentNode !== document.body) {\n document.body.appendChild(panel);\n }\n\n panel.style.position = 'fixed';\n panel.style.left = '0px';\n panel.style.top = '0px';\n this.syncPortalledThemeVars();\n this.applyPortalledStacking();\n\n queueMicrotask(() => {\n if (!this.multi.open()) return;\n\n const anchor = anchorRectFor(this.findAnchorEl());\n const viewportWidth = viewportRect().width;\n const inlineSize = Math.max(0, Math.min(anchor.width, viewportWidth - 16));\n panel.style.width = `${inlineSize}px`;\n panel.style.minWidth = `${inlineSize}px`;\n this.reposition();\n });\n\n this.setupOutsidePointer();\n }\n\n private restoreToPlaceholder(force = false): void {\n const panel = this.elRef.nativeElement;\n if (!force && panel.parentNode !== document.body) {\n this.teardownOutsidePointer();\n return;\n }\n\n if (this.placeholder?.parentNode) {\n this.placeholder.parentNode.insertBefore(panel, this.placeholder);\n } else if (this.originalParent) {\n this.originalParent.appendChild(panel);\n }\n\n this.teardownRepositionListeners();\n panel.style.position = '';\n panel.style.left = '';\n panel.style.top = '';\n panel.style.zIndex = '';\n panel.style.width = '';\n panel.style.minWidth = '';\n this.clearPortalledThemeVars();\n this.teardownOutsidePointer();\n }\n}\n"]}
|