@rettangoli/ui 0.1.7 → 0.1.9

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.7",
3
+ "version": "0.1.9",
4
4
  "description": "A UI component library for building web interfaces.",
5
5
  "main": "dist/rettangoli-esm.min.js",
6
6
  "type": "module",
@@ -49,7 +49,7 @@
49
49
  "homepage": "https://github.com/yuusoft-org/rettangoli#readme",
50
50
  "dependencies": {
51
51
  "@floating-ui/dom": "^1.6.13",
52
- "@rettangoli/fe": "0.0.7-rc16",
52
+ "@rettangoli/fe": "0.0.9",
53
53
  "commander": "^13.1.0",
54
54
  "jempl": "0.1.4-rc1",
55
55
  "js-yaml": "^4.1.0",
@@ -4,14 +4,14 @@ export const handleBeforeMount = (deps) => {
4
4
  };
5
5
 
6
6
  export const handleOnUpdate = (deps, payload) => {
7
- const { oldAttrs, newAttrs } = payload;
8
- const { store, props, render } = deps;
7
+ const { oldAttrs, newAttrs, newProps } = payload;
8
+ const { store, render } = deps;
9
9
 
10
10
  if (oldAttrs?.key === newAttrs?.key) {
11
11
  return;
12
12
  }
13
13
 
14
- store.setFormValues(props.defaultValues);
14
+ store.setFormValues(newProps.defaultValues);
15
15
  render();
16
16
  };
17
17
 
@@ -167,8 +167,8 @@ export const getFormFieldValue = ({ state }, name) => {
167
167
  return get(state.formValues, name);
168
168
  };
169
169
 
170
- export const setFormValues = (state, defaultValues) => {
171
- state.formValues = defaultValues || {};
170
+ export const setFormValues = (state, formValues) => {
171
+ state.formValues = formValues || {};
172
172
  };
173
173
 
174
174
  export const setFormFieldValue = (state, { name, value, props }) => {
@@ -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,41 @@ 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
+ // Close on backdrop clicks (when clicking outside the popover content)
80
+ const path = e.composedPath();
81
+ const clickedOnBackdrop = path[0] === this._dialogElement ||
82
+ (path[0].nodeName === 'DIALOG' && path[0] === this._dialogElement);
83
+
84
+ if (clickedOnBackdrop) {
85
+ this.dispatchEvent(new CustomEvent('close', {
86
+ detail: {}
87
+ }));
88
+ }
89
+ });
72
90
 
73
- // Handle overlay clicks to close popover
74
- this._popoverOverlay.addEventListener('click', () => {
75
- this.dispatchEvent(new CustomEvent('close', {
76
- detail: {}
77
- }));
91
+ // Handle right-click on backdrop to close popover
92
+ this._dialogElement.addEventListener('contextmenu', (e) => {
93
+ // Close on backdrop right-clicks
94
+ const path = e.composedPath();
95
+ const clickedOnBackdrop = path[0] === this._dialogElement ||
96
+ (path[0].nodeName === 'DIALOG' && path[0] === this._dialogElement);
97
+
98
+ if (clickedOnBackdrop) {
99
+ e.preventDefault();
100
+ this.dispatchEvent(new CustomEvent('close', {
101
+ detail: {}
102
+ }));
103
+ }
78
104
  });
79
105
 
80
- // Handle right-click on overlay to close popover
81
- this._popoverOverlay.addEventListener('contextmenu', (e) => {
106
+ // Handle ESC key - prevent native close and emit custom event
107
+ this._dialogElement.addEventListener('cancel', (e) => {
82
108
  e.preventDefault();
83
109
  this.dispatchEvent(new CustomEvent('close', {
84
110
  detail: {}
@@ -88,20 +114,17 @@ class RettangoliPopoverElement extends HTMLElement {
88
114
  // Create popover container
89
115
  this._popoverContainer = document.createElement('div');
90
116
  this._popoverContainer.className = 'popover-container';
91
- this.shadow.appendChild(this._popoverContainer);
117
+ this._dialogElement.appendChild(this._popoverContainer);
92
118
 
93
119
  // Store reference for content slot
94
120
  this._slotElement = null;
95
121
 
96
122
  // Track if we're open
97
123
  this._isOpen = false;
98
-
99
- // Bind event handlers
100
- this._handleEscKey = this._handleEscKey.bind(this);
101
124
  }
102
125
 
103
126
  static get observedAttributes() {
104
- return ["open", "x", "y", "placement", "no-overlay"];
127
+ return ["open", "x", "y", "placement"];
105
128
  }
106
129
 
107
130
  connectedCallback() {
@@ -112,14 +135,19 @@ class RettangoliPopoverElement extends HTMLElement {
112
135
  }
113
136
 
114
137
  disconnectedCallback() {
115
- // Clean up event listeners
116
- this._removeGlobalListeners();
138
+ // Clean up dialog if it's open
139
+ if (this._isOpen && this._dialogElement.open) {
140
+ this._dialogElement.close();
141
+ }
117
142
  }
118
143
 
119
144
  attributeChangedCallback(name, oldValue, newValue) {
120
145
  if (name === 'open') {
121
146
  if (newValue !== null && !this._isOpen) {
122
- this._show();
147
+ // Only show if element is connected to DOM
148
+ if (this.isConnected) {
149
+ this._show();
150
+ }
123
151
  } else if (newValue === null && this._isOpen) {
124
152
  this._hide();
125
153
  }
@@ -138,8 +166,20 @@ class RettangoliPopoverElement extends HTMLElement {
138
166
  }
139
167
 
140
168
  this._isOpen = true;
141
- this._updatePosition();
142
- this._addGlobalListeners();
169
+
170
+ // Show the dialog using setTimeout to ensure it's in the DOM
171
+ if (!this._dialogElement.open) {
172
+ setTimeout(() => {
173
+ if (this._dialogElement && !this._dialogElement.open) {
174
+ this._dialogElement.showModal();
175
+ }
176
+ }, 0);
177
+ }
178
+
179
+ // Update position after dialog is shown
180
+ requestAnimationFrame(() => {
181
+ this._updatePosition();
182
+ });
143
183
  }
144
184
  }
145
185
 
@@ -147,13 +187,16 @@ class RettangoliPopoverElement extends HTMLElement {
147
187
  if (this._isOpen) {
148
188
  this._isOpen = false;
149
189
 
190
+ // Close the dialog
191
+ if (this._dialogElement.open) {
192
+ this._dialogElement.close();
193
+ }
194
+
150
195
  // Remove slot to unmount content
151
196
  if (this._slotElement) {
152
197
  this._popoverContainer.removeChild(this._slotElement);
153
198
  this._slotElement = null;
154
199
  }
155
-
156
- this._removeGlobalListeners();
157
200
  }
158
201
  }
159
202
 
@@ -246,26 +289,7 @@ class RettangoliPopoverElement extends HTMLElement {
246
289
  return { left, top };
247
290
  }
248
291
 
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
-
292
+
269
293
  // Expose popover container for advanced usage
270
294
  get popover() {
271
295
  return this._popoverContainer;