@taiga-ui/core 5.24.0 → 5.25.0-canary.3fb67e2

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.
Files changed (75) hide show
  1. package/classes/accessors.d.ts +9 -1
  2. package/components/cell/cell-stretch.directive.d.ts +1 -1
  3. package/components/data-list/option.directive.d.ts +1 -1
  4. package/components/scrollbar/scroll-controls.component.d.ts +1 -1
  5. package/components/scrollbar/scroll-ref.directive.d.ts +2 -0
  6. package/components/scrollbar/scrollbar.component.d.ts +2 -3
  7. package/fesm2022/taiga-ui-core-classes.mjs +24 -7
  8. package/fesm2022/taiga-ui-core-classes.mjs.map +1 -1
  9. package/fesm2022/taiga-ui-core-components-button.mjs +2 -2
  10. package/fesm2022/taiga-ui-core-components-button.mjs.map +1 -1
  11. package/fesm2022/taiga-ui-core-components-calendar.mjs +1 -1
  12. package/fesm2022/taiga-ui-core-components-calendar.mjs.map +1 -1
  13. package/fesm2022/taiga-ui-core-components-carousel.mjs +2 -2
  14. package/fesm2022/taiga-ui-core-components-carousel.mjs.map +1 -1
  15. package/fesm2022/taiga-ui-core-components-cell.mjs +6 -9
  16. package/fesm2022/taiga-ui-core-components-cell.mjs.map +1 -1
  17. package/fesm2022/taiga-ui-core-components-checkbox.mjs +2 -2
  18. package/fesm2022/taiga-ui-core-components-checkbox.mjs.map +1 -1
  19. package/fesm2022/taiga-ui-core-components-data-list.mjs +7 -7
  20. package/fesm2022/taiga-ui-core-components-data-list.mjs.map +1 -1
  21. package/fesm2022/taiga-ui-core-components-icon.mjs +2 -2
  22. package/fesm2022/taiga-ui-core-components-icon.mjs.map +1 -1
  23. package/fesm2022/taiga-ui-core-components-label.mjs +3 -3
  24. package/fesm2022/taiga-ui-core-components-label.mjs.map +1 -1
  25. package/fesm2022/taiga-ui-core-components-link.mjs +2 -2
  26. package/fesm2022/taiga-ui-core-components-link.mjs.map +1 -1
  27. package/fesm2022/taiga-ui-core-components-notification.mjs +2 -2
  28. package/fesm2022/taiga-ui-core-components-notification.mjs.map +1 -1
  29. package/fesm2022/taiga-ui-core-components-radio.mjs +2 -2
  30. package/fesm2022/taiga-ui-core-components-radio.mjs.map +1 -1
  31. package/fesm2022/taiga-ui-core-components-root.mjs +3 -3
  32. package/fesm2022/taiga-ui-core-components-root.mjs.map +1 -1
  33. package/fesm2022/taiga-ui-core-components-scrollbar.mjs +34 -49
  34. package/fesm2022/taiga-ui-core-components-scrollbar.mjs.map +1 -1
  35. package/fesm2022/taiga-ui-core-components-textfield.mjs +6 -5
  36. package/fesm2022/taiga-ui-core-components-textfield.mjs.map +1 -1
  37. package/fesm2022/taiga-ui-core-components-title.mjs +3 -3
  38. package/fesm2022/taiga-ui-core-components-title.mjs.map +1 -1
  39. package/fesm2022/taiga-ui-core-directives-appearance.mjs +3 -3
  40. package/fesm2022/taiga-ui-core-directives-appearance.mjs.map +1 -1
  41. package/fesm2022/taiga-ui-core-directives-group.mjs +3 -3
  42. package/fesm2022/taiga-ui-core-directives-group.mjs.map +1 -1
  43. package/fesm2022/taiga-ui-core-directives-icons.mjs +3 -3
  44. package/fesm2022/taiga-ui-core-directives-icons.mjs.map +1 -1
  45. package/fesm2022/taiga-ui-core-portals-alert.mjs +3 -3
  46. package/fesm2022/taiga-ui-core-portals-alert.mjs.map +1 -1
  47. package/fesm2022/taiga-ui-core-portals-dialog.mjs +2 -2
  48. package/fesm2022/taiga-ui-core-portals-dialog.mjs.map +1 -1
  49. package/fesm2022/taiga-ui-core-portals-dropdown.mjs +239 -146
  50. package/fesm2022/taiga-ui-core-portals-dropdown.mjs.map +1 -1
  51. package/fesm2022/taiga-ui-core-portals-hint.mjs +32 -34
  52. package/fesm2022/taiga-ui-core-portals-hint.mjs.map +1 -1
  53. package/fesm2022/taiga-ui-core-portals-modal.mjs +3 -3
  54. package/fesm2022/taiga-ui-core-portals-modal.mjs.map +1 -1
  55. package/fesm2022/taiga-ui-core-tokens.mjs +30 -8
  56. package/fesm2022/taiga-ui-core-tokens.mjs.map +1 -1
  57. package/fesm2022/taiga-ui-core-utils-dom.mjs +21 -1
  58. package/fesm2022/taiga-ui-core-utils-dom.mjs.map +1 -1
  59. package/package.json +36 -36
  60. package/portals/dropdown/dropdown-anchor.directive.d.ts +5 -1
  61. package/portals/dropdown/dropdown-anchored.directive.d.ts +4 -1
  62. package/portals/dropdown/dropdown-context.directive.d.ts +3 -4
  63. package/portals/dropdown/dropdown-position-sided.directive.d.ts +2 -0
  64. package/portals/dropdown/dropdown-position.directive.d.ts +2 -0
  65. package/portals/dropdown/dropdown-selection.directive.d.ts +1 -1
  66. package/portals/dropdown/dropdown.component.d.ts +3 -4
  67. package/portals/dropdown/dropdown.directive.d.ts +1 -0
  68. package/portals/dropdown/dropdown.providers.d.ts +1 -1
  69. package/portals/hint/hint-anchor.directive.d.ts +5 -1
  70. package/portals/hint/hint-pointer.directive.d.ts +1 -3
  71. package/portals/hint/hint.d.ts +1 -2
  72. package/tokens/date-format.d.ts +19 -0
  73. package/tokens/number-format.d.ts +18 -8
  74. package/utils/dom/anchor-delegate.d.ts +1 -0
  75. package/utils/dom/index.d.ts +1 -0
@@ -1,21 +1,21 @@
1
1
  import { tuiProvide, tuiDirectiveBinding, tuiCreateOptions } from '@taiga-ui/cdk/utils/di';
2
2
  import { coerceArray } from '@angular/cdk/coercion';
3
3
  import * as i0 from '@angular/core';
4
- import { Injectable, Directive, InjectionToken, Optional, Self, SkipSelf, inject, forwardRef, computed, ChangeDetectionStrategy, Component, input, effect, ElementRef, INJECTOR, ChangeDetectorRef, signal, TemplateRef, contentChild, model, PLATFORM_ID, ViewContainerRef, afterNextRender } from '@angular/core';
5
- import { outputFromObservable, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
4
+ import { Injectable, Directive, InjectionToken, Optional, Self, SkipSelf, inject, computed, ChangeDetectionStrategy, Component, input, effect, ElementRef, INJECTOR, ChangeDetectorRef, signal, TemplateRef, contentChild, model, PLATFORM_ID, ViewContainerRef } from '@angular/core';
5
+ import { takeUntilDestroyed, outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
6
6
  import { tuiZonefreeScheduler, tuiIfMap, tuiCloseWatcher, tuiTypedFromEvent, tuiStopPropagation, tuiZoneOptimized } from '@taiga-ui/cdk/observables';
7
7
  import { tuiInjectElement, tuiGetActualTarget, tuiIsElement, tuiIsElementEditable, tuiIsHTMLElement, tuiPointToClientRect, tuiIsTextNode, tuiIsTextfield } from '@taiga-ui/cdk/utils/dom';
8
- import { TuiDriverDirective, TuiPositionAccessor, tuiFallbackAccessor, TuiRectAccessor, tuiPositionAccessorFor, tuiRectAccessorFor, tuiAsVehicle, tuiAsDriver, tuiAsRectAccessor, TuiDriver, tuiAsPositionAccessor } from '@taiga-ui/core/classes';
8
+ import { TuiDriverDirective, TuiRectAccessor, TuiPositionAccessor, tuiProvideAccessor, tuiAsRectAccessor, tuiInjectAccessor, tuiAsPositionAccessor, tuiAsVehicle, tuiAsDriver, TuiDriver } from '@taiga-ui/core/classes';
9
9
  import { TuiPopupService } from '@taiga-ui/core/portals/popup';
10
- import { tuiCheckFixedPosition, tuiGetWordRange } from '@taiga-ui/core/utils/dom';
10
+ import { tuiCheckFixedPosition, tuiAnchorDelegate, tuiGetWordRange } from '@taiga-ui/core/utils/dom';
11
11
  import { PolymorpheusOutlet, PolymorpheusComponent, PolymorpheusTemplate } from '@taiga-ui/polymorpheus';
12
- import { BehaviorSubject, Subject, distinctUntilChanged, takeWhile, map, throttleTime, merge, filter, EMPTY, switchMap, delay, startWith, takeUntil, fromEvent, of, tap, share, combineLatest } from 'rxjs';
12
+ import { BehaviorSubject, takeWhile, map, Subject, distinctUntilChanged, throttleTime, merge, filter, EMPTY, switchMap, delay, startWith, takeUntil, fromEvent, of, tap, share, combineLatest } from 'rxjs';
13
13
  import * as i1 from '@taiga-ui/cdk/directives/active-zone';
14
14
  import { TuiActiveZone } from '@taiga-ui/cdk/directives/active-zone';
15
15
  import * as i2 from '@taiga-ui/cdk/directives/animated';
16
16
  import { TuiAnimated } from '@taiga-ui/cdk/directives/animated';
17
- import { TuiScrollbar } from '@taiga-ui/core/components/scrollbar';
18
- import { TUI_VIEWPORT, TUI_DARK_MODE, TUI_SELECTION_STREAM } from '@taiga-ui/core/tokens';
17
+ import { TuiScrollbar, TuiScrollControls, TUI_SCROLL_REF } from '@taiga-ui/core/components/scrollbar';
18
+ import { TUI_VIEWPORT, TUI_ANCHOR_SUPPORT, TUI_DARK_MODE, TUI_SELECTION_STREAM } from '@taiga-ui/core/tokens';
19
19
  import { EMPTY_CLIENT_RECT, TUI_TRUE_HANDLER, CHAR_ZERO_WIDTH_SPACE, CHAR_NO_BREAK_SPACE } from '@taiga-ui/cdk/constants';
20
20
  import { tuiClamp } from '@taiga-ui/cdk/utils/math';
21
21
  import { tuiPx, tuiGenerateId, tuiSetSignal, tuiIsString } from '@taiga-ui/cdk/utils/miscellaneous';
@@ -99,102 +99,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
99
99
  }]
100
100
  }] });
101
101
 
102
- class TuiDropdownPosition extends TuiPositionAccessor {
103
- constructor() {
104
- super(...arguments);
105
- this.el = tuiInjectElement();
106
- this.options = inject(TUI_DROPDOWN_OPTIONS);
107
- this.viewport = inject(TUI_VIEWPORT);
108
- this.direction = new Subject();
109
- this.type = 'dropdown';
110
- this.accessor = tuiFallbackAccessor('dropdown')(inject(TuiRectAccessor, { self: true, optional: true }), { getClientRect: () => this.el.getBoundingClientRect() });
111
- this.tuiDropdownDirectionChange = outputFromObservable(this.direction.pipe(distinctUntilChanged()));
112
- }
113
- getPosition({ width, height }) {
114
- if (!width && !height) {
115
- this.previous = undefined;
116
- }
117
- const hostRect = this.accessor.getClientRect();
118
- const viewportRect = this.viewport.getClientRect();
119
- const { minHeight, direction, offset, limitWidth } = this.options;
120
- const align = this.getAlign(this.options.align);
121
- const viewport = {
122
- top: viewportRect.top - offset,
123
- bottom: viewportRect.bottom + offset,
124
- right: viewportRect.right - offset,
125
- left: viewportRect.left + offset,
126
- };
127
- const previous = this.previous || direction || 'bottom';
128
- const available = {
129
- top: hostRect.top - 2 * offset - viewport.top,
130
- bottom: viewport.bottom - hostRect.bottom - 2 * offset,
131
- };
132
- const rectWidth = limitWidth === 'fixed' ? hostRect.width : width;
133
- const right = Math.max(hostRect.right - rectWidth, offset);
134
- const left = hostRect.left + width < viewport.right ? hostRect.left : right;
135
- const position = {
136
- top: hostRect.top - offset - height,
137
- bottom: hostRect.bottom + offset,
138
- right: Math.max(viewport.left, right),
139
- center: hostRect.left + hostRect.width / 2 + width / 2 < viewport.right
140
- ? hostRect.left + hostRect.width / 2 - width / 2
141
- : right,
142
- left: Math.max(viewport.left, left),
143
- };
144
- const better = available.top > available.bottom ? 'top' : 'bottom';
145
- if ((available[previous] > minHeight && direction) ||
146
- available[previous] > height) {
147
- this.direction.next(previous);
148
- return [position[align], position[previous]];
149
- }
150
- this.previous = better;
151
- this.direction.next(better);
152
- return [position[align], position[better]];
153
- }
154
- getAlign(align) {
155
- const rtl = this.el.matches('[dir="rtl"] :scope');
156
- if (rtl && align === 'start') {
157
- return 'right';
158
- }
159
- if (rtl && align === 'end') {
160
- return 'left';
161
- }
162
- if (align === 'center') {
163
- return 'center';
164
- }
165
- return align === 'end' ? 'right' : 'left';
166
- }
167
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownPosition, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
168
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: TuiDropdownPosition, isStandalone: true, outputs: { tuiDropdownDirectionChange: "tuiDropdownDirectionChange" }, usesInheritance: true, ngImport: i0 }); }
169
- }
170
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownPosition, decorators: [{
171
- type: Directive
172
- }] });
173
-
174
102
  const MAX_WIDTH_GAP = 16; // 8px min gap from each side
175
103
  class TuiDropdownAnchored {
176
104
  constructor() {
177
105
  this.el = tuiInjectElement();
178
106
  this.directive = inject(TuiDropdownDirective);
179
- this.accessor = this.directive.accessor;
107
+ this.rect = inject(TuiRectAccessor);
180
108
  this.viewport = inject(TUI_VIEWPORT);
181
109
  this.vvs = inject(TuiVisualViewportService);
182
110
  this.options = inject(TUI_DROPDOWN_OPTIONS);
183
111
  this.position = this.directive.position;
112
+ this.accessor = inject(TuiPositionAccessor);
184
113
  this.styles$ = inject(TuiPositionService).pipe(takeWhile(() => this.directive.el.isConnected &&
185
114
  this.directive.el.getClientRects().length > 0), map((v) => (this.position === 'fixed' ? this.vvs.correct(v) : v)), map((point) => this.getStyles(...point)), takeUntilDestroyed());
115
+ this.anchored = inject(TUI_ANCHOR_SUPPORT) &&
116
+ 'position' in this.accessor &&
117
+ this.viewport.type === 'window';
186
118
  }
187
119
  ngAfterViewInit() {
188
- this.styles$.subscribe({
189
- next: (styles) => Object.assign(this.el.style, styles),
190
- complete: () => this.directive.toggle(false),
191
- });
120
+ if (this.anchored) {
121
+ this.accessor.position?.(this.el);
122
+ }
123
+ else {
124
+ this.styles$.subscribe({
125
+ next: (styles) => Object.assign(this.el.style, styles),
126
+ complete: () => this.directive.toggle(false),
127
+ });
128
+ }
129
+ }
130
+ // https://github.com/w3c/csswg-drafts/issues/14112
131
+ retrigger() {
132
+ if (!this.anchored ||
133
+ this.el.scrollHeight === this.el.clientHeight ||
134
+ this.el.clientHeight === this.options.maxHeight) {
135
+ return;
136
+ }
137
+ this.el.style.position = 'static';
138
+ void this.el.offsetHeight;
139
+ this.el.style.position = 'fixed';
192
140
  }
193
141
  getStyles(x, y) {
194
142
  const { maxHeight, minHeight, offset, limitWidth } = this.options;
195
143
  const parent = this.el.offsetParent?.getBoundingClientRect() || EMPTY_CLIENT_RECT;
196
144
  const { left = 0, top = 0 } = this.position === 'fixed' ? {} : parent;
197
- const rect = this.accessor.getClientRect();
145
+ const rect = this.rect.getClientRect();
198
146
  const viewport = this.viewport.getClientRect();
199
147
  const zoom = this.directive.el.currentCSSZoom || 1;
200
148
  const above = rect.top - viewport.top - 2 * offset;
@@ -216,10 +164,10 @@ class TuiDropdownAnchored {
216
164
  };
217
165
  }
218
166
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownAnchored, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
219
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: TuiDropdownAnchored, isStandalone: true, providers: [
167
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: TuiDropdownAnchored, isStandalone: true, host: { listeners: { "window:scrollend.zoneless.capture": "retrigger()" }, properties: { "class._anchored": "anchored" } }, providers: [
220
168
  TuiPositionService,
221
- tuiPositionAccessorFor('dropdown', TuiDropdownPosition),
222
- tuiRectAccessorFor('dropdown', forwardRef(() => TuiDropdownDirective)),
169
+ tuiProvideAccessor(TuiPositionAccessor, 'dropdown'),
170
+ tuiProvideAccessor(TuiRectAccessor, 'dropdown'),
223
171
  ], ngImport: i0 }); }
224
172
  }
225
173
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownAnchored, decorators: [{
@@ -227,19 +175,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
227
175
  args: [{
228
176
  providers: [
229
177
  TuiPositionService,
230
- tuiPositionAccessorFor('dropdown', TuiDropdownPosition),
231
- tuiRectAccessorFor('dropdown', forwardRef(() => TuiDropdownDirective)),
178
+ tuiProvideAccessor(TuiPositionAccessor, 'dropdown'),
179
+ tuiProvideAccessor(TuiRectAccessor, 'dropdown'),
232
180
  ],
181
+ host: {
182
+ '[class._anchored]': 'anchored',
183
+ '(window:scrollend.zoneless.capture)': 'retrigger()',
184
+ },
233
185
  }]
234
186
  }] });
235
187
 
236
188
  /**
237
- * @description:
238
- * This component is used to show template in a portal
239
- * using default style of white rounded box with a shadow
189
+ * TODO: Remove extends TuiScrollbar in v6 when TuiScrollable is dropped
240
190
  */
241
- class TuiDropdownComponent {
191
+ class TuiDropdownComponent extends TuiScrollbar {
242
192
  constructor() {
193
+ super(...arguments);
243
194
  this.options = inject(TUI_DROPDOWN_OPTIONS);
244
195
  this.directive = inject(TuiDropdownDirective);
245
196
  this.context = inject(TUI_DROPDOWN_CONTEXT, { optional: true });
@@ -247,15 +198,48 @@ class TuiDropdownComponent {
247
198
  this.theme = computed((_ = this.darkMode()) => this.directive.el.closest('[tuiTheme]')?.getAttribute('tuiTheme'));
248
199
  this.close = () => this.directive.toggle(false);
249
200
  }
250
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
251
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: TuiDropdownComponent, isStandalone: true, selector: "tui-dropdown", host: { properties: { "attr.data-appearance": "options.appearance", "attr.tuiTheme": "theme()" } }, hostDirectives: [{ directive: i1.TuiActiveZone }, { directive: i2.TuiAnimated }, { directive: TuiDropdownAnchored }], ngImport: i0, template: "<tui-scrollbar class=\"t-scroll\">\n <div\n *polymorpheusOutlet=\"directive.content() as text; context: {$implicit: close}\"\n class=\"t-primitive\"\n >\n {{ text }}\n </div>\n</tui-scrollbar>\n", styles: [":host{position:absolute;display:flex;box-shadow:var(--tui-shadow-medium);color:var(--tui-text-primary);background:var(--tui-background-elevation-1);border-radius:var(--tui-radius-m);overflow:hidden;border:1px solid var(--tui-border-normal);box-sizing:border-box;isolation:isolate;pointer-events:auto;--tui-from: translateY(-1rem)}:host:has(tui-data-list[data-size=l]){border-radius:var(--tui-radius-l)}:host.tui-enter,:host.tui-leave{animation-name:tuiFade,tuiSlide;animation-duration:calc(var(--tui-duration) / 2);pointer-events:none}:host:not([style*=top]){visibility:hidden}.t-scroll{flex-grow:1;max-inline-size:calc(100% + 2px);inline-size:max-content;overscroll-behavior:none;margin:-1px}.t-primitive{padding:1rem}\n"], dependencies: [{ kind: "directive", type: PolymorpheusOutlet, selector: "[polymorpheusOutlet]", inputs: ["polymorpheusOutlet", "polymorpheusOutletContext"] }, { kind: "component", type: TuiScrollbar, selector: "tui-scrollbar" }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
201
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
202
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: TuiDropdownComponent, isStandalone: true, selector: "tui-dropdown", host: { properties: { "attr.data-appearance": "options.appearance", "attr.tuiTheme": "theme()", "style.max-block-size.px": "options.maxHeight" } }, providers: [
203
+ {
204
+ provide: TUI_SCROLL_REF,
205
+ useFactory: () => inject(TuiDropdownComponent).browserScrollRef,
206
+ },
207
+ ], usesInheritance: true, hostDirectives: [{ directive: i1.TuiActiveZone }, { directive: i2.TuiAnimated }, { directive: TuiDropdownAnchored }], ngImport: i0, template: `
208
+ <tui-scroll-controls class="t-scrollbar" />
209
+ <div class="t-wrapper">
210
+ <div
211
+ *polymorpheusOutlet="
212
+ directive.content() as text;
213
+ context: {$implicit: close}
214
+ "
215
+ [innerHTML]="text"
216
+ ></div>
217
+ </div>
218
+ `, isInline: true, styles: [":host{position:absolute;display:flex;box-shadow:var(--tui-shadow-medium);color:var(--tui-text-primary);background:var(--tui-background-elevation-1);border-radius:var(--tui-radius-m);overflow:auto;isolation:isolate;pointer-events:auto;outline-offset:-1px;outline:1px solid color-mix(in srgb,rgb(from var(--tui-border-normal) r g b / 1) 12%,var(--tui-background-elevation-1));overscroll-behavior:none;position-try-fallbacks:flip-block,flip-inline,flip-block flip-inline;--tui-from: translateY(-1rem)}:host:has(tui-data-list[data-size=l]){border-radius:var(--tui-radius-l)}:host.tui-enter,:host.tui-leave{animation-name:tuiFade,tuiSlide;animation-duration:calc(var(--tui-duration) / 2);pointer-events:none}:host:not([style*=top]){visibility:hidden}:host._delegated{scroll-timeline:none}.t-wrapper{z-index:-1;flex:1}.t-wrapper>*{padding:1rem}@supports (anchor-name: --t-scrollbar){.t-scrollbar{position:fixed;inset:anchor(top) anchor(right) anchor(bottom) anchor(left);position-anchor:--t-scrollbar;min-inline-size:0;min-block-size:0;margin:0}}\n"], dependencies: [{ kind: "directive", type: PolymorpheusOutlet, selector: "[polymorpheusOutlet]", inputs: ["polymorpheusOutlet", "polymorpheusOutletContext"] }, { kind: "component", type: TuiScrollControls, selector: "tui-scroll-controls" }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
252
219
  }
253
220
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownComponent, decorators: [{
254
221
  type: Component,
255
- args: [{ selector: 'tui-dropdown', imports: [PolymorpheusOutlet, TuiScrollbar], changeDetection: ChangeDetectionStrategy.Default, hostDirectives: [TuiActiveZone, TuiAnimated, TuiDropdownAnchored], host: {
222
+ args: [{ selector: 'tui-dropdown', imports: [PolymorpheusOutlet, TuiScrollControls], template: `
223
+ <tui-scroll-controls class="t-scrollbar" />
224
+ <div class="t-wrapper">
225
+ <div
226
+ *polymorpheusOutlet="
227
+ directive.content() as text;
228
+ context: {$implicit: close}
229
+ "
230
+ [innerHTML]="text"
231
+ ></div>
232
+ </div>
233
+ `, changeDetection: ChangeDetectionStrategy.Default, providers: [
234
+ {
235
+ provide: TUI_SCROLL_REF,
236
+ useFactory: () => inject(TuiDropdownComponent).browserScrollRef,
237
+ },
238
+ ], hostDirectives: [TuiActiveZone, TuiAnimated, TuiDropdownAnchored], host: {
256
239
  '[attr.data-appearance]': 'options.appearance',
257
240
  '[attr.tuiTheme]': 'theme()',
258
- }, template: "<tui-scrollbar class=\"t-scroll\">\n <div\n *polymorpheusOutlet=\"directive.content() as text; context: {$implicit: close}\"\n class=\"t-primitive\"\n >\n {{ text }}\n </div>\n</tui-scrollbar>\n", styles: [":host{position:absolute;display:flex;box-shadow:var(--tui-shadow-medium);color:var(--tui-text-primary);background:var(--tui-background-elevation-1);border-radius:var(--tui-radius-m);overflow:hidden;border:1px solid var(--tui-border-normal);box-sizing:border-box;isolation:isolate;pointer-events:auto;--tui-from: translateY(-1rem)}:host:has(tui-data-list[data-size=l]){border-radius:var(--tui-radius-l)}:host.tui-enter,:host.tui-leave{animation-name:tuiFade,tuiSlide;animation-duration:calc(var(--tui-duration) / 2);pointer-events:none}:host:not([style*=top]){visibility:hidden}.t-scroll{flex-grow:1;max-inline-size:calc(100% + 2px);inline-size:max-content;overscroll-behavior:none;margin:-1px}.t-primitive{padding:1rem}\n"] }]
241
+ '[style.max-block-size.px]': 'options.maxHeight',
242
+ }, styles: [":host{position:absolute;display:flex;box-shadow:var(--tui-shadow-medium);color:var(--tui-text-primary);background:var(--tui-background-elevation-1);border-radius:var(--tui-radius-m);overflow:auto;isolation:isolate;pointer-events:auto;outline-offset:-1px;outline:1px solid color-mix(in srgb,rgb(from var(--tui-border-normal) r g b / 1) 12%,var(--tui-background-elevation-1));overscroll-behavior:none;position-try-fallbacks:flip-block,flip-inline,flip-block flip-inline;--tui-from: translateY(-1rem)}:host:has(tui-data-list[data-size=l]){border-radius:var(--tui-radius-l)}:host.tui-enter,:host.tui-leave{animation-name:tuiFade,tuiSlide;animation-duration:calc(var(--tui-duration) / 2);pointer-events:none}:host:not([style*=top]){visibility:hidden}:host._delegated{scroll-timeline:none}.t-wrapper{z-index:-1;flex:1}.t-wrapper>*{padding:1rem}@supports (anchor-name: --t-scrollbar){.t-scrollbar{position:fixed;inset:anchor(top) anchor(right) anchor(bottom) anchor(left);position-anchor:--t-scrollbar;min-inline-size:0;min-block-size:0;margin:0}}\n"] }]
259
243
  }] });
260
244
 
261
245
  /**
@@ -297,20 +281,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
297
281
  args: [{ selector: '[tuiDropdownA11y]' }]
298
282
  }] });
299
283
 
300
- class TuiDropdownAnchor {
284
+ class TuiDropdownAnchor extends TuiRectAccessor {
301
285
  constructor() {
286
+ super(...arguments);
287
+ this.el = tuiInjectElement();
302
288
  this.anchor = `--${tuiGenerateId()}`;
289
+ this.type = 'dropdown';
303
290
  }
304
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownAnchor, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
291
+ getClientRect() {
292
+ return this.el.getBoundingClientRect();
293
+ }
294
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownAnchor, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
305
295
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: TuiDropdownAnchor, isStandalone: true, host: { properties: { "attr.data-tui-anchor": "anchor", "style.anchor-name": "anchor" } }, providers: [
296
+ tuiAsRectAccessor(TuiDropdownAnchor),
306
297
  tuiProvide(TUI_DROPDOWN_HOST, ElementRef),
307
298
  tuiProvide(TUI_DROPDOWN_ANCHOR, ElementRef),
308
- ], ngImport: i0 }); }
299
+ ], usesInheritance: true, ngImport: i0 }); }
309
300
  }
310
301
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownAnchor, decorators: [{
311
302
  type: Directive,
312
303
  args: [{
313
304
  providers: [
305
+ tuiAsRectAccessor(TuiDropdownAnchor),
314
306
  tuiProvide(TUI_DROPDOWN_HOST, ElementRef),
315
307
  tuiProvide(TUI_DROPDOWN_ANCHOR, ElementRef),
316
308
  ],
@@ -321,6 +313,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
321
313
  }]
322
314
  }] });
323
315
 
316
+ class TuiDropdownPosition extends TuiPositionAccessor {
317
+ constructor() {
318
+ super(...arguments);
319
+ this.el = tuiInjectElement();
320
+ this.options = inject(TUI_DROPDOWN_OPTIONS);
321
+ this.viewport = inject(TUI_VIEWPORT);
322
+ this.anchor = inject(TUI_DROPDOWN_ANCHOR);
323
+ this.direction = new Subject();
324
+ this.type = 'dropdown';
325
+ this.accessor = tuiInjectAccessor(TuiRectAccessor, 'dropdown');
326
+ this.tuiDropdownDirectionChange = outputFromObservable(this.direction.pipe(distinctUntilChanged()));
327
+ }
328
+ position(element) {
329
+ const { direction, align, offset, limitWidth, minHeight, maxHeight } = this.options;
330
+ const rect = this.anchor.nativeElement.getBoundingClientRect();
331
+ const viewport = this.viewport.getClientRect();
332
+ const horizontal = align === 'center' ? '' : `span-x-${invert(align)}`;
333
+ const height = Math.min(element.clientHeight, maxHeight) + offset * 2;
334
+ const vertical = direction || 'bottom';
335
+ const top = rect.top - viewport.top;
336
+ const bottom = viewport.bottom - rect.bottom;
337
+ const available = vertical === 'top' ? top : bottom;
338
+ const max = Math.max(top, bottom);
339
+ Object.assign(element.style, {
340
+ position: 'fixed',
341
+ visibility: 'visible',
342
+ positionAnchor: this.anchor.nativeElement.dataset.tuiAnchor,
343
+ positionArea: `${available < height && max !== available ? flip(vertical) : vertical} ${horizontal}`,
344
+ marginBlock: `${offset}px`,
345
+ minBlockSize: `calc-size(fit-content, min(size, ${minHeight}px))`,
346
+ maxBlockSize: `calc-size(fit-content, min(size, ${maxHeight}px))`,
347
+ minInlineSize: limitWidth === 'min' ? 'anchor-size(inline)' : '',
348
+ inlineSize: limitWidth === 'fixed' ? 'anchor-size(inline)' : '',
349
+ blockSize: `calc-size(fit-content, calc(100% - ${2 * offset}px))`,
350
+ });
351
+ }
352
+ getPosition({ width, height }) {
353
+ if (!width && !height) {
354
+ this.previous = undefined;
355
+ }
356
+ const hostRect = this.accessor.getClientRect();
357
+ const viewportRect = this.viewport.getClientRect();
358
+ const { minHeight, direction, offset, limitWidth } = this.options;
359
+ const align = this.getAlign(this.options.align);
360
+ const previous = this.previous || direction || 'bottom';
361
+ const viewport = {
362
+ top: viewportRect.top - offset,
363
+ bottom: viewportRect.bottom + offset,
364
+ right: viewportRect.right - offset,
365
+ left: viewportRect.left + offset,
366
+ };
367
+ const available = {
368
+ top: hostRect.top - 2 * offset - viewport.top,
369
+ bottom: viewport.bottom - hostRect.bottom - 2 * offset,
370
+ };
371
+ const rectWidth = limitWidth === 'fixed' ? hostRect.width : width;
372
+ const right = Math.max(hostRect.right - rectWidth, offset);
373
+ const left = hostRect.left + width < viewport.right ? hostRect.left : right;
374
+ const better = available.top > available.bottom ? 'top' : 'bottom';
375
+ const position = {
376
+ top: hostRect.top - offset - height,
377
+ bottom: hostRect.bottom + offset,
378
+ right: Math.max(viewport.left, right),
379
+ center: hostRect.left + hostRect.width / 2 + width / 2 < viewport.right
380
+ ? hostRect.left + hostRect.width / 2 - width / 2
381
+ : right,
382
+ left: Math.max(viewport.left, left),
383
+ };
384
+ if ((available[previous] > minHeight && direction) ||
385
+ available[previous] > height) {
386
+ this.direction.next(previous);
387
+ return [position[align], position[previous]];
388
+ }
389
+ this.previous = better;
390
+ this.direction.next(better);
391
+ return [position[align], position[better]];
392
+ }
393
+ getAlign(align) {
394
+ const rtl = this.el.matches('[dir="rtl"] :scope');
395
+ if (rtl && align === 'start') {
396
+ return 'right';
397
+ }
398
+ if (rtl && align === 'end') {
399
+ return 'left';
400
+ }
401
+ if (align === 'center') {
402
+ return 'center';
403
+ }
404
+ return align === 'end' ? 'right' : 'left';
405
+ }
406
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownPosition, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
407
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: TuiDropdownPosition, isStandalone: true, outputs: { tuiDropdownDirectionChange: "tuiDropdownDirectionChange" }, providers: [tuiAsPositionAccessor(TuiDropdownPosition)], usesInheritance: true, ngImport: i0 }); }
408
+ }
409
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownPosition, decorators: [{
410
+ type: Directive,
411
+ args: [{ providers: [tuiAsPositionAccessor(TuiDropdownPosition)] }]
412
+ }] });
413
+ // TODO: Review in v6 to possible sync alignment with native anchors
414
+ function invert(align) {
415
+ return align === 'start' ? 'end' : 'start';
416
+ }
417
+ function flip(direction) {
418
+ return direction === 'top' ? 'bottom' : 'top';
419
+ }
420
+
324
421
  class TuiDropdownDirective {
325
422
  constructor() {
326
423
  this.injector = inject(INJECTOR);
@@ -350,11 +447,12 @@ class TuiDropdownDirective {
350
447
  : content,
351
448
  });
352
449
  }
450
+ /** @deprecated remove in v6 */
353
451
  get accessor() {
354
452
  const accessors = this.injector.get(TuiRectAccessor, null, {
355
453
  self: true,
356
454
  });
357
- return tuiFallbackAccessor('dropdown')(accessors, this);
455
+ return ([...(accessors || [])].reverse().find(({ type }) => type === 'dropdown') || this);
358
456
  }
359
457
  get position() {
360
458
  return tuiCheckFixedPosition(this.el) ? 'fixed' : 'absolute';
@@ -394,10 +492,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
394
492
  TuiDropdownAnchor,
395
493
  TuiDropdownDriverDirective,
396
494
  { directive: TuiDropdownA11y, inputs: ['tuiDropdownRole'] },
397
- {
398
- directive: TuiDropdownPosition,
399
- outputs: ['tuiDropdownDirectionChange'],
400
- },
495
+ { directive: TuiDropdownPosition, outputs: ['tuiDropdownDirectionChange'] },
401
496
  ],
402
497
  host: { '[class.tui-dropdown-open]': 'ref()' },
403
498
  }]
@@ -578,12 +673,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
578
673
  args: [{ selector: 'ng-template[tuiDropdown]' }]
579
674
  }], ctorParameters: () => [] });
580
675
 
581
- const STYLE = {
582
- position: 'fixed',
583
- blockSize: '1px',
584
- inlineSize: '1px',
585
- pointerEvents: 'none',
586
- };
587
676
  class TuiDropdownContext extends TuiRectAccessor {
588
677
  constructor() {
589
678
  super(...arguments);
@@ -606,14 +695,12 @@ class TuiDropdownContext extends TuiRectAccessor {
606
695
  this.currentRect = EMPTY_CLIENT_RECT;
607
696
  });
608
697
  this.type = 'dropdown';
609
- this.nativeElement = this.doc.createElement('div');
610
- }
611
- ngAfterViewInit() {
612
- const anchorName = `--${tuiGenerateId()}`;
613
- const positionAnchor = this.el.getAttribute('data-tui-anchor');
614
- const style = { ...STYLE, positionAnchor, anchorName };
615
- Object.assign(this.nativeElement.style, style);
616
- this.nativeElement.setAttribute('data-tui-anchor', anchorName);
698
+ this.nativeElement = tuiAnchorDelegate({
699
+ width: '1px',
700
+ height: '1px',
701
+ top: 'calc(anchor(top)',
702
+ left: 'calc(anchor(left)',
703
+ });
617
704
  }
618
705
  ngOnDestroy() {
619
706
  this.nativeElement.parentNode?.removeChild(this.nativeElement);
@@ -626,7 +713,6 @@ class TuiDropdownContext extends TuiRectAccessor {
626
713
  this.currentRect = tuiPointToClientRect(x, y);
627
714
  this.nativeElement.style.top = `calc(anchor(top) + ${y - top}px)`;
628
715
  this.nativeElement.style.left = `calc(anchor(left) + ${x - left}px)`;
629
- this.doc.body.appendChild(this.nativeElement);
630
716
  this.driver.next(true);
631
717
  }
632
718
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownContext, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
@@ -768,23 +854,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
768
854
  class TuiDropdownPositionSided extends TuiPositionAccessor {
769
855
  constructor() {
770
856
  super(...arguments);
857
+ this.anchor = inject(TUI_DROPDOWN_ANCHOR, { optional: true });
771
858
  this.options = inject(TUI_DROPDOWN_OPTIONS);
772
859
  this.viewport = inject(TUI_VIEWPORT);
773
- this.vertical = inject(TuiDropdownPosition);
860
+ this.vertical = inject(TuiDropdownPosition, { optional: true });
774
861
  this.previous = this.options.direction || 'bottom';
775
862
  this.tuiDropdownSided = input('');
776
863
  this.tuiDropdownSidedOffset = input(4);
777
864
  this.type = 'dropdown';
778
865
  }
779
- getPosition(rect) {
866
+ position(element) {
780
867
  if (this.tuiDropdownSided() === false) {
868
+ this.vertical?.position(element);
869
+ return;
870
+ }
871
+ const { direction, align, offset, minHeight, maxHeight } = this.options;
872
+ const horizontal = align === 'center' ? 'end' : align;
873
+ Object.assign(element.style, {
874
+ position: 'fixed',
875
+ visibility: 'visible',
876
+ positionAnchor: this.anchor?.nativeElement.dataset.tuiAnchor,
877
+ positionArea: `x-${horizontal} span-${direction || 'bottom'}`,
878
+ minBlockSize: `calc-size(fit-content, min(size, ${minHeight}px))`,
879
+ maxBlockSize: `min(calc-size(fit-content, size), ${maxHeight}px)`,
880
+ margin: `${-offset}px ${offset}px`,
881
+ });
882
+ }
883
+ getPosition(rect) {
884
+ if (this.tuiDropdownSided() === false && this.vertical) {
781
885
  return this.vertical.getPosition(rect);
782
886
  }
783
887
  const { height, width } = rect;
784
- const hostRect = this.vertical.accessor?.getClientRect() ?? EMPTY_CLIENT_RECT;
888
+ const hostRect = this.vertical?.accessor.getClientRect() ?? EMPTY_CLIENT_RECT;
785
889
  const viewport = this.viewport.getClientRect();
786
890
  const { direction, offset } = this.options;
787
- const adjusted = this.vertical.getAlign(this.options.align);
891
+ const adjusted = this.vertical?.getAlign(this.options.align) || 'center';
788
892
  const align = adjusted === 'center' ? 'left' : adjusted;
789
893
  const available = {
790
894
  top: hostRect.bottom - viewport.top,
@@ -803,21 +907,21 @@ class TuiDropdownPositionSided extends TuiPositionAccessor {
803
907
  const left = available[align] > width ? position[align] : maxLeft;
804
908
  if ((available[this.previous] > height && direction) ||
805
909
  this.previous === better) {
806
- this.vertical.direction.next(this.previous);
910
+ this.vertical?.direction.next(this.previous);
807
911
  return [left, position[this.previous]];
808
912
  }
809
913
  this.previous = better;
810
- this.vertical.direction.next(better);
914
+ this.vertical?.direction.next(better);
811
915
  return [left, position[better]];
812
916
  }
813
917
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownPositionSided, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
814
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.25", type: TuiDropdownPositionSided, isStandalone: true, selector: "[tuiDropdownSided]", inputs: { tuiDropdownSided: { classPropertyName: "tuiDropdownSided", publicName: "tuiDropdownSided", isSignal: true, isRequired: false, transformFunction: null }, tuiDropdownSidedOffset: { classPropertyName: "tuiDropdownSidedOffset", publicName: "tuiDropdownSidedOffset", isSignal: true, isRequired: false, transformFunction: null } }, providers: [TuiDropdownPosition, tuiAsPositionAccessor(TuiDropdownPositionSided)], usesInheritance: true, ngImport: i0 }); }
918
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.25", type: TuiDropdownPositionSided, isStandalone: true, selector: "[tuiDropdownSided]", inputs: { tuiDropdownSided: { classPropertyName: "tuiDropdownSided", publicName: "tuiDropdownSided", isSignal: true, isRequired: false, transformFunction: null }, tuiDropdownSidedOffset: { classPropertyName: "tuiDropdownSidedOffset", publicName: "tuiDropdownSidedOffset", isSignal: true, isRequired: false, transformFunction: null } }, providers: [tuiAsPositionAccessor(TuiDropdownPositionSided)], usesInheritance: true, ngImport: i0 }); }
815
919
  }
816
920
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiDropdownPositionSided, decorators: [{
817
921
  type: Directive,
818
922
  args: [{
819
923
  selector: '[tuiDropdownSided]',
820
- providers: [TuiDropdownPosition, tuiAsPositionAccessor(TuiDropdownPositionSided)],
924
+ providers: [tuiAsPositionAccessor(TuiDropdownPositionSided)],
821
925
  }]
822
926
  }] });
823
927
 
@@ -847,27 +951,17 @@ class TuiDropdownSelection extends TuiDriver {
847
951
  const visible = valid || this.inDropdown(range);
848
952
  const focus = tuiGetFocused(this.doc);
849
953
  const textfield = focus && tuiIsTextfield(focus) && this.el.contains(focus);
954
+ const show = visible && textfield ? this.isCaretVisible(this.range) : visible;
850
955
  this.updateAnchor();
851
- return visible && textfield ? this.isCaretVisible(this.range) : visible;
956
+ return show;
852
957
  }));
853
958
  this.range = isPlatformBrowser(inject(PLATFORM_ID))
854
959
  ? new Range()
855
960
  : {};
856
961
  this.type = 'dropdown';
857
- this.nativeElement = this.doc.createElement('div');
962
+ this.nativeElement = tuiAnchorDelegate();
858
963
  this.tuiDropdownSelection = input('');
859
964
  this.tuiDropdownSelectionPosition = input('selection');
860
- afterNextRender(() => {
861
- const anchorName = `--${tuiGenerateId()}`;
862
- Object.assign(this.nativeElement.style, {
863
- position: 'fixed',
864
- pointerEvents: 'none',
865
- positionAnchor: this.el.dataset.tuiAnchor,
866
- anchorName,
867
- });
868
- this.nativeElement.dataset.tuiAnchor = anchorName;
869
- this.doc.body.appendChild(this.nativeElement);
870
- });
871
965
  }
872
966
  ngOnDestroy() {
873
967
  this.nativeElement.parentNode?.removeChild(this.nativeElement);
@@ -929,12 +1023,11 @@ class TuiDropdownSelection extends TuiDriver {
929
1023
  }
930
1024
  veryVerySadInputFix(element) {
931
1025
  const { ghost = this.initGhost(this.ghostHost) } = this;
932
- const { top, left, width, height } = this.ghostHost.getBoundingClientRect();
1026
+ const { width, height } = this.ghostHost.getBoundingClientRect();
933
1027
  const { selectionStart, selectionEnd, value } = element;
934
1028
  const range = this.doc.createRange();
935
- const hostRect = this.ghostHost.getBoundingClientRect();
936
- ghost.style.top = tuiPx(top - hostRect.top);
937
- ghost.style.left = tuiPx(left - hostRect.left);
1029
+ ghost.style.top = '0px';
1030
+ ghost.style.left = '0px';
938
1031
  ghost.style.width = tuiPx(width);
939
1032
  ghost.style.height = tuiPx(height);
940
1033
  ghost.textContent = `${CHAR_ZERO_WIDTH_SPACE}${value}${CHAR_NO_BREAK_SPACE}`;