@rettangoli/ui 0.1.31 → 1.0.0-rc1

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 (89) hide show
  1. package/README.md +20 -85
  2. package/dist/rettangoli-iife-layout.min.js +113 -173
  3. package/dist/rettangoli-iife-ui.min.js +123 -183
  4. package/package.json +5 -4
  5. package/src/common/dimensions.js +72 -0
  6. package/src/common/link.js +111 -0
  7. package/src/common/responsive.js +8 -0
  8. package/src/common.js +43 -8
  9. package/src/components/accordionItem/accordionItem.handlers.js +1 -1
  10. package/src/components/accordionItem/accordionItem.schema.yaml +14 -0
  11. package/src/components/accordionItem/accordionItem.store.js +8 -8
  12. package/src/components/accordionItem/accordionItem.view.yaml +5 -35
  13. package/src/components/breadcrumb/breadcrumb.handlers.js +24 -3
  14. package/src/components/breadcrumb/breadcrumb.schema.yaml +51 -0
  15. package/src/components/breadcrumb/breadcrumb.store.js +66 -10
  16. package/src/components/breadcrumb/breadcrumb.view.yaml +18 -58
  17. package/src/components/dropdownMenu/dropdownMenu.handlers.js +17 -3
  18. package/src/components/dropdownMenu/dropdownMenu.schema.yaml +64 -0
  19. package/src/components/dropdownMenu/dropdownMenu.store.js +48 -6
  20. package/src/components/dropdownMenu/dropdownMenu.view.yaml +24 -46
  21. package/src/components/form/form.handlers.js +25 -108
  22. package/src/components/form/form.schema.yaml +283 -0
  23. package/src/components/form/form.store.js +19 -14
  24. package/src/components/form/form.view.yaml +28 -319
  25. package/src/components/globalUi/globalUi.handlers.js +2 -2
  26. package/src/components/globalUi/globalUi.schema.yaml +8 -0
  27. package/src/components/globalUi/globalUi.store.js +8 -8
  28. package/src/components/globalUi/globalUi.view.yaml +9 -46
  29. package/src/components/navbar/navbar.handlers.js +1 -1
  30. package/src/components/navbar/navbar.schema.yaml +25 -0
  31. package/src/components/navbar/navbar.store.js +28 -14
  32. package/src/components/navbar/navbar.view.yaml +21 -65
  33. package/src/components/pageOutline/pageOutline.handlers.js +17 -11
  34. package/src/components/pageOutline/pageOutline.schema.yaml +16 -0
  35. package/src/components/pageOutline/pageOutline.store.js +6 -7
  36. package/src/components/pageOutline/pageOutline.view.yaml +1 -29
  37. package/src/components/popoverInput/popoverInput.handlers.js +31 -31
  38. package/src/components/popoverInput/popoverInput.schema.yaml +18 -0
  39. package/src/components/popoverInput/popoverInput.store.js +9 -9
  40. package/src/components/popoverInput/popoverInput.view.yaml +5 -22
  41. package/src/components/select/select.handlers.js +31 -35
  42. package/src/components/select/select.schema.yaml +36 -0
  43. package/src/components/select/select.store.js +34 -35
  44. package/src/components/select/select.view.yaml +13 -56
  45. package/src/components/sidebar/sidebar.handlers.js +5 -5
  46. package/src/components/sidebar/sidebar.schema.yaml +57 -0
  47. package/src/components/sidebar/sidebar.store.js +45 -23
  48. package/src/components/sidebar/sidebar.view.yaml +79 -174
  49. package/src/components/sliderInput/sliderInput.handlers.js +28 -8
  50. package/src/components/sliderInput/sliderInput.schema.yaml +27 -0
  51. package/src/components/sliderInput/sliderInput.store.js +9 -9
  52. package/src/components/sliderInput/sliderInput.view.yaml +8 -33
  53. package/src/components/table/table.handlers.js +3 -3
  54. package/src/components/table/table.schema.yaml +27 -0
  55. package/src/components/table/table.store.js +8 -8
  56. package/src/components/table/table.view.yaml +16 -62
  57. package/src/components/tabs/tabs.schema.yaml +26 -0
  58. package/src/components/tabs/tabs.store.js +12 -9
  59. package/src/components/tabs/tabs.view.yaml +4 -60
  60. package/src/components/tooltip/tooltip.schema.yaml +18 -0
  61. package/src/components/tooltip/tooltip.store.js +7 -7
  62. package/src/components/tooltip/tooltip.view.yaml +4 -22
  63. package/src/components/waveform/waveform.handlers.js +6 -6
  64. package/src/components/waveform/waveform.schema.yaml +25 -0
  65. package/src/components/waveform/waveform.store.js +6 -6
  66. package/src/components/waveform/waveform.view.yaml +6 -34
  67. package/src/deps/createGlobalUI.js +2 -2
  68. package/src/primitives/button.js +200 -114
  69. package/src/primitives/colorPicker.js +56 -50
  70. package/src/primitives/dialog.js +2 -1
  71. package/src/primitives/image.js +73 -103
  72. package/src/primitives/input-number.js +139 -93
  73. package/src/primitives/input.js +87 -64
  74. package/src/primitives/popover.js +36 -28
  75. package/src/primitives/slider.js +6 -4
  76. package/src/primitives/svg.js +9 -10
  77. package/src/primitives/text.js +26 -47
  78. package/src/primitives/textarea.js +25 -9
  79. package/src/primitives/view.js +49 -90
  80. package/src/setup.js +1 -7
  81. package/src/styles/buttonMarginStyles.js +1 -13
  82. package/src/styles/cursorStyles.js +1 -5
  83. package/src/styles/flexDirectionStyles.js +4 -4
  84. package/src/styles/marginStylesForTarget.js +13 -0
  85. package/src/styles/textColorStyles.js +14 -6
  86. package/src/styles/textStyles.js +4 -4
  87. package/src/styles/viewStyles.js +6 -6
  88. package/src/styles/viewStylesForTarget.js +58 -0
  89. package/src/styles/flexChildStyles.js +0 -43
@@ -2,16 +2,26 @@ import {
2
2
  css,
3
3
  dimensionWithUnit,
4
4
  convertObjectToCssString,
5
- styleMapKeys,
6
5
  permutateBreakpoints,
6
+ createResponsiveStyleBuckets,
7
+ responsiveStyleSizes,
8
+ applyDimensionToStyleBucket,
7
9
  } from "../common.js";
8
10
  import cursorStyles from "../styles/cursorStyles.js";
9
11
  import marginStyles from "../styles/marginStyles.js";
10
12
 
13
+ const inputStyleMapKeys = ["mt", "mr", "mb", "ml", "m", "mh", "mv", "cur"];
14
+
11
15
  // Internal implementation without uhtml
12
16
  class RettangoliInputElement extends HTMLElement {
13
17
  static styleSheet = null;
14
18
 
19
+ static inputSpecificAttributes = [
20
+ "type",
21
+ "disabled",
22
+ "s",
23
+ ];
24
+
15
25
  static initializeStyleSheet() {
16
26
  if (!RettangoliInputElement.styleSheet) {
17
27
  RettangoliInputElement.styleSheet = new CSSStyleSheet();
@@ -61,13 +71,7 @@ class RettangoliInputElement extends HTMLElement {
61
71
  this.shadow.adoptedStyleSheets = [RettangoliInputElement.styleSheet];
62
72
 
63
73
  // Initialize style tracking properties
64
- this._styles = {
65
- default: {},
66
- sm: {},
67
- md: {},
68
- lg: {},
69
- xl: {},
70
- };
74
+ this._styles = createResponsiveStyleBuckets();
71
75
  this._lastStyleString = "";
72
76
 
73
77
  // Create initial DOM structure
@@ -77,8 +81,9 @@ class RettangoliInputElement extends HTMLElement {
77
81
  this.shadow.appendChild(this._styleElement);
78
82
  this.shadow.appendChild(this._inputElement);
79
83
 
80
- // Bind event handler
81
- this._inputElement.addEventListener('input', this._onChange);
84
+ // Bind event handlers
85
+ this._inputElement.addEventListener('input', this._onInput);
86
+ this._inputElement.addEventListener('change', this._onChange);
82
87
  }
83
88
 
84
89
  static get observedAttributes() {
@@ -90,7 +95,7 @@ class RettangoliInputElement extends HTMLElement {
90
95
  "value",
91
96
  "s",
92
97
  ...permutateBreakpoints([
93
- ...styleMapKeys,
98
+ ...inputStyleMapKeys,
94
99
  "wh",
95
100
  "w",
96
101
  "h",
@@ -114,49 +119,58 @@ class RettangoliInputElement extends HTMLElement {
114
119
  this._inputElement.focus();
115
120
  }
116
121
 
117
- _onChange = (event) => {
118
- this.dispatchEvent(new CustomEvent('input-change', {
122
+ _onInput = () => {
123
+ this.dispatchEvent(new CustomEvent('value-input', {
119
124
  detail: {
120
125
  value: this._inputElement.value,
121
126
  },
127
+ bubbles: true,
128
+ }));
129
+ };
130
+
131
+ _onChange = () => {
132
+ this.dispatchEvent(new CustomEvent('value-change', {
133
+ detail: {
134
+ value: this._inputElement.value,
135
+ },
136
+ bubbles: true,
122
137
  }));
123
138
  };
124
139
 
125
140
  attributeChangedCallback(name, oldValue, newValue) {
126
- if (name === 'value') {
127
- requestAnimationFrame((() => {
128
- const value = this.getAttribute("value");
129
- this._inputElement.value = value ?? "";
130
- }))
141
+ if (oldValue === newValue) {
142
+ return;
131
143
  }
132
144
 
133
- if (name === 'placeholder') {
134
- requestAnimationFrame((() => {
135
- const placeholder = this.getAttribute("placeholder");
136
- if (placeholder === undefined || placeholder === 'null') {
137
- this._inputElement.removeAttribute('placeholder');
138
- } else {
139
- this._inputElement.setAttribute('placeholder', placeholder ?? "");
140
- }
141
- }))
145
+ if (name === "key") {
146
+ this._syncValueAttribute();
147
+ return;
148
+ }
149
+
150
+ if (name === "value") {
151
+ this._syncValueAttribute();
152
+ return;
153
+ }
154
+
155
+ if (name === "placeholder") {
156
+ this._syncPlaceholderAttribute();
157
+ return;
142
158
  }
143
159
 
144
160
  // Handle input-specific attributes first
145
- if (["type", "disabled", "step", "s"].includes(name)) {
161
+ if (RettangoliInputElement.inputSpecificAttributes.includes(name)) {
146
162
  this._updateInputAttributes();
147
163
  return;
148
164
  }
149
165
 
166
+ this.updateStyles();
167
+ }
168
+
169
+ updateStyles() {
150
170
  // Reset styles for fresh calculation
151
- this._styles = {
152
- default: {},
153
- sm: {},
154
- md: {},
155
- lg: {},
156
- xl: {},
157
- };
158
-
159
- ["default", "sm", "md", "lg", "xl"].forEach((size) => {
171
+ this._styles = createResponsiveStyleBuckets();
172
+
173
+ responsiveStyleSizes.forEach((size) => {
160
174
  const addSizePrefix = (tag) => {
161
175
  return `${size === "default" ? "" : `${size}-`}${tag}`;
162
176
  };
@@ -179,28 +193,26 @@ class RettangoliInputElement extends HTMLElement {
179
193
  this._styles[size].opacity = opacity;
180
194
  }
181
195
 
182
- if (width === "f") {
183
- this._styles[size].width = "var(--width-stretch)";
184
- } else if (width !== undefined) {
185
- this._styles[size].width = width;
186
- this._styles[size]["min-width"] = width;
187
- this._styles[size]["max-width"] = width;
188
- }
196
+ applyDimensionToStyleBucket({
197
+ styleBucket: this._styles[size],
198
+ axis: "width",
199
+ dimension: width,
200
+ fillValue: "var(--width-stretch)",
201
+ });
189
202
 
190
- if (height === "f") {
191
- this._styles[size].height = "100%";
192
- } else if (height !== undefined) {
193
- this._styles[size].height = height;
194
- this._styles[size]["min-height"] = height;
195
- this._styles[size]["max-height"] = height;
196
- }
203
+ applyDimensionToStyleBucket({
204
+ styleBucket: this._styles[size],
205
+ axis: "height",
206
+ dimension: height,
207
+ fillValue: "100%",
208
+ });
197
209
 
198
210
  if (this.hasAttribute(addSizePrefix("hide"))) {
199
- this._styles[size].display = "none !important";
211
+ this._styles[size].display = "none";
200
212
  }
201
213
 
202
214
  if (this.hasAttribute(addSizePrefix("show"))) {
203
- this._styles[size].display = "block !important";
215
+ this._styles[size].display = "block";
204
216
  }
205
217
  });
206
218
 
@@ -212,20 +224,28 @@ class RettangoliInputElement extends HTMLElement {
212
224
  }
213
225
  }
214
226
 
227
+ _setOrRemoveInputAttribute(name, value) {
228
+ if (value === null || value === undefined || value === "null") {
229
+ this._inputElement.removeAttribute(name);
230
+ return;
231
+ }
232
+ this._inputElement.setAttribute(name, value);
233
+ }
234
+
235
+ _syncValueAttribute() {
236
+ this._inputElement.value = this.getAttribute("value") ?? "";
237
+ }
238
+
239
+ _syncPlaceholderAttribute() {
240
+ this._setOrRemoveInputAttribute("placeholder", this.getAttribute("placeholder"));
241
+ }
242
+
215
243
  _updateInputAttributes() {
216
- const type = this.getAttribute("type") || "text";
217
- // const placeholder = this.getAttribute("placeholder");
218
- // const value = this.getAttribute("value");
219
- const step = this.getAttribute("step");
244
+ const requestedType = this.getAttribute("type");
245
+ const type = requestedType === "password" ? "password" : "text";
220
246
  const isDisabled = this.hasAttribute('disabled');
221
247
 
222
- this._inputElement.setAttribute("type", type);
223
-
224
- if (step !== null) {
225
- this._inputElement.setAttribute("step", step);
226
- } else {
227
- this._inputElement.removeAttribute("step");
228
- }
248
+ this._setOrRemoveInputAttribute("type", type);
229
249
 
230
250
  if (isDisabled) {
231
251
  this._inputElement.setAttribute("disabled", "");
@@ -236,6 +256,9 @@ class RettangoliInputElement extends HTMLElement {
236
256
 
237
257
  connectedCallback() {
238
258
  this._updateInputAttributes();
259
+ this._syncValueAttribute();
260
+ this._syncPlaceholderAttribute();
261
+ this.updateStyles();
239
262
  }
240
263
  }
241
264
 
@@ -83,9 +83,7 @@ class RettangoliPopoverElement extends HTMLElement {
83
83
  (path[0].nodeName === 'DIALOG' && path[0] === this._dialogElement);
84
84
 
85
85
  if (clickedOnBackdrop) {
86
- this.dispatchEvent(new CustomEvent('close', {
87
- detail: {}
88
- }));
86
+ this._emitClose();
89
87
  }
90
88
  });
91
89
 
@@ -98,18 +96,14 @@ class RettangoliPopoverElement extends HTMLElement {
98
96
 
99
97
  if (clickedOnBackdrop) {
100
98
  e.preventDefault();
101
- this.dispatchEvent(new CustomEvent('close', {
102
- detail: {}
103
- }));
99
+ this._emitClose();
104
100
  }
105
101
  });
106
102
 
107
103
  // Handle ESC key - prevent native close and emit custom event
108
104
  this._dialogElement.addEventListener('cancel', (e) => {
109
105
  e.preventDefault();
110
- this.dispatchEvent(new CustomEvent('close', {
111
- detail: {}
112
- }));
106
+ this._emitClose();
113
107
  });
114
108
 
115
109
  // Create popover container
@@ -124,8 +118,15 @@ class RettangoliPopoverElement extends HTMLElement {
124
118
  this._isOpen = false;
125
119
  }
126
120
 
121
+ _emitClose() {
122
+ this.dispatchEvent(new CustomEvent('close', {
123
+ detail: {},
124
+ bubbles: true,
125
+ }));
126
+ }
127
+
127
128
  static get observedAttributes() {
128
- return ["open", "x", "y", "placement"];
129
+ return ["open", "x", "y", "place", "no-overlay"];
129
130
  }
130
131
 
131
132
  connectedCallback() {
@@ -152,8 +153,11 @@ class RettangoliPopoverElement extends HTMLElement {
152
153
  } else if (newValue === null && this._isOpen) {
153
154
  this._hide();
154
155
  }
155
- } else if ((name === 'x' || name === 'y' || name === 'placement') && this._isOpen) {
156
+ } else if ((name === 'x' || name === 'y' || name === 'place') && this._isOpen) {
156
157
  this._updatePosition();
158
+ } else if (name === 'no-overlay' && oldValue !== newValue && this._isOpen) {
159
+ this._hide();
160
+ this._show();
157
161
  }
158
162
  }
159
163
 
@@ -208,16 +212,16 @@ class RettangoliPopoverElement extends HTMLElement {
208
212
  _updatePosition() {
209
213
  const x = parseFloat(this.getAttribute('x') || '0');
210
214
  const y = parseFloat(this.getAttribute('y') || '0');
211
- const placement = this.getAttribute('placement') || 'bottom-start';
215
+ const place = this.getAttribute('place') || 'bs';
212
216
 
213
217
  // Remove positioned attribute to hide during repositioning
214
218
  this.removeAttribute('positioned');
215
219
 
216
- // Calculate position based on placement
220
+ // Calculate position based on place
217
221
  // We'll position after the popover is rendered to get its dimensions
218
222
  requestAnimationFrame(() => {
219
223
  const rect = this._popoverContainer.getBoundingClientRect();
220
- const { left, top } = this._calculatePosition(x, y, rect.width, rect.height, placement);
224
+ const { left, top } = this._calculatePosition(x, y, rect.width, rect.height, place);
221
225
 
222
226
  // Set position first
223
227
  this._popoverContainer.style.left = `${left}px`;
@@ -230,60 +234,64 @@ class RettangoliPopoverElement extends HTMLElement {
230
234
  });
231
235
  }
232
236
 
233
- _calculatePosition(x, y, width, height, placement) {
237
+ _calculatePosition(x, y, width, height, place) {
234
238
  const offset = 8; // Small offset from the cursor
235
239
  let left = x;
236
240
  let top = y;
237
241
 
238
- switch (placement) {
239
- case 'top':
242
+ switch (place) {
243
+ case 't':
240
244
  left = x - width / 2;
241
245
  top = y - height - offset;
242
246
  break;
243
- case 'top-start':
247
+ case 'ts':
244
248
  left = x;
245
249
  top = y - height - offset;
246
250
  break;
247
- case 'top-end':
251
+ case 'te':
248
252
  left = x - width;
249
253
  top = y - height - offset;
250
254
  break;
251
- case 'right':
255
+ case 'r':
252
256
  left = x + offset;
253
257
  top = y - height / 2;
254
258
  break;
255
- case 'right-start':
259
+ case 'rs':
256
260
  left = x + offset;
257
261
  top = y;
258
262
  break;
259
- case 'right-end':
263
+ case 're':
260
264
  left = x + offset;
261
265
  top = y - height;
262
266
  break;
263
- case 'bottom':
267
+ case 'b':
264
268
  left = x - width / 2;
265
269
  top = y + offset;
266
270
  break;
267
- case 'bottom-start':
271
+ case 'bs':
268
272
  left = x;
269
273
  top = y + offset;
270
274
  break;
271
- case 'bottom-end':
275
+ case 'be':
272
276
  left = x - width;
273
277
  top = y + offset;
274
278
  break;
275
- case 'left':
279
+ case 'l':
276
280
  left = x - width - offset;
277
281
  top = y - height / 2;
278
282
  break;
279
- case 'left-start':
283
+ case 'ls':
280
284
  left = x - width - offset;
281
285
  top = y;
282
286
  break;
283
- case 'left-end':
287
+ case 'le':
284
288
  left = x - width - offset;
285
289
  top = y - height;
286
290
  break;
291
+ default:
292
+ left = x;
293
+ top = y + offset;
294
+ break;
287
295
  }
288
296
 
289
297
  // Ensure popover stays within viewport
@@ -132,18 +132,20 @@ class RettangoliSliderElement extends HTMLElement {
132
132
  }
133
133
 
134
134
  _onInput = () => {
135
- this.dispatchEvent(new CustomEvent('slider-input', {
135
+ this.dispatchEvent(new CustomEvent('value-input', {
136
136
  detail: {
137
137
  value: Number(this._inputElement.value),
138
138
  },
139
+ bubbles: true,
139
140
  }));
140
141
  };
141
142
 
142
143
  _onChange = () => {
143
- this.dispatchEvent(new CustomEvent('slider-change', {
144
+ this.dispatchEvent(new CustomEvent('value-change', {
144
145
  detail: {
145
146
  value: Number(this._inputElement.value),
146
147
  },
148
+ bubbles: true,
147
149
  }));
148
150
  };
149
151
 
@@ -213,11 +215,11 @@ class RettangoliSliderElement extends HTMLElement {
213
215
  }
214
216
 
215
217
  if (this.hasAttribute(addSizePrefix("hide"))) {
216
- this._styles[size].display = "none !important";
218
+ this._styles[size].display = "none";
217
219
  }
218
220
 
219
221
  if (this.hasAttribute(addSizePrefix("show"))) {
220
- this._styles[size].display = "block !important";
222
+ this._styles[size].display = "block";
221
223
  }
222
224
  });
223
225
 
@@ -1,5 +1,4 @@
1
1
  import { css, dimensionWithUnit } from "../common.js";
2
- import flexChildStyles from "../styles/flexChildStyles.js";
3
2
  import paddingSvgStyles from "../styles/paddingSvgStyles.js";
4
3
  import marginStyles from "../styles/marginStyles.js";
5
4
  import cursorStyles from "../styles/cursorStyles.js";
@@ -21,7 +20,6 @@ class RettangoliSvgElement extends HTMLElement {
21
20
  ${textColorStyles}
22
21
  ${paddingSvgStyles}
23
22
  ${marginStyles}
24
- ${flexChildStyles}
25
23
  ${cursorStyles}
26
24
  `);
27
25
  }
@@ -58,18 +56,19 @@ class RettangoliSvgElement extends HTMLElement {
58
56
 
59
57
  _updateSizing() {
60
58
  const wh = this.getAttribute("wh");
61
- const width = dimensionWithUnit(
62
- wh === null ? this.getAttribute("w") : wh
63
- );
64
- const height = dimensionWithUnit(
65
- wh === null ? this.getAttribute("h") : wh
66
- );
59
+ const width = dimensionWithUnit(wh === null ? this.getAttribute("w") : wh);
60
+ const height = dimensionWithUnit(wh === null ? this.getAttribute("h") : wh);
67
61
 
68
- if (width) {
62
+ if (width != null) {
69
63
  this.style.width = width;
64
+ } else {
65
+ this.style.width = "";
70
66
  }
71
- if (height) {
67
+
68
+ if (height != null) {
72
69
  this.style.height = height;
70
+ } else {
71
+ this.style.height = "";
73
72
  }
74
73
  }
75
74
 
@@ -1,4 +1,10 @@
1
- import { css, dimensionWithUnit } from "../common.js";
1
+ import {
2
+ css,
3
+ dimensionWithUnit,
4
+ overlayLinkStyles,
5
+ syncLinkOverlay,
6
+ applyInlineWidthDimension,
7
+ } from "../common.js";
2
8
  import cursorStyles from "../styles/cursorStyles.js";
3
9
  import textStyles from "../styles/textStyles.js";
4
10
  import textColorStyles from "../styles/textColorStyles.js";
@@ -30,18 +36,7 @@ class RettangoliTextElement extends HTMLElement {
30
36
  text-decoration: var(--anchor-text-decoration-hover);
31
37
  color: var(--anchor-color-hover);
32
38
  }
33
- :host([href]) {
34
- cursor: pointer;
35
- position: relative;
36
- }
37
- :host([href]) a {
38
- position: absolute;
39
- top: 0;
40
- left: 0;
41
- right: 0;
42
- bottom: 0;
43
- z-index: 1;
44
- }
39
+ ${overlayLinkStyles}
45
40
  ${textStyles}
46
41
  ${textColorStyles}
47
42
  ${marginStyles}
@@ -63,7 +58,7 @@ class RettangoliTextElement extends HTMLElement {
63
58
  }
64
59
 
65
60
  static get observedAttributes() {
66
- return ["key", "w", "ellipsis", "href", "target"];
61
+ return ["key", "w", "ellipsis", "href", "new-tab", "rel"];
67
62
  }
68
63
 
69
64
  connectedCallback() {
@@ -72,7 +67,7 @@ class RettangoliTextElement extends HTMLElement {
72
67
  }
73
68
 
74
69
  attributeChangedCallback(name, oldValue, newValue) {
75
- if (name === "href" || name === "target") {
70
+ if (name === "href" || name === "new-tab" || name === "rel") {
76
71
  this._updateDOM();
77
72
  } else {
78
73
  this._updateStyling();
@@ -93,43 +88,27 @@ class RettangoliTextElement extends HTMLElement {
93
88
  this.style.whiteSpace = "";
94
89
  }
95
90
 
96
- if (width === "f") {
97
- this.style.width = "var(--width-stretch)";
98
- } else if (width !== undefined) {
99
- this.style.width = width;
100
- } else {
101
- this.style.width = "";
102
- }
91
+ // Allow shrinking in flex layouts so ellipsis and wrapping constraints work predictably.
92
+ applyInlineWidthDimension({
93
+ style: this.style,
94
+ width,
95
+ flexMinWidth: "0",
96
+ });
103
97
  }
104
98
 
105
99
  _updateDOM() {
106
100
  const href = this.getAttribute("href");
107
- const target = this.getAttribute("target");
101
+ const newTab = this.hasAttribute("new-tab");
102
+ const rel = this.getAttribute("rel");
108
103
 
109
- // Ensure slot is always in the shadow DOM
110
- if (this._slotElement.parentNode !== this.shadow) {
111
- this.shadow.appendChild(this._slotElement);
112
- }
113
-
114
- if (href) {
115
- if (!this._linkElement) {
116
- // Create link overlay only if it doesn't exist
117
- this._linkElement = document.createElement("a");
118
- this.shadow.appendChild(this._linkElement);
119
- }
120
-
121
- // Update link attributes
122
- this._linkElement.href = href;
123
- if (target) {
124
- this._linkElement.target = target;
125
- } else {
126
- this._linkElement.removeAttribute("target");
127
- }
128
- } else if (this._linkElement) {
129
- // Remove link overlay
130
- this.shadow.removeChild(this._linkElement);
131
- this._linkElement = null;
132
- }
104
+ this._linkElement = syncLinkOverlay({
105
+ shadowRoot: this.shadow,
106
+ slotElement: this._slotElement,
107
+ linkElement: this._linkElement,
108
+ href,
109
+ newTab,
110
+ rel,
111
+ });
133
112
  }
134
113
  }
135
114
 
@@ -62,34 +62,42 @@ class RettangoliTextAreaElement extends HTMLElement {
62
62
 
63
63
  // Create initial DOM structure
64
64
  this._textareaElement = document.createElement('textarea');
65
- this._textareaElement.setAttribute('type', 'text');
66
65
  this._styleElement = document.createElement('style');
67
66
 
68
67
  this.shadow.appendChild(this._styleElement);
69
68
  this.shadow.appendChild(this._textareaElement);
70
69
 
71
- // Bind event handler
72
- this._textareaElement.addEventListener('input', this._onChange);
70
+ // Bind event handlers
71
+ this._textareaElement.addEventListener('input', this._onInput);
72
+ this._textareaElement.addEventListener('change', this._onChange);
73
73
  }
74
74
 
75
- _onChange = (event) => {
76
- this.dispatchEvent(new CustomEvent('textarea-change', {
75
+ _onInput = () => {
76
+ this.dispatchEvent(new CustomEvent('value-input', {
77
77
  detail: {
78
78
  value: this._textareaElement.value,
79
79
  },
80
+ bubbles: true,
81
+ }));
82
+ };
83
+
84
+ _onChange = () => {
85
+ this.dispatchEvent(new CustomEvent('value-change', {
86
+ detail: {
87
+ value: this._textareaElement.value,
88
+ },
89
+ bubbles: true,
80
90
  }));
81
91
  };
82
92
 
83
93
  static get observedAttributes() {
84
94
  return [
85
95
  "key",
86
- "type",
87
96
  "placeholder",
88
97
  "disabled",
89
98
  "value",
90
99
  "cols",
91
100
  "rows",
92
- "ellipsis",
93
101
  ...permutateBreakpoints([
94
102
  ...styleMapKeys,
95
103
  "wh",
@@ -129,6 +137,14 @@ class RettangoliTextAreaElement extends HTMLElement {
129
137
  }
130
138
 
131
139
  attributeChangedCallback(name, oldValue, newValue) {
140
+ if (name === "key") {
141
+ requestAnimationFrame(() => {
142
+ const value = this.getAttribute("value");
143
+ this._textareaElement.value = value ?? "";
144
+ });
145
+ return;
146
+ }
147
+
132
148
  if (name === 'value') {
133
149
  requestAnimationFrame((() => {
134
150
  const value = this.getAttribute("value");
@@ -202,11 +218,11 @@ class RettangoliTextAreaElement extends HTMLElement {
202
218
  }
203
219
 
204
220
  if (this.hasAttribute(addSizePrefix("hide"))) {
205
- this._styles[size].display = "none !important";
221
+ this._styles[size].display = "none";
206
222
  }
207
223
 
208
224
  if (this.hasAttribute(addSizePrefix("show"))) {
209
- this._styles[size].display = "block !important";
225
+ this._styles[size].display = "block";
210
226
  }
211
227
  });
212
228