@rettangoli/ui 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/ui",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A UI component library for building web interfaces.",
5
5
  "main": "dist/rettangoli-esm.min.js",
6
6
  "type": "module",
@@ -38,6 +38,7 @@ export const handleOnUpdate = (deps, payload) => {
38
38
  export const handleButtonClick = (deps, payload) => {
39
39
  const { store, render, getRefIds, props } = deps;
40
40
  const event = payload._event;
41
+ event.stopPropagation();
41
42
 
42
43
  const button = getRefIds()['select-button'].elm;
43
44
 
@@ -73,6 +74,7 @@ export const handleClickOptionsPopoverOverlay = (deps, payload) => {
73
74
  export const handleOptionClick = (deps, payload) => {
74
75
  const { render, dispatchEvent, props, store } = deps;
75
76
  const event = payload._event;
77
+ event.stopPropagation();
76
78
  const id = event.currentTarget.id.replace('option-', '');
77
79
 
78
80
  const option = props.options[id];
@@ -145,7 +147,8 @@ export const handleClearClick = (deps, payload) => {
145
147
 
146
148
  export const handleAddOptionClick = (deps, payload) => {
147
149
  const { store, render, dispatchEvent } = deps;
148
-
150
+ const { _event: event } = payload;
151
+ event.stopPropagation();
149
152
  // Close the popover
150
153
  store.closeOptionsPopover();
151
154
 
@@ -12,7 +12,7 @@ function flattenItems(items, selectedItemId = null) {
12
12
  for (const item of items) {
13
13
  const itemId = item.id || item.href || item.path;
14
14
  const isSelected = selectedItemId === itemId;
15
-
15
+
16
16
  // Add the parent item if it's not just a group label
17
17
  result.push({
18
18
  id: itemId,
@@ -31,7 +31,7 @@ function flattenItems(items, selectedItemId = null) {
31
31
  for (const subItem of item.items) {
32
32
  const subItemId = subItem.id || subItem.href || subItem.path;
33
33
  const isSubSelected = selectedItemId === subItemId;
34
-
34
+
35
35
  result.push({
36
36
  id: subItemId,
37
37
  title: subItem.title,
@@ -139,4 +139,4 @@ export const selectItem = ({ state, props, attrs }, id) => {
139
139
 
140
140
  export const setState = (state) => {
141
141
  // State management if needed
142
- }
142
+ }
@@ -11,36 +11,41 @@ class RettangoliPopoverElement extends HTMLElement {
11
11
  display: contents;
12
12
  }
13
13
 
14
- .popover-overlay {
14
+ dialog {
15
+ padding: 0;
16
+ border: none;
17
+ background: transparent;
18
+ margin: 0;
19
+ overflow: visible;
20
+ color: inherit;
21
+ scrollbar-width: none;
22
+ outline: none;
15
23
  position: fixed;
16
24
  top: 0;
17
25
  left: 0;
18
26
  width: 100vw;
19
27
  height: 100vh;
20
- z-index: 999;
21
- display: none;
28
+ /* Prevent dialog from being focused */
29
+ pointer-events: none;
30
+ }
31
+
32
+ dialog::backdrop {
33
+ background-color: transparent;
34
+ /* Allow backdrop to receive clicks */
35
+ pointer-events: auto;
22
36
  }
23
37
 
24
38
  .popover-container {
25
39
  position: fixed;
26
40
  z-index: 1000;
27
- display: none;
28
41
  outline: none;
29
- }
30
-
31
- :host([open]:not([no-overlay])) .popover-overlay {
32
- display: block;
42
+ pointer-events: auto;
33
43
  }
34
44
 
35
45
  :host([open]) .popover-container {
36
46
  display: block;
37
47
  visibility: hidden;
38
48
  }
39
-
40
- /* For no-overlay mode, make the container non-interactive */
41
- :host([no-overlay]) .popover-container {
42
- pointer-events: none;
43
- }
44
49
 
45
50
  :host([open][positioned]) .popover-container {
46
51
  visibility: visible;
@@ -65,20 +70,42 @@ class RettangoliPopoverElement extends HTMLElement {
65
70
  this.shadow = this.attachShadow({ mode: "open" });
66
71
  this.shadow.adoptedStyleSheets = [RettangoliPopoverElement.styleSheet];
67
72
 
68
- // Create overlay
69
- this._popoverOverlay = document.createElement('div');
70
- this._popoverOverlay.className = 'popover-overlay';
71
- this.shadow.appendChild(this._popoverOverlay);
73
+ // Create dialog element
74
+ this._dialogElement = document.createElement('dialog');
75
+ this.shadow.appendChild(this._dialogElement);
76
+
77
+ // Handle dialog backdrop clicks to close popover
78
+ this._dialogElement.addEventListener('click', (e) => {
79
+ e.stopPropagation();
80
+ // Close on backdrop clicks (when clicking outside the popover content)
81
+ const path = e.composedPath();
82
+ const clickedOnBackdrop = path[0] === this._dialogElement ||
83
+ (path[0].nodeName === 'DIALOG' && path[0] === this._dialogElement);
84
+
85
+ if (clickedOnBackdrop) {
86
+ this.dispatchEvent(new CustomEvent('close', {
87
+ detail: {}
88
+ }));
89
+ }
90
+ });
72
91
 
73
- // Handle overlay clicks to close popover
74
- this._popoverOverlay.addEventListener('click', () => {
75
- this.dispatchEvent(new CustomEvent('close', {
76
- detail: {}
77
- }));
92
+ // Handle right-click on backdrop to close popover
93
+ this._dialogElement.addEventListener('contextmenu', (e) => {
94
+ // Close on backdrop right-clicks
95
+ const path = e.composedPath();
96
+ const clickedOnBackdrop = path[0] === this._dialogElement ||
97
+ (path[0].nodeName === 'DIALOG' && path[0] === this._dialogElement);
98
+
99
+ if (clickedOnBackdrop) {
100
+ e.preventDefault();
101
+ this.dispatchEvent(new CustomEvent('close', {
102
+ detail: {}
103
+ }));
104
+ }
78
105
  });
79
106
 
80
- // Handle right-click on overlay to close popover
81
- this._popoverOverlay.addEventListener('contextmenu', (e) => {
107
+ // Handle ESC key - prevent native close and emit custom event
108
+ this._dialogElement.addEventListener('cancel', (e) => {
82
109
  e.preventDefault();
83
110
  this.dispatchEvent(new CustomEvent('close', {
84
111
  detail: {}
@@ -88,20 +115,17 @@ class RettangoliPopoverElement extends HTMLElement {
88
115
  // Create popover container
89
116
  this._popoverContainer = document.createElement('div');
90
117
  this._popoverContainer.className = 'popover-container';
91
- this.shadow.appendChild(this._popoverContainer);
118
+ this._dialogElement.appendChild(this._popoverContainer);
92
119
 
93
120
  // Store reference for content slot
94
121
  this._slotElement = null;
95
122
 
96
123
  // Track if we're open
97
124
  this._isOpen = false;
98
-
99
- // Bind event handlers
100
- this._handleEscKey = this._handleEscKey.bind(this);
101
125
  }
102
126
 
103
127
  static get observedAttributes() {
104
- return ["open", "x", "y", "placement", "no-overlay"];
128
+ return ["open", "x", "y", "placement"];
105
129
  }
106
130
 
107
131
  connectedCallback() {
@@ -112,14 +136,19 @@ class RettangoliPopoverElement extends HTMLElement {
112
136
  }
113
137
 
114
138
  disconnectedCallback() {
115
- // Clean up event listeners
116
- this._removeGlobalListeners();
139
+ // Clean up dialog if it's open
140
+ if (this._isOpen && this._dialogElement.open) {
141
+ this._dialogElement.close();
142
+ }
117
143
  }
118
144
 
119
145
  attributeChangedCallback(name, oldValue, newValue) {
120
146
  if (name === 'open') {
121
147
  if (newValue !== null && !this._isOpen) {
122
- this._show();
148
+ // Only show if element is connected to DOM
149
+ if (this.isConnected) {
150
+ this._show();
151
+ }
123
152
  } else if (newValue === null && this._isOpen) {
124
153
  this._hide();
125
154
  }
@@ -138,8 +167,20 @@ class RettangoliPopoverElement extends HTMLElement {
138
167
  }
139
168
 
140
169
  this._isOpen = true;
141
- this._updatePosition();
142
- this._addGlobalListeners();
170
+
171
+ // Show the dialog using setTimeout to ensure it's in the DOM
172
+ if (!this._dialogElement.open) {
173
+ setTimeout(() => {
174
+ if (this._dialogElement && !this._dialogElement.open) {
175
+ this._dialogElement.showModal();
176
+ }
177
+ }, 0);
178
+ }
179
+
180
+ // Update position after dialog is shown
181
+ requestAnimationFrame(() => {
182
+ this._updatePosition();
183
+ });
143
184
  }
144
185
  }
145
186
 
@@ -147,13 +188,16 @@ class RettangoliPopoverElement extends HTMLElement {
147
188
  if (this._isOpen) {
148
189
  this._isOpen = false;
149
190
 
191
+ // Close the dialog
192
+ if (this._dialogElement.open) {
193
+ this._dialogElement.close();
194
+ }
195
+
150
196
  // Remove slot to unmount content
151
197
  if (this._slotElement) {
152
198
  this._popoverContainer.removeChild(this._slotElement);
153
199
  this._slotElement = null;
154
200
  }
155
-
156
- this._removeGlobalListeners();
157
201
  }
158
202
  }
159
203
 
@@ -246,25 +290,6 @@ class RettangoliPopoverElement extends HTMLElement {
246
290
  return { left, top };
247
291
  }
248
292
 
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
293
 
269
294
  // Expose popover container for advanced usage
270
295
  get popover() {