@rettangoli/ui 0.1.2-rc3 → 0.1.2-rc31

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 (56) hide show
  1. package/dist/rettangoli-iife-layout.min.js +115 -43
  2. package/dist/rettangoli-iife-ui.min.js +187 -67
  3. package/package.json +5 -3
  4. package/src/cli/buildSvg.js +86 -0
  5. package/src/cli/index.js +1 -0
  6. package/src/common.js +19 -0
  7. package/src/components/breadcrumb/breadcrumb.handlers.js +9 -0
  8. package/src/components/breadcrumb/breadcrumb.store.js +29 -0
  9. package/src/components/breadcrumb/breadcrumb.view.yaml +64 -0
  10. package/src/components/dropdownMenu/dropdownMenu.handlers.js +4 -4
  11. package/src/components/dropdownMenu/dropdownMenu.store.js +5 -17
  12. package/src/components/dropdownMenu/dropdownMenu.view.yaml +15 -13
  13. package/src/components/form/form.handlers.js +173 -25
  14. package/src/components/form/form.store.js +176 -22
  15. package/src/components/form/form.view.yaml +217 -33
  16. package/src/components/pageOutline/pageOutline.handlers.js +57 -17
  17. package/src/components/pageOutline/pageOutline.store.js +46 -1
  18. package/src/components/pageOutline/pageOutline.view.yaml +7 -5
  19. package/src/components/popoverInput/popoverInput.handlers.js +99 -0
  20. package/src/components/popoverInput/popoverInput.store.js +48 -0
  21. package/src/components/popoverInput/popoverInput.view.yaml +55 -0
  22. package/src/components/select/select.handlers.js +116 -11
  23. package/src/components/select/select.store.js +84 -18
  24. package/src/components/select/select.view.yaml +40 -10
  25. package/src/components/sidebar/sidebar.view.yaml +1 -1
  26. package/src/components/sliderInput/sliderInput.handlers.js +41 -0
  27. package/src/components/sliderInput/sliderInput.store.js +18 -0
  28. package/src/components/sliderInput/sliderInput.view.yaml +42 -0
  29. package/src/components/table/table.handlers.js +1 -1
  30. package/src/components/tabs/tabs.handlers.js +10 -0
  31. package/src/components/tabs/tabs.store.js +29 -0
  32. package/src/components/tabs/tabs.view.yaml +64 -0
  33. package/src/components/tooltip/tooltip.handlers.js +0 -0
  34. package/src/components/tooltip/tooltip.store.js +12 -0
  35. package/src/components/tooltip/tooltip.view.yaml +27 -0
  36. package/src/components/waveform/waveform.handlers.js +92 -0
  37. package/src/components/waveform/waveform.store.js +17 -0
  38. package/src/components/waveform/waveform.view.yaml +38 -0
  39. package/src/entry-iife-layout.js +3 -0
  40. package/src/entry-iife-ui.js +4 -0
  41. package/src/index.js +5 -1
  42. package/src/primitives/button.js +10 -0
  43. package/src/primitives/colorPicker.js +9 -0
  44. package/src/primitives/dialog.js +254 -0
  45. package/src/primitives/input.js +41 -11
  46. package/src/primitives/popover.js +280 -0
  47. package/src/primitives/slider.js +18 -9
  48. package/src/primitives/svg.js +2 -0
  49. package/src/primitives/textarea.js +25 -1
  50. package/src/styles/cursorStyles.js +38 -2
  51. package/src/components/dialog/dialog.handlers.js +0 -5
  52. package/src/components/dialog/dialog.store.js +0 -25
  53. package/src/components/dialog/dialog.view.yaml +0 -44
  54. package/src/components/popover/popover.handlers.js +0 -5
  55. package/src/components/popover/popover.store.js +0 -12
  56. package/src/components/popover/popover.view.yaml +0 -57
@@ -0,0 +1,254 @@
1
+ import { css } from "../common.js";
2
+
3
+ class RettangoliDialogElement extends HTMLElement {
4
+ static styleSheet = null;
5
+
6
+ static initializeStyleSheet() {
7
+ if (!RettangoliDialogElement.styleSheet) {
8
+ RettangoliDialogElement.styleSheet = new CSSStyleSheet();
9
+ RettangoliDialogElement.styleSheet.replaceSync(css`
10
+ :host {
11
+ display: contents;
12
+ }
13
+
14
+ dialog {
15
+ padding: 0;
16
+ border: none;
17
+ background: transparent;
18
+ margin: auto;
19
+ overflow-y: scroll;
20
+ color: inherit;
21
+ max-height: 100vh;
22
+ height: 100vh;
23
+ max-width: 100vw;
24
+ scrollbar-width: none;
25
+ outline: none;
26
+ }
27
+
28
+ dialog::backdrop {
29
+ background-color: rgba(0, 0, 0, 0.5);
30
+ }
31
+
32
+ slot[name="content"] {
33
+ background-color: var(--background) !important;
34
+ display: block;
35
+ padding: var(--spacing-lg);
36
+ border: 1px solid var(--border);
37
+ border-radius: var(--border-radius-md);
38
+ margin-left: var(--spacing-lg);
39
+ margin-right: var(--spacing-lg);
40
+ width: fit-content;
41
+ max-width: calc(100vw - 2 * var(--spacing-lg));
42
+ /* Default margins will be set dynamically via JavaScript for adaptive centering */
43
+ margin-top: 40px;
44
+ margin-bottom: 40px;
45
+ }
46
+
47
+ /* Size attribute styles */
48
+ :host([s="sm"]) slot[name="content"] {
49
+ width: 33vw;
50
+ }
51
+
52
+ :host([s="md"]) slot[name="content"] {
53
+ width: 50vw;
54
+ }
55
+
56
+ :host([s="lg"]) slot[name="content"] {
57
+ width: 80vw;
58
+ }
59
+
60
+ :host([s="f"]) slot[name="content"] {
61
+ width: 100vw;
62
+ margin-left: 0;
63
+ margin-right: 0;
64
+ }
65
+
66
+ @keyframes dialog-in {
67
+ from {
68
+ opacity: 0;
69
+ transform: scale(0.95);
70
+ }
71
+ to {
72
+ opacity: 1;
73
+ transform: scale(1);
74
+ }
75
+ }
76
+
77
+ dialog[open] slot[name="content"] {
78
+ animation: dialog-in 150ms cubic-bezier(0.16, 1, 0.3, 1);
79
+ }
80
+ `);
81
+ }
82
+ }
83
+
84
+ constructor() {
85
+ super();
86
+ RettangoliDialogElement.initializeStyleSheet();
87
+ this.shadow = this.attachShadow({ mode: "open" });
88
+ this.shadow.adoptedStyleSheets = [RettangoliDialogElement.styleSheet];
89
+
90
+ // Create dialog element
91
+ this._dialogElement = document.createElement('dialog');
92
+ this.shadow.appendChild(this._dialogElement);
93
+
94
+ // Store reference for content slot
95
+ this._slotElement = null;
96
+ this._isConnected = false;
97
+
98
+ // Handle click outside - emit custom event
99
+ this._dialogElement.addEventListener('click', (e) => {
100
+ if (e.target === this._dialogElement) {
101
+ this.dispatchEvent(new CustomEvent('close', {
102
+ detail: {}
103
+ }));
104
+ }
105
+ });
106
+
107
+ // Handle right-click on overlay to close dialog
108
+ this._dialogElement.addEventListener('contextmenu', (e) => {
109
+ if (e.target === this._dialogElement) {
110
+ e.preventDefault();
111
+ this.dispatchEvent(new CustomEvent('close', {
112
+ detail: {}
113
+ }));
114
+ }
115
+ });
116
+
117
+ // Handle ESC key - prevent native close and emit custom event
118
+ this._dialogElement.addEventListener('cancel', (e) => {
119
+ e.preventDefault();
120
+ this.dispatchEvent(new CustomEvent('close', {
121
+ detail: {}
122
+ }));
123
+ });
124
+ }
125
+
126
+ static get observedAttributes() {
127
+ return ["open", "w", "s"];
128
+ }
129
+
130
+ connectedCallback() {
131
+ this._updateDialog();
132
+ this._isConnected = true;
133
+ // Check initial open attribute
134
+ if (this.hasAttribute('open')) {
135
+ this._showModal();
136
+ }
137
+ }
138
+
139
+ attributeChangedCallback(name, oldValue, newValue) {
140
+ if (name === 'open') {
141
+ if (newValue !== null && !this._dialogElement.open && this._isConnected) {
142
+ this._showModal();
143
+ } else if (newValue === null && this._dialogElement.open) {
144
+ this._hideModal();
145
+ }
146
+ } else if (name === 'w') {
147
+ this._updateWidth();
148
+ } else if (name === 's') {
149
+ // Size is handled via CSS :host() selectors
150
+ }
151
+ }
152
+
153
+ _updateDialog() {
154
+ this._updateWidth();
155
+ }
156
+
157
+ _updateWidth() {
158
+ const width = this.getAttribute('w');
159
+ if (width) {
160
+ this._dialogElement.style.width = width;
161
+ } else {
162
+ this._dialogElement.style.width = '';
163
+ }
164
+ }
165
+
166
+ // Internal methods
167
+ _showModal() {
168
+ if (!this._dialogElement.open) {
169
+ // Create and append slot for content only if it doesn't exist
170
+ if (!this._slotElement) {
171
+ this._slotElement = document.createElement('slot');
172
+ this._slotElement.setAttribute('name', 'content');
173
+ this._dialogElement.appendChild(this._slotElement);
174
+ }
175
+
176
+ this._dialogElement.showModal();
177
+
178
+ // Reset scroll position
179
+ this._dialogElement.scrollTop = 0;
180
+
181
+ // Apply adaptive centering
182
+ this._applyAdaptiveCentering();
183
+ }
184
+ }
185
+
186
+ _hideModal() {
187
+ if (this._dialogElement.open) {
188
+ this._dialogElement.close();
189
+
190
+ // Remove slot to unmount content
191
+ if (this._slotElement) {
192
+ // Reset any inline styles applied for adaptive centering
193
+ this._slotElement.style.marginTop = '';
194
+ this._slotElement.style.marginBottom = '';
195
+
196
+ this._dialogElement.removeChild(this._slotElement);
197
+ this._slotElement = null;
198
+ }
199
+
200
+ // Reset dialog height
201
+ this._dialogElement.style.height = '';
202
+
203
+ // Don't emit any event when programmatically closed via attribute
204
+ }
205
+ }
206
+
207
+ _applyAdaptiveCentering() {
208
+ if (!this._slotElement) {
209
+ return;
210
+ }
211
+
212
+ // Use requestAnimationFrame to ensure DOM has updated
213
+ requestAnimationFrame(() => {
214
+ if (!this._slotElement) return;
215
+
216
+ // Get the actual height of the content
217
+ const contentHeight = this._slotElement.offsetHeight;
218
+ const viewportHeight = window.innerHeight;
219
+
220
+ // Calculate centered position with minimum margins for scrollability
221
+ const minMargin = 40; // Minimum margin in pixels to ensure scrollability
222
+
223
+ if (contentHeight >= viewportHeight - (2 * minMargin)) {
224
+ // Content is too tall, use minimum margins to allow scrolling
225
+ // Start near the top with small margin so content isn't pushed too far down
226
+ this._slotElement.style.marginTop = `${minMargin}px`;
227
+ this._slotElement.style.marginBottom = `${minMargin}px`;
228
+ // Keep dialog at full height for scrolling
229
+ this._dialogElement.style.height = '100vh';
230
+ } else {
231
+ // Content fits, center it vertically
232
+ const totalMargin = viewportHeight - contentHeight;
233
+ const margin = Math.floor(totalMargin / 2);
234
+ this._slotElement.style.marginTop = `${margin}px`;
235
+ this._slotElement.style.marginBottom = `${margin}px`;
236
+ // Set dialog height to auto to prevent unnecessary scrollbar
237
+ this._dialogElement.style.height = 'auto';
238
+ }
239
+ });
240
+ }
241
+
242
+
243
+ // Expose dialog element for advanced usage
244
+ get dialog() {
245
+ return this._dialogElement;
246
+ }
247
+ }
248
+
249
+ // Export factory function to maintain API compatibility
250
+ export default ({ render, html }) => {
251
+ // Note: render and html parameters are accepted but not used
252
+ // This maintains backward compatibility with existing code
253
+ return RettangoliDialogElement;
254
+ };
@@ -1,5 +1,5 @@
1
- import {
2
- css,
1
+ import {
2
+ css,
3
3
  dimensionWithUnit,
4
4
  convertObjectToCssString,
5
5
  styleMapKeys,
@@ -59,7 +59,7 @@ class RettangoliInputElement extends HTMLElement {
59
59
  RettangoliInputElement.initializeStyleSheet();
60
60
  this.shadow = this.attachShadow({ mode: "closed" });
61
61
  this.shadow.adoptedStyleSheets = [RettangoliInputElement.styleSheet];
62
-
62
+
63
63
  // Initialize style tracking properties
64
64
  this._styles = {
65
65
  default: {},
@@ -69,11 +69,11 @@ class RettangoliInputElement extends HTMLElement {
69
69
  xl: {},
70
70
  };
71
71
  this._lastStyleString = "";
72
-
72
+
73
73
  // Create initial DOM structure
74
74
  this._inputElement = document.createElement('input');
75
75
  this._styleElement = document.createElement('style');
76
-
76
+
77
77
  this.shadow.appendChild(this._styleElement);
78
78
  this.shadow.appendChild(this._inputElement);
79
79
 
@@ -83,10 +83,11 @@ class RettangoliInputElement extends HTMLElement {
83
83
 
84
84
  static get observedAttributes() {
85
85
  return [
86
- "key",
87
- "type",
88
- "placeholder",
86
+ "key",
87
+ "type",
88
+ "placeholder",
89
89
  "disabled",
90
+ "value",
90
91
  "s",
91
92
  ...permutateBreakpoints([
92
93
  ...styleMapKeys,
@@ -105,6 +106,14 @@ class RettangoliInputElement extends HTMLElement {
105
106
  return this._inputElement.value;
106
107
  }
107
108
 
109
+ set value(newValue) {
110
+ this._inputElement.value = newValue;
111
+ }
112
+
113
+ focus() {
114
+ this._inputElement.focus();
115
+ }
116
+
108
117
  _onChange = (event) => {
109
118
  this.dispatchEvent(new CustomEvent('input-change', {
110
119
  detail: {
@@ -114,8 +123,17 @@ class RettangoliInputElement extends HTMLElement {
114
123
  };
115
124
 
116
125
  attributeChangedCallback(name, oldValue, newValue) {
126
+ // Handle key attribute change - reset value
127
+ if (name === "key" && oldValue !== newValue) {
128
+ requestAnimationFrame((() => {
129
+ const value = this.getAttribute("value");
130
+ this._inputElement.value = value ?? "";
131
+ }))
132
+ return;
133
+ }
134
+
117
135
  // Handle input-specific attributes first
118
- if (["type", "placeholder", "disabled", "s"].includes(name)) {
136
+ if (["type", "placeholder", "disabled", "value", "step", "s"].includes(name)) {
119
137
  this._updateInputAttributes();
120
138
  return;
121
139
  }
@@ -188,16 +206,28 @@ class RettangoliInputElement extends HTMLElement {
188
206
  _updateInputAttributes() {
189
207
  const type = this.getAttribute("type") || "text";
190
208
  const placeholder = this.getAttribute("placeholder");
209
+ const value = this.getAttribute("value");
210
+ const step = this.getAttribute("step");
191
211
  const isDisabled = this.hasAttribute('disabled');
192
212
 
193
213
  this._inputElement.setAttribute("type", type);
194
-
214
+
195
215
  if (placeholder !== null) {
196
216
  this._inputElement.setAttribute("placeholder", placeholder);
197
217
  } else {
198
218
  this._inputElement.removeAttribute("placeholder");
199
219
  }
200
-
220
+
221
+ if (value !== null) {
222
+ this._inputElement.value = value;
223
+ }
224
+
225
+ if (step !== null) {
226
+ this._inputElement.setAttribute("step", step);
227
+ } else {
228
+ this._inputElement.removeAttribute("step");
229
+ }
230
+
201
231
  if (isDisabled) {
202
232
  this._inputElement.setAttribute("disabled", "");
203
233
  } else {
@@ -0,0 +1,280 @@
1
+ import { css } from "../common.js";
2
+
3
+ class RettangoliPopoverElement extends HTMLElement {
4
+ static styleSheet = null;
5
+
6
+ static initializeStyleSheet() {
7
+ if (!RettangoliPopoverElement.styleSheet) {
8
+ RettangoliPopoverElement.styleSheet = new CSSStyleSheet();
9
+ RettangoliPopoverElement.styleSheet.replaceSync(css`
10
+ :host {
11
+ display: contents;
12
+ }
13
+
14
+ .popover-overlay {
15
+ position: fixed;
16
+ top: 0;
17
+ left: 0;
18
+ width: 100vw;
19
+ height: 100vh;
20
+ z-index: 999;
21
+ display: none;
22
+ }
23
+
24
+ .popover-container {
25
+ position: fixed;
26
+ z-index: 1000;
27
+ display: none;
28
+ outline: none;
29
+ }
30
+
31
+ :host([open]:not([no-overlay])) .popover-overlay {
32
+ display: block;
33
+ }
34
+
35
+ :host([open]) .popover-container {
36
+ display: block;
37
+ visibility: hidden;
38
+ }
39
+
40
+ /* For no-overlay mode, make the container non-interactive */
41
+ :host([no-overlay]) .popover-container {
42
+ pointer-events: none;
43
+ }
44
+
45
+ :host([open][positioned]) .popover-container {
46
+ visibility: visible;
47
+ }
48
+
49
+ slot[name="content"] {
50
+ display: block;
51
+ background-color: var(--muted);
52
+ border: 1px solid var(--border);
53
+ border-radius: var(--border-radius-md);
54
+ padding: var(--spacing-md);
55
+ min-width: 200px;
56
+ max-width: 400px;
57
+ }
58
+ `);
59
+ }
60
+ }
61
+
62
+ constructor() {
63
+ super();
64
+ RettangoliPopoverElement.initializeStyleSheet();
65
+ this.shadow = this.attachShadow({ mode: "open" });
66
+ this.shadow.adoptedStyleSheets = [RettangoliPopoverElement.styleSheet];
67
+
68
+ // Create overlay
69
+ this._popoverOverlay = document.createElement('div');
70
+ this._popoverOverlay.className = 'popover-overlay';
71
+ this.shadow.appendChild(this._popoverOverlay);
72
+
73
+ // Handle overlay clicks to close popover
74
+ this._popoverOverlay.addEventListener('click', () => {
75
+ this.dispatchEvent(new CustomEvent('close', {
76
+ detail: {}
77
+ }));
78
+ });
79
+
80
+ // Handle right-click on overlay to close popover
81
+ this._popoverOverlay.addEventListener('contextmenu', (e) => {
82
+ e.preventDefault();
83
+ this.dispatchEvent(new CustomEvent('close', {
84
+ detail: {}
85
+ }));
86
+ });
87
+
88
+ // Create popover container
89
+ this._popoverContainer = document.createElement('div');
90
+ this._popoverContainer.className = 'popover-container';
91
+ this.shadow.appendChild(this._popoverContainer);
92
+
93
+ // Store reference for content slot
94
+ this._slotElement = null;
95
+
96
+ // Track if we're open
97
+ this._isOpen = false;
98
+
99
+ // Bind event handlers
100
+ this._handleEscKey = this._handleEscKey.bind(this);
101
+ }
102
+
103
+ static get observedAttributes() {
104
+ return ["open", "x", "y", "placement", "no-overlay"];
105
+ }
106
+
107
+ connectedCallback() {
108
+ // Check initial open attribute
109
+ if (this.hasAttribute('open')) {
110
+ this._show();
111
+ }
112
+ }
113
+
114
+ disconnectedCallback() {
115
+ // Clean up event listeners
116
+ this._removeGlobalListeners();
117
+ }
118
+
119
+ attributeChangedCallback(name, oldValue, newValue) {
120
+ if (name === 'open') {
121
+ if (newValue !== null && !this._isOpen) {
122
+ this._show();
123
+ } else if (newValue === null && this._isOpen) {
124
+ this._hide();
125
+ }
126
+ } else if ((name === 'x' || name === 'y' || name === 'placement') && this._isOpen) {
127
+ this._updatePosition();
128
+ }
129
+ }
130
+
131
+ _show() {
132
+ if (!this._isOpen) {
133
+ // Create and append slot for content only if it doesn't exist
134
+ if (!this._slotElement) {
135
+ this._slotElement = document.createElement('slot');
136
+ this._slotElement.setAttribute('name', 'content');
137
+ this._popoverContainer.appendChild(this._slotElement);
138
+ }
139
+
140
+ this._isOpen = true;
141
+ this._updatePosition();
142
+ this._addGlobalListeners();
143
+ }
144
+ }
145
+
146
+ _hide() {
147
+ if (this._isOpen) {
148
+ this._isOpen = false;
149
+
150
+ // Remove slot to unmount content
151
+ if (this._slotElement) {
152
+ this._popoverContainer.removeChild(this._slotElement);
153
+ this._slotElement = null;
154
+ }
155
+
156
+ this._removeGlobalListeners();
157
+ }
158
+ }
159
+
160
+ _updatePosition() {
161
+ const x = parseFloat(this.getAttribute('x') || '0');
162
+ const y = parseFloat(this.getAttribute('y') || '0');
163
+ const placement = this.getAttribute('placement') || 'bottom-start';
164
+
165
+ // Remove positioned attribute to hide during repositioning
166
+ this.removeAttribute('positioned');
167
+
168
+ // Calculate position based on placement
169
+ // We'll position after the popover is rendered to get its dimensions
170
+ requestAnimationFrame(() => {
171
+ const rect = this._popoverContainer.getBoundingClientRect();
172
+ const { left, top } = this._calculatePosition(x, y, rect.width, rect.height, placement);
173
+
174
+ // Set position first
175
+ this._popoverContainer.style.left = `${left}px`;
176
+ this._popoverContainer.style.top = `${top}px`;
177
+
178
+ // Then make visible in next frame to prevent flicker
179
+ requestAnimationFrame(() => {
180
+ this.setAttribute('positioned', '');
181
+ });
182
+ });
183
+ }
184
+
185
+ _calculatePosition(x, y, width, height, placement) {
186
+ const offset = 8; // Small offset from the cursor
187
+ let left = x;
188
+ let top = y;
189
+
190
+ switch (placement) {
191
+ case 'top':
192
+ left = x - width / 2;
193
+ top = y - height - offset;
194
+ break;
195
+ case 'top-start':
196
+ left = x;
197
+ top = y - height - offset;
198
+ break;
199
+ case 'top-end':
200
+ left = x - width;
201
+ top = y - height - offset;
202
+ break;
203
+ case 'right':
204
+ left = x + offset;
205
+ top = y - height / 2;
206
+ break;
207
+ case 'right-start':
208
+ left = x + offset;
209
+ top = y;
210
+ break;
211
+ case 'right-end':
212
+ left = x + offset;
213
+ top = y - height;
214
+ break;
215
+ case 'bottom':
216
+ left = x - width / 2;
217
+ top = y + offset;
218
+ break;
219
+ case 'bottom-start':
220
+ left = x;
221
+ top = y + offset;
222
+ break;
223
+ case 'bottom-end':
224
+ left = x - width;
225
+ top = y + offset;
226
+ break;
227
+ case 'left':
228
+ left = x - width - offset;
229
+ top = y - height / 2;
230
+ break;
231
+ case 'left-start':
232
+ left = x - width - offset;
233
+ top = y;
234
+ break;
235
+ case 'left-end':
236
+ left = x - width - offset;
237
+ top = y - height;
238
+ break;
239
+ }
240
+
241
+ // Ensure popover stays within viewport
242
+ const padding = 8;
243
+ left = Math.max(padding, Math.min(left, window.innerWidth - width - padding));
244
+ top = Math.max(padding, Math.min(top, window.innerHeight - height - padding));
245
+
246
+ return { left, top };
247
+ }
248
+
249
+ _addGlobalListeners() {
250
+ // Use setTimeout to avoid immediate triggering
251
+ setTimeout(() => {
252
+ document.addEventListener('keydown', this._handleEscKey);
253
+ }, 0);
254
+ }
255
+
256
+ _removeGlobalListeners() {
257
+ document.removeEventListener('keydown', this._handleEscKey);
258
+ }
259
+
260
+
261
+ _handleEscKey(e) {
262
+ if (e.key === 'Escape') {
263
+ this.dispatchEvent(new CustomEvent('close', {
264
+ detail: {}
265
+ }));
266
+ }
267
+ }
268
+
269
+ // Expose popover container for advanced usage
270
+ get popover() {
271
+ return this._popoverContainer;
272
+ }
273
+ }
274
+
275
+ // Export factory function to maintain API compatibility
276
+ export default ({ render, html }) => {
277
+ // Note: render and html parameters are accepted but not used
278
+ // This maintains backward compatibility with existing code
279
+ return RettangoliPopoverElement;
280
+ };