@aquera/nile-elements 1.5.9 → 1.6.1

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 (111) hide show
  1. package/README.md +6 -0
  2. package/dist/index.cjs.js +1 -1
  3. package/dist/index.esm.js +1 -1
  4. package/dist/index.js +382 -227
  5. package/dist/nile-floating-panel/index.cjs.js +1 -1
  6. package/dist/nile-floating-panel/index.esm.js +1 -1
  7. package/dist/nile-floating-panel/nile-floating-panel.cjs.js +1 -1
  8. package/dist/nile-floating-panel/nile-floating-panel.cjs.js.map +1 -1
  9. package/dist/nile-floating-panel/nile-floating-panel.css.cjs.js +1 -1
  10. package/dist/nile-floating-panel/nile-floating-panel.css.cjs.js.map +1 -1
  11. package/dist/nile-floating-panel/nile-floating-panel.css.esm.js +137 -21
  12. package/dist/nile-floating-panel/nile-floating-panel.esm.js +1 -1
  13. package/dist/nile-lite-tooltip/index.cjs.js +1 -1
  14. package/dist/nile-lite-tooltip/index.esm.js +1 -1
  15. package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js +1 -1
  16. package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js.map +1 -1
  17. package/dist/nile-lite-tooltip/nile-lite-tooltip.esm.js +1 -1
  18. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js +1 -1
  19. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js.map +1 -1
  20. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js +1 -1
  21. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js.map +1 -1
  22. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.esm.js +24 -0
  23. package/dist/nile-rich-text-editor/nile-rich-text-editor.esm.js +1 -1
  24. package/dist/nile-rich-text-editor/nile-rte-link.cjs.js +1 -1
  25. package/dist/nile-rich-text-editor/nile-rte-link.cjs.js.map +1 -1
  26. package/dist/nile-rich-text-editor/nile-rte-link.esm.js +59 -44
  27. package/dist/nile-rich-text-editor/utils/inline-utils.cjs.js +1 -1
  28. package/dist/nile-rich-text-editor/utils/inline-utils.cjs.js.map +1 -1
  29. package/dist/nile-rich-text-editor/utils/inline-utils.esm.js +1 -1
  30. package/dist/src/index.d.ts +1 -1
  31. package/dist/src/index.js +1 -1
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/nile-floating-panel/index.js.map +1 -1
  34. package/dist/src/nile-floating-panel/nile-floating-panel.css.d.ts +1 -1
  35. package/dist/src/nile-floating-panel/nile-floating-panel.css.js +147 -20
  36. package/dist/src/nile-floating-panel/nile-floating-panel.css.js.map +1 -1
  37. package/dist/src/nile-floating-panel/nile-floating-panel.d.ts +90 -24
  38. package/dist/src/nile-floating-panel/nile-floating-panel.js +478 -159
  39. package/dist/src/nile-floating-panel/nile-floating-panel.js.map +1 -1
  40. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js +24 -0
  41. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js.map +1 -1
  42. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.d.ts +6 -0
  43. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js +62 -24
  44. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js.map +1 -1
  45. package/dist/src/nile-rich-text-editor/nile-rte-link.d.ts +12 -0
  46. package/dist/src/nile-rich-text-editor/nile-rte-link.js +239 -130
  47. package/dist/src/nile-rich-text-editor/nile-rte-link.js.map +1 -1
  48. package/dist/src/nile-rich-text-editor/nile-rte-link.test.d.ts +1 -0
  49. package/dist/src/nile-rich-text-editor/nile-rte-link.test.js +469 -0
  50. package/dist/src/nile-rich-text-editor/nile-rte-link.test.js.map +1 -0
  51. package/dist/src/nile-rich-text-editor/utils/inline-utils.js +13 -4
  52. package/dist/src/nile-rich-text-editor/utils/inline-utils.js.map +1 -1
  53. package/dist/src/version.js +1 -1
  54. package/dist/src/version.js.map +1 -1
  55. package/dist/tippy.esm-57628c2b.esm.js +1 -0
  56. package/dist/tippy.esm-78baa8f2.cjs.js +2 -0
  57. package/dist/tippy.esm-78baa8f2.cjs.js.map +1 -0
  58. package/dist/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +4 -3
  60. package/src/index.ts +2 -2
  61. package/src/nile-floating-panel/index.ts +0 -1
  62. package/src/nile-floating-panel/nile-floating-panel.css.ts +149 -21
  63. package/src/nile-floating-panel/nile-floating-panel.ts +489 -190
  64. package/src/nile-rich-text-editor/nile-rich-text-editor.css.ts +24 -0
  65. package/src/nile-rich-text-editor/nile-rich-text-editor.ts +66 -26
  66. package/src/nile-rich-text-editor/nile-rte-link.test.ts +682 -0
  67. package/src/nile-rich-text-editor/nile-rte-link.ts +195 -100
  68. package/src/nile-rich-text-editor/utils/inline-utils.ts +11 -4
  69. package/vscode-html-custom-data.json +229 -25
  70. package/dist/nile-floating-panel/anchor-manager.cjs.js +0 -2
  71. package/dist/nile-floating-panel/anchor-manager.cjs.js.map +0 -1
  72. package/dist/nile-floating-panel/anchor-manager.esm.js +0 -1
  73. package/dist/nile-floating-panel/content-manager.cjs.js +0 -2
  74. package/dist/nile-floating-panel/content-manager.cjs.js.map +0 -1
  75. package/dist/nile-floating-panel/content-manager.esm.js +0 -1
  76. package/dist/nile-floating-panel/event-manager.cjs.js +0 -2
  77. package/dist/nile-floating-panel/event-manager.cjs.js.map +0 -1
  78. package/dist/nile-floating-panel/event-manager.esm.js +0 -1
  79. package/dist/nile-floating-panel/position-manager.cjs.js +0 -2
  80. package/dist/nile-floating-panel/position-manager.cjs.js.map +0 -1
  81. package/dist/nile-floating-panel/position-manager.esm.js +0 -1
  82. package/dist/nile-floating-panel/style-manager.cjs.js +0 -2
  83. package/dist/nile-floating-panel/style-manager.cjs.js.map +0 -1
  84. package/dist/nile-floating-panel/style-manager.esm.js +0 -1
  85. package/dist/nile-floating-panel/types.cjs.js +0 -2
  86. package/dist/nile-floating-panel/types.cjs.js.map +0 -1
  87. package/dist/nile-floating-panel/types.esm.js +0 -1
  88. package/dist/src/nile-floating-panel/anchor-manager.d.ts +0 -6
  89. package/dist/src/nile-floating-panel/anchor-manager.js +0 -27
  90. package/dist/src/nile-floating-panel/anchor-manager.js.map +0 -1
  91. package/dist/src/nile-floating-panel/content-manager.d.ts +0 -5
  92. package/dist/src/nile-floating-panel/content-manager.js +0 -44
  93. package/dist/src/nile-floating-panel/content-manager.js.map +0 -1
  94. package/dist/src/nile-floating-panel/event-manager.d.ts +0 -14
  95. package/dist/src/nile-floating-panel/event-manager.js +0 -52
  96. package/dist/src/nile-floating-panel/event-manager.js.map +0 -1
  97. package/dist/src/nile-floating-panel/position-manager.d.ts +0 -17
  98. package/dist/src/nile-floating-panel/position-manager.js +0 -72
  99. package/dist/src/nile-floating-panel/position-manager.js.map +0 -1
  100. package/dist/src/nile-floating-panel/style-manager.d.ts +0 -9
  101. package/dist/src/nile-floating-panel/style-manager.js +0 -44
  102. package/dist/src/nile-floating-panel/style-manager.js.map +0 -1
  103. package/dist/src/nile-floating-panel/types.d.ts +0 -11
  104. package/dist/src/nile-floating-panel/types.js +0 -2
  105. package/dist/src/nile-floating-panel/types.js.map +0 -1
  106. package/src/nile-floating-panel/anchor-manager.ts +0 -33
  107. package/src/nile-floating-panel/content-manager.ts +0 -54
  108. package/src/nile-floating-panel/event-manager.ts +0 -74
  109. package/src/nile-floating-panel/position-manager.ts +0 -102
  110. package/src/nile-floating-panel/style-manager.ts +0 -54
  111. package/src/nile-floating-panel/types.ts +0 -15
@@ -1,229 +1,548 @@
1
+ /**
2
+ * Copyright Aquera Inc 2025
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
1
7
  var NileFloatingPanel_1;
2
8
  import { __decorate } from "tslib";
3
- import { html, } from 'lit';
4
9
  import { customElement, property } from 'lit/decorators.js';
5
10
  import { styles } from './nile-floating-panel.css';
6
11
  import NileElement from '../internal/nile-element';
7
- import { PositionManager } from './position-manager';
8
- import { StyleManager } from './style-manager';
9
- import { ContentManager } from './content-manager';
10
- import { AnchorManager } from './anchor-manager';
11
- import { EventManager } from './event-manager';
12
- import { VisibilityManager } from '../utilities/visibility-manager';
12
+ import tippy, { roundArrow, followCursor as followCursorPlugin, } from 'tippy.js';
13
+ import { parseFollowCursor, parseDuration, } from '../nile-lite-tooltip/utils';
14
+ import { VisibilityManager } from '../utilities/visibility-manager.js';
13
15
  /**
14
- * Nile floating panel component.
16
+ * Nile floating-panel component.
17
+ *
18
+ * A popover that supports rich content (title, body, actions).
19
+ *
20
+ * **Wrapper mode** (default): first child element is the trigger.
21
+ * **For mode**: set `for="elementId"` to attach to an external element.
15
22
  *
16
23
  * @tag nile-floating-panel
17
- * @event nile-show - Emitted when the panel opens.
18
- * @event nile-hide - Emitted when the panel closes.
24
+ *
25
+ * @fires nile-init - Component initialized.
26
+ * @fires nile-destroy - Component destroyed.
27
+ * @fires nile-show - Panel opened.
28
+ * @fires nile-hide - Panel closed.
29
+ * @fires nile-after-show - Panel fully visible after animation.
30
+ * @fires nile-after-hide - Panel fully hidden after animation.
31
+ * @fires nile-toggle - Open/close transition (detail.open).
32
+ * @fires nile-visibility-change - Hidden by scroll/tab change.
19
33
  */
20
34
  let NileFloatingPanel = NileFloatingPanel_1 = class NileFloatingPanel extends NileElement {
21
35
  constructor() {
22
36
  super(...arguments);
23
- this.anchor = null;
24
- this.position = 'bottom';
37
+ // ─── Tippy.js props ───
38
+ this.placement = 'bottom';
39
+ this.trigger = 'click';
40
+ this.distance = 12;
41
+ this.skidding = 0;
42
+ this.arrow = 'round';
43
+ this.animation = 'fade';
44
+ this.duration = 200;
45
+ this.delay = 0;
46
+ this.interactive = true;
47
+ this.interactiveBorder = 2;
48
+ this.maxWidth = 'none';
49
+ this.zIndex = 9999;
50
+ this.followCursor = false;
51
+ this.hideOnClick = true;
52
+ this.inertia = false;
53
+ this.allowHTML = false;
54
+ this.flip = true;
55
+ // ─── Popover-like props ───
56
+ this.for = null;
25
57
  this.open = false;
26
- this.closeOnOutsideClick = true;
27
- this.enableVisibilityEffect = true;
58
+ this.preventOverlayClose = false;
59
+ this.title = '';
60
+ this.disabled = false;
61
+ /** When set, only one panel in the same group can be open at a time. */
62
+ this.group = null;
63
+ /** Close the panel when Escape is pressed. */
64
+ this.closeOnEscape = true;
65
+ // ─── Visibility manager props ───
66
+ this.enableVisibilityEffect = false;
28
67
  this.enableTabClose = false;
68
+ // ─── Internal state ───
69
+ this.tippyInstance = null;
29
70
  this.panelContainer = null;
30
- this.positionManager = null;
31
- this.styleManager = new StyleManager();
32
- this.eventManager = new EventManager(this);
33
- this.handleSlotChange = () => {
34
- if (this.open && this.panelContainer) {
35
- this.updatePanelContent();
36
- }
37
- };
71
+ this.anchorEl = null;
72
+ this._suppressOpenWatch = false;
73
+ this._panelId = `nile-fp-${Math.random().toString(36).slice(2, 9)}`;
74
+ this._boundEscHandler = this._handleEscapeKey.bind(this);
75
+ this._pendingShowListener = null;
76
+ this._pendingHideListener = null;
77
+ }
78
+ static get prefersReducedMotion() {
79
+ if (!NileFloatingPanel_1._reducedMotionQuery) {
80
+ NileFloatingPanel_1._reducedMotionQuery =
81
+ window.matchMedia('(prefers-reduced-motion: reduce)');
82
+ }
83
+ return NileFloatingPanel_1._reducedMotionQuery.matches;
38
84
  }
39
85
  static get styles() {
40
86
  return [styles];
41
87
  }
42
- connectedCallback() {
43
- super.connectedCallback();
88
+ createRenderRoot() {
89
+ return this;
90
+ }
91
+ // ─── Lifecycle ───
92
+ firstUpdated() {
93
+ this._buildDOM();
94
+ this._attachTippy();
95
+ this._joinGroup();
96
+ this.visibilityManager = new VisibilityManager({
97
+ host: this,
98
+ target: this.anchorEl || null,
99
+ enableVisibilityEffect: this.enableVisibilityEffect,
100
+ enableTabClose: this.enableTabClose,
101
+ isOpen: () => this.open,
102
+ onAnchorOutOfView: () => {
103
+ this._setOpen(false);
104
+ this.tippyInstance?.hide();
105
+ this.emit('nile-visibility-change', {
106
+ visible: false,
107
+ reason: 'anchor-out-of-view',
108
+ });
109
+ },
110
+ onDocumentHidden: () => {
111
+ this._setOpen(false);
112
+ this.tippyInstance?.hide();
113
+ this.emit('nile-visibility-change', {
114
+ visible: false,
115
+ reason: 'document-hidden',
116
+ });
117
+ },
118
+ emit: (event, detail) => this.emit(`nile-${event}`, detail),
119
+ });
120
+ this.emit('nile-init');
44
121
  }
45
122
  disconnectedCallback() {
46
123
  super.disconnectedCallback();
47
- this.cleanupPanel();
124
+ this._cleanupPendingShowListener();
125
+ this._cleanupPendingHideListener();
48
126
  this.visibilityManager?.cleanup();
127
+ this._leaveGroup();
128
+ this._removeEscListener();
129
+ this._destroyTippy();
130
+ this.emit('nile-destroy');
49
131
  }
50
- updated(changedProperties) {
51
- super.updated(changedProperties);
52
- if (changedProperties.has('open')) {
132
+ updated(changed) {
133
+ super.updated(changed);
134
+ if (!this.panelContainer)
135
+ return;
136
+ if (changed.has('open') && !this._suppressOpenWatch) {
53
137
  if (this.open) {
54
- this.emit('nile-show');
55
- this.setupPanel();
56
138
  this.visibilityManager?.setup();
139
+ queueMicrotask(() => this.tippyInstance?.show());
57
140
  }
58
141
  else {
59
- this.emit('nile-hide');
60
142
  this.visibilityManager?.cleanup();
61
- this.cleanupPanel();
143
+ this.tippyInstance?.hide();
62
144
  }
63
145
  }
64
- if (changedProperties.has('closeOnOutsideClick') &&
65
- this.open &&
66
- this.panelContainer) {
67
- this.eventManager.updateOutsideClickHandler(this.panelContainer, this.closeOnOutsideClick, this.open);
68
- }
69
- if (changedProperties.has('anchor') && this.open) {
70
- this.cleanupPanel();
71
- this.setupPanel();
146
+ if (changed.has('group')) {
147
+ this._leaveGroup(changed.get('group'));
148
+ this._joinGroup();
72
149
  }
73
- if ((changedProperties.has('enableVisibilityEffect') ||
74
- changedProperties.has('enableTabClose')) &&
75
- this.open) {
76
- this.setupVisibilityManager();
150
+ const rebuildProps = [
151
+ 'placement', 'trigger', 'distance', 'skidding', 'arrow',
152
+ 'animation', 'duration', 'delay', 'interactive', 'interactiveBorder',
153
+ 'maxWidth', 'zIndex', 'followCursor', 'hideOnClick', 'inertia',
154
+ 'allowHTML', 'flip', 'preventOverlayClose', 'disabled', 'width', 'height',
155
+ ];
156
+ if (rebuildProps.some(p => changed.has(p))) {
157
+ this._attachTippy();
77
158
  }
78
- if (changedProperties.has('position') &&
79
- this.open &&
80
- this.positionManager) {
81
- this.positionManager.updatePosition(this.position);
159
+ }
160
+ // ─── Public API ───
161
+ /** Programmatically shows the panel. Returns a promise that resolves after the show animation. */
162
+ show() {
163
+ this.open = true;
164
+ return new Promise(resolve => {
165
+ this._cleanupPendingShowListener();
166
+ const handler = () => {
167
+ this._pendingShowListener = null;
168
+ resolve();
169
+ };
170
+ this._pendingShowListener = handler;
171
+ this.addEventListener('nile-after-show', handler, { once: true });
172
+ });
173
+ }
174
+ /** Programmatically hides the panel. Returns a promise that resolves after the hide animation. */
175
+ hide() {
176
+ this.open = false;
177
+ return new Promise(resolve => {
178
+ this._cleanupPendingHideListener();
179
+ const handler = () => {
180
+ this._pendingHideListener = null;
181
+ resolve();
182
+ };
183
+ this._pendingHideListener = handler;
184
+ this.addEventListener('nile-after-hide', handler, { once: true });
185
+ });
186
+ }
187
+ _cleanupPendingShowListener() {
188
+ if (this._pendingShowListener) {
189
+ this.removeEventListener('nile-after-show', this._pendingShowListener);
190
+ this._pendingShowListener = null;
82
191
  }
83
- if (changedProperties.has('open') && this.open && this.panelContainer) {
84
- this.updatePanelContent();
192
+ }
193
+ _cleanupPendingHideListener() {
194
+ if (this._pendingHideListener) {
195
+ this.removeEventListener('nile-after-hide', this._pendingHideListener);
196
+ this._pendingHideListener = null;
85
197
  }
86
198
  }
87
- setupPanel() {
88
- if (this.panelContainer) {
199
+ toggle() {
200
+ this.open = !this.open;
201
+ }
202
+ refresh() {
203
+ this._attachTippy();
204
+ }
205
+ /** Returns the current resolved placement from Tippy/Popper. */
206
+ getCurrentPlacement() {
207
+ const popper = this.tippyInstance?.popper;
208
+ const box = popper?.querySelector('.tippy-box');
209
+ return box?.dataset.placement ?? this.placement;
210
+ }
211
+ /** Returns true if the resolved placement matches the requested placement. */
212
+ isPositioningOptimal() {
213
+ return this.getCurrentPlacement() === this.placement;
214
+ }
215
+ // ─── Group management ───
216
+ _joinGroup() {
217
+ if (!this.group)
89
218
  return;
219
+ let set = NileFloatingPanel_1._groups.get(this.group);
220
+ if (!set) {
221
+ set = new Set();
222
+ NileFloatingPanel_1._groups.set(this.group, set);
90
223
  }
91
- const componentStyles = this.constructor
92
- .styles;
93
- if (componentStyles) {
94
- this.styleManager.injectStyles(componentStyles);
95
- }
96
- this.panelContainer = document.createElement('div');
97
- this.panelContainer.setAttribute('part', 'panel');
98
- this.panelContainer.className = 'nile-floating-panel__container';
99
- this.updatePanelContent();
100
- const anchorElement = AnchorManager.resolveAnchor(this.anchor);
101
- AnchorManager.appendToAnchor(anchorElement, this.panelContainer);
102
- requestAnimationFrame(() => {
103
- this.setupPositionManager();
104
- this.setupEventHandlers();
105
- this.setupVisibilityManager();
106
- });
224
+ set.add(this);
107
225
  }
108
- setupEventHandlers() {
109
- if (!this.panelContainer) {
226
+ _leaveGroup(oldGroup) {
227
+ const key = oldGroup ?? this.group;
228
+ if (!key)
110
229
  return;
230
+ const set = NileFloatingPanel_1._groups.get(key);
231
+ if (set) {
232
+ set.delete(this);
233
+ if (set.size === 0)
234
+ NileFloatingPanel_1._groups.delete(key);
111
235
  }
112
- this.eventManager.setupOutsideClickHandler(this.panelContainer, this.closeOnOutsideClick, this.open, () => {
113
- this.open = false;
236
+ }
237
+ _hideGroupSiblings() {
238
+ if (!this.group)
239
+ return;
240
+ const set = NileFloatingPanel_1._groups.get(this.group);
241
+ if (!set)
242
+ return;
243
+ set.forEach(panel => {
244
+ if (panel !== this && panel.open) {
245
+ panel._setOpen(false);
246
+ panel.tippyInstance?.hide();
247
+ }
114
248
  });
115
249
  }
116
- setupPositionManager() {
117
- if (!this.panelContainer) {
250
+ // ─── Escape key ───
251
+ _addEscListener() {
252
+ if (this.closeOnEscape) {
253
+ document.addEventListener('keydown', this._boundEscHandler);
254
+ }
255
+ }
256
+ _removeEscListener() {
257
+ document.removeEventListener('keydown', this._boundEscHandler);
258
+ }
259
+ _handleEscapeKey(e) {
260
+ if (e.key === 'Escape' && this.open) {
261
+ this._setOpen(false);
262
+ this.tippyInstance?.hide();
263
+ }
264
+ }
265
+ // ─── ARIA ───
266
+ _applyAria() {
267
+ if (!this.anchorEl || !this.panelContainer)
118
268
  return;
269
+ this.panelContainer.setAttribute('role', 'dialog');
270
+ this.panelContainer.id = this._panelId;
271
+ this.anchorEl.setAttribute('aria-haspopup', 'dialog');
272
+ this._syncAriaExpanded();
273
+ }
274
+ _syncAriaExpanded() {
275
+ this.anchorEl?.setAttribute('aria-expanded', String(this.open));
276
+ if (this.open) {
277
+ this.anchorEl?.setAttribute('aria-describedby', this._panelId);
119
278
  }
120
- const referenceElement = this.findTriggerElement() || this;
121
- this.positionManager = new PositionManager(referenceElement, this.panelContainer, this.position);
122
- this.positionManager.reposition();
123
- this.positionManager.setupAutoUpdate();
124
- }
125
- findTriggerElement() {
126
- // Try to find the next sibling element as the trigger
127
- let nextSibling = this.nextElementSibling;
128
- while (nextSibling) {
129
- if (nextSibling instanceof HTMLElement) {
130
- return nextSibling;
279
+ else {
280
+ this.anchorEl?.removeAttribute('aria-describedby');
281
+ }
282
+ }
283
+ // ─── DOM construction ───
284
+ _buildDOM() {
285
+ const children = Array.from(this.childNodes);
286
+ this.anchorEl = null;
287
+ const titleNodes = [];
288
+ const actionNodes = [];
289
+ const bodyNodes = [];
290
+ let firstElementSeen = false;
291
+ for (const child of children) {
292
+ if (child instanceof HTMLElement) {
293
+ const slot = child.getAttribute('slot');
294
+ if (slot === 'title') {
295
+ child.removeAttribute('slot');
296
+ titleNodes.push(child);
297
+ continue;
298
+ }
299
+ if (slot === 'action') {
300
+ child.removeAttribute('slot');
301
+ actionNodes.push(child);
302
+ continue;
303
+ }
304
+ if (!firstElementSeen && !this.for) {
305
+ this.anchorEl = child;
306
+ firstElementSeen = true;
307
+ continue;
308
+ }
131
309
  }
132
- nextSibling = nextSibling.nextElementSibling;
310
+ bodyNodes.push(child);
133
311
  }
134
- // Try to find the previous sibling element
135
- let previousSibling = this.previousElementSibling;
136
- while (previousSibling) {
137
- if (previousSibling instanceof HTMLElement) {
138
- return previousSibling;
312
+ if (this.for) {
313
+ const anchor = document.getElementById(this.for);
314
+ if (anchor) {
315
+ this.anchorEl = anchor;
139
316
  }
140
- previousSibling = previousSibling.previousElementSibling;
141
317
  }
142
- return null;
143
- }
144
- setupVisibilityManager() {
145
- if (!this.enableVisibilityEffect) {
146
- return;
318
+ while (this.firstChild) {
319
+ this.removeChild(this.firstChild);
147
320
  }
148
- const triggerElement = this.findTriggerElement();
149
- // Cleanup existing visibility manager if it exists
150
- if (this.visibilityManager) {
151
- this.visibilityManager.cleanup();
321
+ if (this.anchorEl && !this.for) {
322
+ this.appendChild(this.anchorEl);
152
323
  }
153
- this.visibilityManager = new VisibilityManager({
154
- host: this,
155
- target: triggerElement || null,
156
- enableVisibilityEffect: this.enableVisibilityEffect,
157
- enableTabClose: this.enableTabClose,
158
- isOpen: () => this.open,
159
- onAnchorOutOfView: () => {
160
- this.open = false;
161
- this.emit('nile-visibility-change', {
162
- visible: false,
163
- reason: 'anchor-out-of-view',
164
- });
165
- },
166
- onDocumentHidden: () => {
167
- this.open = false;
168
- this.emit('nile-visibility-change', {
169
- visible: false,
170
- reason: 'document-hidden',
171
- });
172
- },
173
- emit: (event, detail) => this.emit(`nile-${event}`, detail),
174
- });
175
- if (this.open) {
176
- this.visibilityManager.setup();
324
+ this.panelContainer = document.createElement('div');
325
+ this.panelContainer.className = 'nile-floating-panel__content';
326
+ this.panelContainer.style.display = 'none';
327
+ const body = document.createElement('div');
328
+ body.className = 'nile-floating-panel__body';
329
+ if (titleNodes.length > 0 || this.title) {
330
+ const titleDiv = document.createElement('div');
331
+ titleDiv.className = 'nile-floating-panel__title';
332
+ if (this.title) {
333
+ titleDiv.textContent = this.title;
334
+ }
335
+ else {
336
+ titleNodes.forEach(n => titleDiv.appendChild(n));
337
+ }
338
+ body.appendChild(titleDiv);
339
+ }
340
+ if (bodyNodes.length > 0) {
341
+ const mainDiv = document.createElement('div');
342
+ mainDiv.className = 'nile-floating-panel__main';
343
+ bodyNodes.forEach(n => mainDiv.appendChild(n));
344
+ body.appendChild(mainDiv);
345
+ }
346
+ if (actionNodes.length > 0) {
347
+ const actionDiv = document.createElement('div');
348
+ actionDiv.className = 'nile-floating-panel__action';
349
+ actionNodes.forEach(n => actionDiv.appendChild(n));
350
+ body.appendChild(actionDiv);
177
351
  }
352
+ this.panelContainer.appendChild(body);
353
+ this.appendChild(this.panelContainer);
354
+ this._applyAria();
178
355
  }
179
- updatePanelContent() {
180
- if (!this.panelContainer) {
181
- return;
356
+ // ─── Tippy management ───
357
+ _resolveArrow() {
358
+ switch (this.arrow) {
359
+ case 'round': return roundArrow;
360
+ case 'none': return false;
361
+ default: return true;
182
362
  }
183
- const slot = this.shadowRoot?.querySelector('slot') || null;
184
- ContentManager.updatePanelContent(this.panelContainer, slot, '');
185
363
  }
186
- reposition() {
187
- if (this.positionManager) {
188
- this.positionManager.reposition();
364
+ _setOpen(value) {
365
+ this._suppressOpenWatch = true;
366
+ this.open = value;
367
+ this._syncAriaExpanded();
368
+ this._suppressOpenWatch = false;
369
+ }
370
+ _getEffectiveDuration() {
371
+ if (NileFloatingPanel_1.prefersReducedMotion)
372
+ return 0;
373
+ return parseDuration(this.duration);
374
+ }
375
+ _getEffectiveAnimation() {
376
+ if (NileFloatingPanel_1.prefersReducedMotion)
377
+ return false;
378
+ return this.animation;
379
+ }
380
+ _attachTippy() {
381
+ this._destroyTippy();
382
+ if (this.disabled || !this.anchorEl || !this.panelContainer)
383
+ return;
384
+ const resolvedFollowCursor = parseFollowCursor(this.followCursor);
385
+ const effectiveHideOnClick = this.preventOverlayClose ? false : this.hideOnClick;
386
+ const options = {
387
+ content: this.panelContainer,
388
+ placement: this.placement,
389
+ trigger: this.trigger,
390
+ offset: [this.skidding, this.distance],
391
+ theme: 'floating-panel',
392
+ animation: this._getEffectiveAnimation(),
393
+ interactive: this.interactive,
394
+ arrow: this._resolveArrow(),
395
+ duration: this._getEffectiveDuration(),
396
+ allowHTML: this.allowHTML,
397
+ delay: this.delay,
398
+ maxWidth: this.maxWidth,
399
+ zIndex: this.zIndex,
400
+ hideOnClick: effectiveHideOnClick,
401
+ inertia: NileFloatingPanel_1.prefersReducedMotion ? false : this.inertia,
402
+ interactiveBorder: this.interactiveBorder,
403
+ appendTo: document.body,
404
+ followCursor: resolvedFollowCursor,
405
+ plugins: resolvedFollowCursor ? [followCursorPlugin] : [],
406
+ popperOptions: {
407
+ modifiers: [{ name: 'flip', enabled: this.flip }],
408
+ },
409
+ onMount: () => {
410
+ if (this.panelContainer)
411
+ this.panelContainer.style.display = '';
412
+ },
413
+ onShow: (instance) => {
414
+ if (this.panelContainer)
415
+ this.panelContainer.style.display = '';
416
+ const tc = instance.popper.querySelector('.tippy-content');
417
+ if (tc) {
418
+ if (this.width)
419
+ tc.style.width = this.width;
420
+ if (this.height) {
421
+ tc.style.height = this.height;
422
+ tc.style.overflow = 'auto';
423
+ }
424
+ }
425
+ this._hideGroupSiblings();
426
+ this._setOpen(true);
427
+ this._addEscListener();
428
+ this.dispatchEvent(new CustomEvent('nile-show', { detail: { instance, target: instance.reference } }));
429
+ this.dispatchEvent(new CustomEvent('nile-toggle', { detail: { open: true, instance, target: instance.reference } }));
430
+ return undefined;
431
+ },
432
+ onShown: (instance) => {
433
+ this.dispatchEvent(new CustomEvent('nile-after-show', { detail: { instance, target: instance.reference } }));
434
+ },
435
+ onHide: (instance) => {
436
+ this._setOpen(false);
437
+ this._removeEscListener();
438
+ this.dispatchEvent(new CustomEvent('nile-hide', { detail: { instance, target: instance.reference } }));
439
+ this.dispatchEvent(new CustomEvent('nile-toggle', { detail: { open: false, instance, target: instance.reference } }));
440
+ return undefined;
441
+ },
442
+ onHidden: (instance) => {
443
+ if (this.panelContainer)
444
+ this.panelContainer.style.display = 'none';
445
+ this.dispatchEvent(new CustomEvent('nile-after-hide', { detail: { instance, target: instance.reference } }));
446
+ },
447
+ };
448
+ this.tippyInstance = tippy(this.anchorEl, options);
449
+ if (this.open) {
450
+ queueMicrotask(() => this.tippyInstance?.show());
189
451
  }
190
452
  }
191
- cleanupPanel() {
192
- this.eventManager.destroy();
193
- if (this.positionManager) {
194
- this.positionManager.destroy();
195
- this.positionManager = null;
453
+ _destroyTippy() {
454
+ if (this.tippyInstance) {
455
+ this.tippyInstance.destroy();
456
+ this.tippyInstance = null;
196
457
  }
197
458
  if (this.panelContainer) {
198
- AnchorManager.removeFromAnchor(this.panelContainer);
459
+ this.panelContainer.style.display = 'none';
460
+ if (this.panelContainer.parentElement !== this) {
461
+ this.appendChild(this.panelContainer);
462
+ }
199
463
  }
200
- this.panelContainer = null;
201
- this.styleManager.cleanupStyles();
202
- }
203
- render() {
204
- return html ` <slot @slotchange=${this.handleSlotChange}></slot> `;
205
464
  }
206
465
  };
466
+ NileFloatingPanel._groups = new Map();
467
+ NileFloatingPanel._reducedMotionQuery = null;
468
+ __decorate([
469
+ property({ type: String })
470
+ ], NileFloatingPanel.prototype, "placement", void 0);
471
+ __decorate([
472
+ property({ type: String })
473
+ ], NileFloatingPanel.prototype, "trigger", void 0);
474
+ __decorate([
475
+ property({ type: Number })
476
+ ], NileFloatingPanel.prototype, "distance", void 0);
207
477
  __decorate([
208
- property()
209
- ], NileFloatingPanel.prototype, "anchor", void 0);
478
+ property({ type: Number })
479
+ ], NileFloatingPanel.prototype, "skidding", void 0);
210
480
  __decorate([
211
- property()
212
- ], NileFloatingPanel.prototype, "position", void 0);
481
+ property({ type: String, reflect: true })
482
+ ], NileFloatingPanel.prototype, "arrow", void 0);
213
483
  __decorate([
214
- property({ type: Boolean, reflect: true, attribute: true })
484
+ property({ type: String, reflect: true })
485
+ ], NileFloatingPanel.prototype, "animation", void 0);
486
+ __decorate([
487
+ property({ type: String, reflect: true })
488
+ ], NileFloatingPanel.prototype, "duration", void 0);
489
+ __decorate([
490
+ property({ type: String, reflect: true })
491
+ ], NileFloatingPanel.prototype, "delay", void 0);
492
+ __decorate([
493
+ property({ type: Boolean, reflect: true })
494
+ ], NileFloatingPanel.prototype, "interactive", void 0);
495
+ __decorate([
496
+ property({ type: Number, reflect: true })
497
+ ], NileFloatingPanel.prototype, "interactiveBorder", void 0);
498
+ __decorate([
499
+ property({ type: String, reflect: true })
500
+ ], NileFloatingPanel.prototype, "maxWidth", void 0);
501
+ __decorate([
502
+ property({ type: Number, reflect: true })
503
+ ], NileFloatingPanel.prototype, "zIndex", void 0);
504
+ __decorate([
505
+ property({ type: String, reflect: true })
506
+ ], NileFloatingPanel.prototype, "followCursor", void 0);
507
+ __decorate([
508
+ property({ type: Boolean, reflect: true })
509
+ ], NileFloatingPanel.prototype, "hideOnClick", void 0);
510
+ __decorate([
511
+ property({ type: Boolean, reflect: true })
512
+ ], NileFloatingPanel.prototype, "inertia", void 0);
513
+ __decorate([
514
+ property({ type: Boolean, reflect: true })
515
+ ], NileFloatingPanel.prototype, "allowHTML", void 0);
516
+ __decorate([
517
+ property({ type: Boolean, reflect: true })
518
+ ], NileFloatingPanel.prototype, "flip", void 0);
519
+ __decorate([
520
+ property({ type: String, attribute: 'for' })
521
+ ], NileFloatingPanel.prototype, "for", void 0);
522
+ __decorate([
523
+ property({ type: Boolean, reflect: true })
215
524
  ], NileFloatingPanel.prototype, "open", void 0);
216
525
  __decorate([
217
- property({
218
- type: Boolean,
219
- reflect: true,
220
- attribute: 'close-on-outside-click',
221
- converter: {
222
- fromAttribute: (value) => (!value || value === 'false' ? false : true),
223
- toAttribute: (value) => (value ? 'true' : 'false'),
224
- },
225
- })
226
- ], NileFloatingPanel.prototype, "closeOnOutsideClick", void 0);
526
+ property({ type: Boolean, reflect: true })
527
+ ], NileFloatingPanel.prototype, "preventOverlayClose", void 0);
528
+ __decorate([
529
+ property({ type: String, reflect: true })
530
+ ], NileFloatingPanel.prototype, "title", void 0);
531
+ __decorate([
532
+ property({ type: Boolean, reflect: true })
533
+ ], NileFloatingPanel.prototype, "disabled", void 0);
534
+ __decorate([
535
+ property({ type: String, reflect: true })
536
+ ], NileFloatingPanel.prototype, "width", void 0);
537
+ __decorate([
538
+ property({ type: String, reflect: true })
539
+ ], NileFloatingPanel.prototype, "height", void 0);
540
+ __decorate([
541
+ property({ type: String, reflect: true })
542
+ ], NileFloatingPanel.prototype, "group", void 0);
543
+ __decorate([
544
+ property({ type: Boolean, reflect: true })
545
+ ], NileFloatingPanel.prototype, "closeOnEscape", void 0);
227
546
  __decorate([
228
547
  property({ type: Boolean, reflect: true })
229
548
  ], NileFloatingPanel.prototype, "enableVisibilityEffect", void 0);