@rettangoli/ui 1.2.1 → 1.2.3

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": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "A UI component library for building web interfaces.",
5
5
  "main": "dist/rettangoli-esm.min.js",
6
6
  "type": "module",
@@ -14,25 +14,24 @@ styles:
14
14
  text-decoration: none
15
15
  color: inherit
16
16
  template:
17
- - rtgl-popover#popover ?open=${open} x=${x} y=${y} place=${place}:
18
- - rtgl-view w=${w} h=${h} sv g=xs slot=content bgc=bg pv=sm:
19
- - $for item, i in items:
20
- - $if item.isLabel:
21
- - rtgl-view w=f ph=lg pv=md:
22
- - rtgl-text s=sm c=mu-fg: ${item.label}
23
- $elif item.isItem && item.isDisabled:
24
- - rtgl-view w=f ph=lg pv=md br=md bgc=${item.bgc}:
25
- - rtgl-text c=${item.c}: ${item.label}
26
- $elif item.isItem && item.hasHref && item.linkExtraAttrs:
27
- - a#option${i} href=${item.href} ${item.linkExtraAttrs} data-index=${item.index} data-testid=${item.testId}:
28
- - rtgl-view w=f h-bgc=${item.hoverBgc} ph=lg pv=md cur=pointer br=md bgc=${item.bgc}:
29
- - rtgl-text c=${item.c}: ${item.label}
30
- $elif item.isItem && item.hasHref:
31
- - a#option${i} href=${item.href} data-index=${item.index} data-testid=${item.testId}:
32
- - rtgl-view w=f h-bgc=${item.hoverBgc} ph=lg pv=md cur=pointer br=md bgc=${item.bgc}:
33
- - rtgl-text c=${item.c}: ${item.label}
34
- $elif item.isItem:
35
- - rtgl-view#option${i} w=f h-bgc=${item.hoverBgc} ph=lg pv=md cur=pointer br=md bgc=${item.bgc} data-index=${item.index} data-testid=${item.testId}:
36
- - rtgl-text c=${item.c}: ${item.label}
37
- $elif item.isSeparator:
38
- - rtgl-view w=f h=1 ph=lg mv=md bgc=bo: null
17
+ - rtgl-popover#popover ?open=${open} x=${x} y=${y} place=${place} content-w=${w} content-h=${h} content-sv=true content-g=xs content-pv=sm:
18
+ - $for item, i in items:
19
+ - $if item.isLabel:
20
+ - rtgl-view w=f p=md:
21
+ - rtgl-text s=sm c=mu-fg: ${item.label}
22
+ $elif item.isItem && item.isDisabled:
23
+ - rtgl-view w=f p=md bgc=${item.bgc}:
24
+ - rtgl-text s=sm c=${item.c}: ${item.label}
25
+ $elif item.isItem && item.hasHref && item.linkExtraAttrs:
26
+ - a#option${i} href=${item.href} ${item.linkExtraAttrs} data-index=${item.index} data-testid=${item.testId}:
27
+ - rtgl-view w=f h-bgc=${item.hoverBgc} p=md cur=pointer bgc=${item.bgc}:
28
+ - rtgl-text s=sm c=${item.c}: ${item.label}
29
+ $elif item.isItem && item.hasHref:
30
+ - a#option${i} href=${item.href} data-index=${item.index} data-testid=${item.testId}:
31
+ - rtgl-view w=f h-bgc=${item.hoverBgc} p=md cur=pointer bgc=${item.bgc}:
32
+ - rtgl-text s=sm c=${item.c}: ${item.label}
33
+ $elif item.isItem:
34
+ - rtgl-view#option${i} w=f h-bgc=${item.hoverBgc} p=md cur=pointer bgc=${item.bgc} data-index=${item.index} data-testid=${item.testId}:
35
+ - rtgl-text s=sm c=${item.c}: ${item.label}
36
+ $elif item.isSeparator:
37
+ - rtgl-view w=f h=1 bgc=mu mv=sm: null
@@ -20,9 +20,8 @@ refs:
20
20
  template:
21
21
  - rtgl-view#textDisplay w=f cur=pointer:
22
22
  - rtgl-text c=${valueColor}: ${value}
23
- - rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y}:
24
- - rtgl-view g=md w=240 slot=content bgc=bg bw=xs bc=bo br=md pv=sm:
25
- - rtgl-text: ${label}
26
- - rtgl-input#input w=f placeholder=${placeholder} ?disabled=${disabled}: null
27
- - rtgl-view w=f ah=e:
28
- - rtgl-button#submit ?disabled=${disabled}: Submit
23
+ - rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y} content-g=md content-w=240 content-pv=sm:
24
+ - rtgl-text: ${label}
25
+ - rtgl-input#input w=f placeholder=${placeholder} ?disabled=${disabled}: null
26
+ - rtgl-view w=f ah=e:
27
+ - rtgl-button#submit ?disabled=${disabled}: Submit
@@ -36,12 +36,11 @@ template:
36
36
  - $if showClear:
37
37
  - rtgl-svg#clearButton ml=md svg=x wh=16 c=mu-fg cur=pointer data-testid="select-clear-button": null
38
38
  - rtgl-svg ml=md svg=chevronDown wh=16 c=mu-fg: null
39
- - rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y} place=rs:
40
- - rtgl-view wh=300 g=xs slot=content bgc=bg sv=true pv=sm:
41
- - $for option, i in options:
42
- - rtgl-view#option${i} w=f ph=lg pv=md cur=pointer bgc=${option.bgc} data-testid=${option.testId}:
43
- - rtgl-text s=sm: ${option.label}
44
- - $if showAddOption:
45
- - rtgl-view w=f bw=xs bc=mu bwt=sm: null
46
- - rtgl-view#optionAdd w=f ph=lg pv=md cur=pointer bgc=${addOptionBgc} data-testid="select-add-option":
47
- - rtgl-text s=sm c=ac: ${addOptionLabel}
39
+ - rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y} place=rs content-wh=300 content-g=xs content-sv=true content-pv=sm:
40
+ - $for option, i in options:
41
+ - rtgl-view#option${i} w=f ph=lg pv=md cur=pointer bgc=${option.bgc} data-testid=${option.testId}:
42
+ - rtgl-text s=sm: ${option.label}
43
+ - $if showAddOption:
44
+ - rtgl-view w=f bw=xs bc=mu bwt=sm: null
45
+ - rtgl-view#optionAdd w=f ph=lg pv=md cur=pointer bgc=${addOptionBgc} data-testid="select-add-option":
46
+ - rtgl-text s=sm c=ac: ${addOptionLabel}
@@ -31,6 +31,6 @@ export const selectViewData = ({ props }) => {
31
31
  content: props.content || '',
32
32
  textSize: preset.textSize,
33
33
  padding: preset.padding,
34
- contentStyle: `padding: 0; min-width: 0; max-width: ${preset.maxWidth}; background-color: var(--muted);`,
34
+ contentStyle: `padding: 0; min-width: 0; max-width: ${preset.maxWidth};`,
35
35
  };
36
36
  }
@@ -1,6 +1,5 @@
1
1
  template:
2
- - rtgl-popover#popover ?open=${open} x=${x} y=${y} place=${place} no-overlay:
3
- - 'rtgl-view slot=content style="${contentStyle}" bgc=mu':
4
- - rtgl-view p=${padding} ah=s av=c:
5
- - rtgl-text ta=s s=${textSize} c=fg: ${content}
2
+ - 'rtgl-popover#popover ?open=${open} x=${x} y=${y} place=${place} no-overlay content-bgc=mu content-style="${contentStyle}"':
3
+ - rtgl-view p=${padding} ah=s av=c:
4
+ - rtgl-text ta=s s=${textSize} c=fg: ${content}
6
5
  refs: {}
@@ -1,5 +1,8 @@
1
1
  import { css } from "../common.js";
2
2
 
3
+ const CONTENT_WRAPPER_ATTR = "data-rtgl-popover-content";
4
+ const DEFAULT_CONTENT_STYLE = "min-width: 200px; max-width: 400px; box-sizing: border-box;";
5
+
3
6
  class RettangoliPopoverElement extends HTMLElement {
4
7
  static styleSheet = null;
5
8
 
@@ -55,17 +58,6 @@ class RettangoliPopoverElement extends HTMLElement {
55
58
  slot[name="content"] {
56
59
  display: contents;
57
60
  }
58
-
59
- ::slotted([slot="content"]) {
60
- display: block;
61
- box-sizing: border-box;
62
- background-color: var(--background);
63
- border: 1px solid var(--border);
64
- border-radius: var(--border-radius-md);
65
- padding: var(--spacing-md);
66
- min-width: 200px;
67
- max-width: 400px;
68
- }
69
61
  `);
70
62
  }
71
63
  }
@@ -119,6 +111,7 @@ class RettangoliPopoverElement extends HTMLElement {
119
111
 
120
112
  // Store reference for content slot
121
113
  this._slotElement = null;
114
+ this._contentWrapper = null;
122
115
 
123
116
  // Track if we're open
124
117
  this._isOpen = false;
@@ -132,10 +125,26 @@ class RettangoliPopoverElement extends HTMLElement {
132
125
  }
133
126
 
134
127
  static get observedAttributes() {
135
- return ["open", "x", "y", "place", "no-overlay"];
128
+ return [
129
+ "open",
130
+ "x",
131
+ "y",
132
+ "place",
133
+ "no-overlay",
134
+ "content-w",
135
+ "content-h",
136
+ "content-wh",
137
+ "content-g",
138
+ "content-sv",
139
+ "content-pv",
140
+ "content-bgc",
141
+ "content-style",
142
+ ];
136
143
  }
137
144
 
138
145
  connectedCallback() {
146
+ this._syncContentWrapper({ reposition: false });
147
+
139
148
  // Check initial open attribute
140
149
  if (this.hasAttribute('open')) {
141
150
  this._show();
@@ -164,11 +173,103 @@ class RettangoliPopoverElement extends HTMLElement {
164
173
  } else if (name === 'no-overlay' && oldValue !== newValue && this._isOpen) {
165
174
  this._hide();
166
175
  this._show();
176
+ } else if (name.startsWith("content-")) {
177
+ this._syncContentWrapper();
178
+ }
179
+ }
180
+
181
+ _isIgnorableTextNode(node) {
182
+ return node?.nodeType === Node.TEXT_NODE && node.textContent?.trim() === "";
183
+ }
184
+
185
+ _ensureContentWrapper() {
186
+ if (this._contentWrapper?.parentNode === this) {
187
+ return this._contentWrapper;
188
+ }
189
+
190
+ const existingWrapper = Array.from(this.children).find((child) => child.hasAttribute(CONTENT_WRAPPER_ATTR));
191
+
192
+ if (existingWrapper) {
193
+ this._contentWrapper = existingWrapper;
194
+ } else {
195
+ this._contentWrapper = document.createElement("rtgl-view");
196
+ this._contentWrapper.setAttribute(CONTENT_WRAPPER_ATTR, "");
197
+ this._contentWrapper.setAttribute("part", "content");
198
+ this._contentWrapper.setAttribute("bgc", "bg");
199
+ this._contentWrapper.setAttribute("bw", "xs");
200
+ this._contentWrapper.setAttribute("bc", "bo");
201
+ this._contentWrapper.setAttribute("br", "md");
202
+ this._contentWrapper.setAttribute("ph", "md");
203
+ this._contentWrapper.setAttribute("pv", "md");
204
+ this._contentWrapper.setAttribute("style", DEFAULT_CONTENT_STYLE);
205
+ }
206
+
207
+ if (this._contentWrapper.parentNode !== this) {
208
+ this.appendChild(this._contentWrapper);
209
+ }
210
+
211
+ return this._contentWrapper;
212
+ }
213
+
214
+ _syncContentWrapperAttributes() {
215
+ const wrapper = this._ensureContentWrapper();
216
+ const attrs = [
217
+ ["content-w", "w"],
218
+ ["content-h", "h"],
219
+ ["content-wh", "wh"],
220
+ ["content-g", "g"],
221
+ ["content-sv", "sv"],
222
+ ];
223
+
224
+ for (const [sourceAttr, targetAttr] of attrs) {
225
+ const value = this.getAttribute(sourceAttr);
226
+
227
+ if (value === null) {
228
+ wrapper.removeAttribute(targetAttr);
229
+ } else {
230
+ wrapper.setAttribute(targetAttr, value);
231
+ }
232
+ }
233
+
234
+ wrapper.setAttribute("bgc", this.getAttribute("content-bgc") || "bg");
235
+ wrapper.setAttribute("ph", "md");
236
+ wrapper.setAttribute("pv", this.getAttribute("content-pv") || "md");
237
+
238
+ const contentStyle = this.getAttribute("content-style");
239
+ wrapper.setAttribute("style", contentStyle ? `${DEFAULT_CONTENT_STYLE} ${contentStyle}` : DEFAULT_CONTENT_STYLE);
240
+ }
241
+
242
+ _syncContentWrapper({ reposition = true } = {}) {
243
+ const wrapper = this._ensureContentWrapper();
244
+ const nodesToWrap = Array.from(this.childNodes).filter((node) => node !== wrapper && !this._isIgnorableTextNode(node));
245
+
246
+ for (const node of nodesToWrap) {
247
+ if (node.nodeType === Node.ELEMENT_NODE && node.getAttribute("slot") === "content") {
248
+ node.removeAttribute("slot");
249
+ }
250
+
251
+ wrapper.appendChild(node);
252
+ }
253
+
254
+ this._syncContentWrapperAttributes();
255
+
256
+ const hasContent = Array.from(wrapper.childNodes).some((node) => !this._isIgnorableTextNode(node));
257
+
258
+ if (hasContent) {
259
+ wrapper.setAttribute("slot", "content");
260
+ } else {
261
+ wrapper.removeAttribute("slot");
262
+ }
263
+
264
+ if (reposition && this._isOpen) {
265
+ this._updatePosition();
167
266
  }
168
267
  }
169
268
 
170
269
  _show() {
171
270
  if (!this._isOpen) {
271
+ this._syncContentWrapper({ reposition: false });
272
+
172
273
  // Create and append slot for content only if it doesn't exist
173
274
  if (!this._slotElement) {
174
275
  this._slotElement = document.createElement('slot');
@@ -226,6 +327,7 @@ class RettangoliPopoverElement extends HTMLElement {
226
327
  // Calculate position based on place
227
328
  // We'll position after the popover is rendered to get its dimensions
228
329
  requestAnimationFrame(() => {
330
+ this._syncContentWrapper({ reposition: false });
229
331
  const rect = this._popoverContainer.getBoundingClientRect();
230
332
  const { left, top } = this._calculatePosition(x, y, rect.width, rect.height, place);
231
333
 
@@ -313,6 +415,10 @@ class RettangoliPopoverElement extends HTMLElement {
313
415
  get popover() {
314
416
  return this._popoverContainer;
315
417
  }
418
+
419
+ get content() {
420
+ return this._contentWrapper;
421
+ }
316
422
  }
317
423
 
318
424
  // Export factory function to maintain API compatibility