@moni-labs/moni-ui 0.4.2 → 0.4.4

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 (92) hide show
  1. package/README.md +6 -6
  2. package/custom-elements.json +61 -16
  3. package/dist/browser/moni-ui.iife.min.js +155 -6
  4. package/dist/browser/moni-ui.iife.min.js.br +0 -0
  5. package/dist/browser/moni-ui.iife.min.js.gz +0 -0
  6. package/dist/browser/moni-ui.iife.min.js.map +1 -1
  7. package/dist/browser/moni-ui.min.css +1 -1
  8. package/dist/browser/moni-ui.min.css.br +0 -0
  9. package/dist/browser/moni-ui.min.css.gz +0 -0
  10. package/dist/browser/moni-ui.min.css.map +1 -1
  11. package/dist/browser/moni-ui.min.js +189 -30
  12. package/dist/browser/moni-ui.min.js.br +0 -0
  13. package/dist/browser/moni-ui.min.js.gz +0 -0
  14. package/dist/browser/moni-ui.min.js.map +1 -1
  15. package/dist/cdn/chunks/{class-map-DBljv-_e.js → class-map-EVPcxmRq.js} +1 -1
  16. package/dist/cdn/chunks/{decorate-DxX3e27g.js → decorate-D4jKxi6H.js} +56 -0
  17. package/dist/cdn/chunks/if-defined-DlAUOFi9.js +1 -0
  18. package/dist/cdn/chunks/{live-B7Rjg1kt.js → live-DdUrWiUp.js} +1 -1
  19. package/dist/cdn/components/moni-app-bar.js +1 -1
  20. package/dist/cdn/components/moni-badge.js +1 -1
  21. package/dist/cdn/components/moni-bottom-sheet.js +1 -1
  22. package/dist/cdn/components/moni-button-group.js +72 -3
  23. package/dist/cdn/components/moni-button-segment.js +1 -1
  24. package/dist/cdn/components/moni-button.js +2 -1
  25. package/dist/cdn/components/moni-card.js +1 -1
  26. package/dist/cdn/components/moni-carousel.js +1 -1
  27. package/dist/cdn/components/moni-checkbox.js +1 -1
  28. package/dist/cdn/components/moni-chip.js +1 -1
  29. package/dist/cdn/components/moni-color-field.js +1 -1
  30. package/dist/cdn/components/moni-context-menu.js +1 -1
  31. package/dist/cdn/components/moni-dialog.js +1 -1
  32. package/dist/cdn/components/moni-divider.js +1 -1
  33. package/dist/cdn/components/moni-expansion.js +1 -1
  34. package/dist/cdn/components/moni-fab-menu.js +1 -1
  35. package/dist/cdn/components/moni-fab.js +1 -1
  36. package/dist/cdn/components/moni-file-field.js +1 -1
  37. package/dist/cdn/components/moni-icon.js +1 -1
  38. package/dist/cdn/components/moni-list-item.js +1 -1
  39. package/dist/cdn/components/moni-list.js +1 -1
  40. package/dist/cdn/components/moni-loading-indicator.js +22 -3
  41. package/dist/cdn/components/moni-menu-item.js +1 -1
  42. package/dist/cdn/components/moni-menu.js +1 -1
  43. package/dist/cdn/components/moni-morph-modal.js +1 -1
  44. package/dist/cdn/components/moni-nav-item.js +1 -1
  45. package/dist/cdn/components/moni-nav.js +1 -1
  46. package/dist/cdn/components/moni-progress.js +1 -1
  47. package/dist/cdn/components/moni-radio.js +1 -1
  48. package/dist/cdn/components/moni-ripple.js +1 -1
  49. package/dist/cdn/components/moni-segmented-button.js +1 -1
  50. package/dist/cdn/components/moni-select-option.js +1 -1
  51. package/dist/cdn/components/moni-select.js +1 -1
  52. package/dist/cdn/components/moni-shape.js +1 -1
  53. package/dist/cdn/components/moni-side-sheet.js +1 -1
  54. package/dist/cdn/components/moni-slider.js +1 -1
  55. package/dist/cdn/components/moni-snackbar.js +1 -1
  56. package/dist/cdn/components/moni-split-button.js +1 -1
  57. package/dist/cdn/components/moni-step.js +1 -1
  58. package/dist/cdn/components/moni-stepper.js +1 -1
  59. package/dist/cdn/components/moni-switch.js +1 -1
  60. package/dist/cdn/components/moni-tab.js +1 -1
  61. package/dist/cdn/components/moni-tabs.js +1 -1
  62. package/dist/cdn/components/moni-text-field.js +1 -1
  63. package/dist/cdn/components/moni-textarea.js +1 -1
  64. package/dist/cdn/components/moni-time-picker.js +6 -2
  65. package/dist/cdn/components/moni-toolbar.js +1 -1
  66. package/dist/cdn/components/moni-tooltip.js +1 -1
  67. package/dist/cdn/components/moni-typography.js +1 -1
  68. package/dist/cdn/importmap.json +98 -98
  69. package/dist/cdn/moni-ui.min.css +1 -1
  70. package/dist/components/_base/shared-styles.d.ts.map +1 -1
  71. package/dist/components/_base/shared-styles.js +56 -0
  72. package/dist/components/moni-button-group.d.ts +14 -0
  73. package/dist/components/moni-button-group.d.ts.map +1 -1
  74. package/dist/components/moni-button-group.js +130 -25
  75. package/dist/components/moni-button.d.ts.map +1 -1
  76. package/dist/components/moni-button.js +1 -0
  77. package/dist/components/moni-loading-indicator.d.ts.map +1 -1
  78. package/dist/components/moni-loading-indicator.js +21 -2
  79. package/dist/components/moni-time-picker.d.ts.map +1 -1
  80. package/dist/components/moni-time-picker.js +5 -1
  81. package/dist/styles/base.css +27 -7
  82. package/package.json +1 -1
  83. package/src/components/_base/shared-styles.test.ts +15 -0
  84. package/src/components/_base/shared-styles.ts +56 -0
  85. package/src/components/moni-button-group.test.ts +76 -0
  86. package/src/components/moni-button-group.ts +130 -34
  87. package/src/components/moni-button.ts +1 -0
  88. package/src/components/moni-loading-indicator.test.ts +9 -0
  89. package/src/components/moni-loading-indicator.ts +21 -2
  90. package/src/components/moni-time-picker.ts +5 -1
  91. package/src/styles/base.css +27 -7
  92. package/dist/cdn/chunks/if-defined-CuEAJKpp.js +0 -1
@@ -64,6 +64,7 @@ import { MoniElement, sharedStyles } from './_base/index.js';
64
64
  let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
65
65
  constructor() {
66
66
  super(...arguments);
67
+ this._activeObserver = new MutationObserver(() => this.updateSelectionNeighbors());
67
68
  /**
68
69
  * Variante visual del grupo de botones.
69
70
  * - `standard`: Los elementos se espacian normalmente.
@@ -83,6 +84,12 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
83
84
  * @type {boolean}
84
85
  */
85
86
  this.multi = false;
87
+ /** Impide que el grupo quede sin selección. */
88
+ this.selectionRequired = false;
89
+ /** Forma base común de los botones. La selección invierte round ↔ square. */
90
+ this.shape = 'round';
91
+ /** Controla si los botones conservan su ancho intrínseco o llenan la superficie. */
92
+ this.resizing = 'fixed';
86
93
  /**
87
94
  * Espacio CSS personalizado entre botones (ej., '1rem').
88
95
  * Solo se aplica cuando la variante es 'standard'.
@@ -106,6 +113,14 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
106
113
  */
107
114
  this.labelledBy = '';
108
115
  }
116
+ connectedCallback() {
117
+ super.connectedCallback();
118
+ this._activeObserver.observe(this, { subtree: true, attributes: true, attributeFilter: ['active'] });
119
+ }
120
+ disconnectedCallback() {
121
+ this._activeObserver.disconnect();
122
+ super.disconnectedCallback();
123
+ }
109
124
  static { this.styles = [
110
125
  sharedStyles,
111
126
  css `
@@ -113,12 +128,18 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
113
128
  display: inline-flex;
114
129
  align-items: center;
115
130
  vertical-align: middle;
131
+ max-inline-size: 100%;
132
+ }
133
+ :host([variant='connected']),
134
+ :host([resizing='flexible']) {
135
+ inline-size: 100%;
116
136
  }
117
137
 
118
138
  .group-container {
119
139
  display: inline-flex;
120
140
  align-items: center;
121
141
  width: 100%;
142
+ flex-wrap: nowrap;
122
143
  }
123
144
 
124
145
  slot {
@@ -148,6 +169,69 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
148
169
  :host([variant='connected']) .group-container {
149
170
  gap: 0.125rem; /* 2dp */
150
171
  }
172
+ :host([variant='connected']) ::slotted(moni-button),
173
+ :host([variant='connected']) ::slotted(moni-icon-button),
174
+ :host([resizing='flexible']) ::slotted(moni-button) {
175
+ flex: 1 1 0;
176
+ }
177
+ :host([variant='connected']) ::slotted(moni-button) {
178
+ --moni-button-inline-size: 100%;
179
+ min-inline-size: 0;
180
+ }
181
+ :host([variant='connected'][size='xsmall']) ::slotted(moni-button),
182
+ :host([variant='connected'][size='xsmall']) ::slotted(moni-icon-button),
183
+ :host([variant='connected'][size='small']) ::slotted(moni-button),
184
+ :host([variant='connected'][size='small']) ::slotted(moni-icon-button) {
185
+ min-inline-size: 3rem;
186
+ }
187
+
188
+ /* M3 Expressive selection emphasis. Increasing the button's internal
189
+ * space (instead of scaling the host) keeps labels and icons crisp and
190
+ * lets the existing padding transition transfer width smoothly. */
191
+ :host([variant='standard'][size='xsmall']) ::slotted(moni-button[active]),
192
+ :host([variant='standard'][size='xsmall']) ::slotted(moni-button[data-group-pressed]) {
193
+ --moni-button-padding: 0 1.25rem;
194
+ }
195
+ :host([variant='standard'][size='small']) ::slotted(moni-button[active]),
196
+ :host([variant='standard'][size='small']) ::slotted(moni-button[data-group-pressed]) {
197
+ --moni-button-padding: 0 1.75rem;
198
+ }
199
+ :host([variant='standard'][size='medium']) ::slotted(moni-button[active]),
200
+ :host([variant='standard'][size='medium']) ::slotted(moni-button[data-group-pressed]) {
201
+ --moni-button-padding: 0 2.375rem;
202
+ }
203
+ :host([variant='standard'][size='large']) ::slotted(moni-button[active]),
204
+ :host([variant='standard'][size='large']) ::slotted(moni-button[data-group-pressed]) {
205
+ --moni-button-padding: 0 4rem;
206
+ }
207
+ :host([variant='standard'][size='xlarge']) ::slotted(moni-button[active]),
208
+ :host([variant='standard'][size='extra']) ::slotted(moni-button[active]),
209
+ :host([variant='standard'][size='xlarge']) ::slotted(moni-button[data-group-pressed]),
210
+ :host([variant='standard'][size='extra']) ::slotted(moni-button[data-group-pressed]) {
211
+ --moni-button-padding: 0 5.25rem;
212
+ }
213
+
214
+ :host([variant='standard'][size='xsmall']) ::slotted(moni-button[data-group-adjacent-pressed]) { --moni-button-padding: 0 .5rem; }
215
+ :host([variant='standard'][size='small']) ::slotted(moni-button[data-group-adjacent-pressed]) { --moni-button-padding: 0 .75rem; }
216
+ :host([variant='standard'][size='medium']) ::slotted(moni-button[data-group-adjacent-pressed]) { --moni-button-padding: 0 1.25rem; }
217
+ :host([variant='standard'][size='large']) ::slotted(moni-button[data-group-adjacent-pressed]) { --moni-button-padding: 0 2.75rem; }
218
+ :host([variant='standard'][size='xlarge']) ::slotted(moni-button[data-group-adjacent-pressed]),
219
+ :host([variant='standard'][size='extra']) ::slotted(moni-button[data-group-adjacent-pressed]) { --moni-button-padding: 0 3.75rem; }
220
+
221
+ /* A selected standard button borrows space from its immediate
222
+ * neighbours, keeping the group's outside footprint stable. */
223
+ :host([variant='standard'][size='xsmall']) ::slotted(moni-button[data-group-adjacent-selected='shared']) { --moni-button-padding: 0 .5rem; }
224
+ :host([variant='standard'][size='xsmall']) ::slotted(moni-button[data-group-adjacent-selected='sole']) { --moni-button-padding: 0 .25rem; }
225
+ :host([variant='standard'][size='small']) ::slotted(moni-button[data-group-adjacent-selected='shared']) { --moni-button-padding: 0 .625rem; }
226
+ :host([variant='standard'][size='small']) ::slotted(moni-button[data-group-adjacent-selected='sole']) { --moni-button-padding: 0 .25rem; }
227
+ :host([variant='standard'][size='medium']) ::slotted(moni-button[data-group-adjacent-selected='shared']) { --moni-button-padding: 0 1.0625rem; }
228
+ :host([variant='standard'][size='medium']) ::slotted(moni-button[data-group-adjacent-selected='sole']) { --moni-button-padding: 0 .625rem; }
229
+ :host([variant='standard'][size='large']) ::slotted(moni-button[data-group-adjacent-selected='shared']) { --moni-button-padding: 0 2.5rem; }
230
+ :host([variant='standard'][size='large']) ::slotted(moni-button[data-group-adjacent-selected='sole']) { --moni-button-padding: 0 2rem; }
231
+ :host([variant='standard'][size='xlarge']) ::slotted(moni-button[data-group-adjacent-selected='shared']),
232
+ :host([variant='standard'][size='extra']) ::slotted(moni-button[data-group-adjacent-selected='shared']) { --moni-button-padding: 0 3.375rem; }
233
+ :host([variant='standard'][size='xlarge']) ::slotted(moni-button[data-group-adjacent-selected='sole']),
234
+ :host([variant='standard'][size='extra']) ::slotted(moni-button[data-group-adjacent-selected='sole']) { --moni-button-padding: 0 2.75rem; }
151
235
  `
152
236
  ]; }
153
237
  /**
@@ -157,7 +241,7 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
157
241
  */
158
242
  updated(changedProperties) {
159
243
  super.updated(changedProperties);
160
- if (changedProperties.has('variant') || changedProperties.has('size') || changedProperties.has('gap')) {
244
+ if (changedProperties.has('variant') || changedProperties.has('size') || changedProperties.has('gap') || changedProperties.has('shape')) {
161
245
  this.updateChildren();
162
246
  }
163
247
  }
@@ -169,6 +253,24 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
169
253
  handleSlotChange() {
170
254
  this.updateChildren();
171
255
  }
256
+ getButtons() {
257
+ return (this.slottedButtons ?? []).filter((el) => el.tagName.toLowerCase() === 'moni-button' || el.tagName.toLowerCase() === 'moni-icon-button');
258
+ }
259
+ /** Keeps selection emphasis inside the group's original visual footprint.
260
+ * The selected width is funded evenly by every unselected button so edge
261
+ * selections remain visually balanced instead of collapsing one neighbour. */
262
+ updateSelectionNeighbors() {
263
+ const buttons = this.getButtons();
264
+ buttons.forEach((button) => button.removeAttribute('data-group-adjacent-selected'));
265
+ if (this.variant !== 'standard')
266
+ return;
267
+ const selected = buttons.filter((button) => button.hasAttribute('active'));
268
+ if (selected.length === 0)
269
+ return;
270
+ const unselected = buttons.filter((button) => !button.hasAttribute('active'));
271
+ const weight = unselected.length === 1 ? 'sole' : 'shared';
272
+ unselected.forEach((button) => button.setAttribute('data-group-adjacent-selected', weight));
273
+ }
172
274
  getGapValue(gap) {
173
275
  if (!gap)
174
276
  return '';
@@ -193,14 +295,17 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
193
295
  * correspondiente (`left-round-flat`, `inner-round`, etc.) para formar una cápsula unificada.
194
296
  */
195
297
  updateChildren() {
196
- const buttons = this.slottedButtons.filter((el) => el.tagName.toLowerCase() === 'moni-button' || el.tagName.toLowerCase() === 'moni-icon-button');
298
+ const buttons = this.getButtons();
197
299
  const normalizedGap = this.gap.trim().toLowerCase();
198
300
  const hasConnectedGap = !['0', '0px', '0rem', '0em'].includes(normalizedGap);
199
301
  buttons.forEach((btn, index) => {
200
302
  // Propagate size
201
303
  btn.setAttribute('size', this.size);
202
304
  // Propagate shape for connected variant
203
- if (this.variant === 'connected') {
305
+ if (this.variant === 'connected' && this.shape === 'square') {
306
+ btn.setAttribute('shape', 'square');
307
+ }
308
+ else if (this.variant === 'connected') {
204
309
  if (hasConnectedGap) {
205
310
  if (buttons.length === 1) {
206
311
  btn.setAttribute('shape', 'round');
@@ -231,13 +336,10 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
231
336
  }
232
337
  }
233
338
  else {
234
- // Standard variant: buttons retain their own shapes or default round
235
- const currentShape = btn.getAttribute('shape');
236
- if (!currentShape || ['left-round-flat', 'right-round-flat', 'no-round', 'left-round', 'right-round', 'inner-round'].includes(currentShape)) {
237
- btn.setAttribute('shape', 'round');
238
- }
339
+ btn.setAttribute('shape', this.shape);
239
340
  }
240
341
  });
342
+ this.updateSelectionNeighbors();
241
343
  }
242
344
  /**
243
345
  * Maneja la interacción táctil o de mouse (Pointer Down).
@@ -252,22 +354,13 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
252
354
  const button = target.closest('moni-button, moni-icon-button');
253
355
  if (!button || button.hasAttribute('disabled'))
254
356
  return;
255
- const buttons = this.slottedButtons.filter((el) => el.tagName.toLowerCase() === 'moni-button' || el.tagName.toLowerCase() === 'moni-icon-button');
357
+ const buttons = this.getButtons();
256
358
  const index = buttons.indexOf(button);
257
359
  if (index === -1)
258
360
  return;
259
- const prev = buttons[index - 1];
260
- const next = buttons[index + 1];
261
- if (prev) {
262
- prev.style.transition = 'transform 200ms cubic-bezier(0.2, 0, 0, 1)';
263
- prev.style.transform = 'translateX(-6px) scaleX(0.92)';
264
- prev.style.transformOrigin = 'right center';
265
- }
266
- if (next) {
267
- next.style.transition = 'transform 200ms cubic-bezier(0.2, 0, 0, 1)';
268
- next.style.transform = 'translateX(6px) scaleX(0.92)';
269
- next.style.transformOrigin = 'left center';
270
- }
361
+ button.setAttribute('data-group-pressed', '');
362
+ buttons[index - 1]?.setAttribute('data-group-adjacent-pressed', '');
363
+ buttons[index + 1]?.setAttribute('data-group-adjacent-pressed', '');
271
364
  }
272
365
  /**
273
366
  * Conclusión de la interacción táctil (Pointer Up).
@@ -275,10 +368,10 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
275
368
  * a su estado nativo neutro.
276
369
  */
277
370
  handlePointerUp() {
278
- const buttons = this.slottedButtons.filter((el) => el.tagName.toLowerCase() === 'moni-button' || el.tagName.toLowerCase() === 'moni-icon-button');
371
+ const buttons = this.getButtons();
279
372
  buttons.forEach((btn) => {
280
- btn.style.transform = '';
281
- btn.style.transformOrigin = '';
373
+ btn.removeAttribute('data-group-pressed');
374
+ btn.removeAttribute('data-group-adjacent-pressed');
282
375
  });
283
376
  }
284
377
  /**
@@ -292,7 +385,7 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
292
385
  if (!clickedButton || clickedButton.hasAttribute('disabled')) {
293
386
  return;
294
387
  }
295
- const buttons = this.slottedButtons.filter((el) => el.tagName.toLowerCase() === 'moni-button' || el.tagName.toLowerCase() === 'moni-icon-button');
388
+ const buttons = this.getButtons();
296
389
  if (!this.multi) {
297
390
  buttons.forEach((btn) => {
298
391
  if (btn !== clickedButton) {
@@ -303,6 +396,9 @@ let MoniButtonGroup = class MoniButtonGroup extends MoniElement {
303
396
  }
304
397
  // Toggle clicked button active state
305
398
  const wasActive = clickedButton.hasAttribute('active');
399
+ const activeCount = buttons.filter((btn) => btn.hasAttribute('active')).length;
400
+ if (wasActive && this.selectionRequired && activeCount <= 1)
401
+ return;
306
402
  if (wasActive) {
307
403
  clickedButton.removeAttribute('active');
308
404
  clickedButton.active = false;
@@ -366,6 +462,15 @@ __decorate([
366
462
  __decorate([
367
463
  property({ type: Boolean, reflect: true })
368
464
  ], MoniButtonGroup.prototype, "multi", void 0);
465
+ __decorate([
466
+ property({ type: Boolean, reflect: true, attribute: 'selection-required' })
467
+ ], MoniButtonGroup.prototype, "selectionRequired", void 0);
468
+ __decorate([
469
+ property({ reflect: true })
470
+ ], MoniButtonGroup.prototype, "shape", void 0);
471
+ __decorate([
472
+ property({ reflect: true })
473
+ ], MoniButtonGroup.prototype, "resizing", void 0);
369
474
  __decorate([
370
475
  property()
371
476
  ], MoniButtonGroup.prototype, "gap", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"moni-button.d.ts","sourceRoot":"","sources":["../../src/components/moni-button.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,WAAW,EAAgB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,gBAAgB,CAAC;AACxB,OAAO,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,qBACa,UAAW,SAAQ,WAAW;IACjC,OAAO,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAS;IAE1C;;;;OAIG;IAEH,OAAO,EACJ,QAAQ,GACR,OAAO,GACP,UAAU,GACV,MAAM,GACN,MAAM,GACN,UAAU,GACV,OAAO,CAAY;IAEtB;;;;;;OAMG;IAEH,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAW;IAE7E;;;;OAIG;IAEH,KAAK,EACF,OAAO,GACP,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,YAAY,GACZ,aAAa,GACb,WAAW,GACX,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,aAAa,CAAW;IAE3B;;;OAGG;IACyC,QAAQ,UAAS;IAE7D;;;OAGG;IACyC,OAAO,UAAS;IAE5D;;;OAGG;IACyC,MAAM,UAAS;IAE3D;;;OAGG;IAC0B,IAAI,SAAM;IAEvC;;;OAGG;IACsD,YAAY,SAAM;IAE3E;;;;OAIG;IAEH,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAY;IAE/C;;;OAGG;IAC0B,IAAI,SAAM;IAEvC;;;OAGG;IAC0B,MAAM,SAAM;IAEhC,iBAAiB,IAAI,IAAI;IAclC,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,sBAAsB;IAK9B,OAAgB,MAAM,4BA6jBpB;IAEF;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACM,MAAM;CAmFf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,aAAa,EAAE,UAAU,CAAC;KAC1B;CACD;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"moni-button.d.ts","sourceRoot":"","sources":["../../src/components/moni-button.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,WAAW,EAAgB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,gBAAgB,CAAC;AACxB,OAAO,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,qBACa,UAAW,SAAQ,WAAW;IACjC,OAAO,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAS;IAE1C;;;;OAIG;IAEH,OAAO,EACJ,QAAQ,GACR,OAAO,GACP,UAAU,GACV,MAAM,GACN,MAAM,GACN,UAAU,GACV,OAAO,CAAY;IAEtB;;;;;;OAMG;IAEH,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAW;IAE7E;;;;OAIG;IAEH,KAAK,EACF,OAAO,GACP,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,YAAY,GACZ,aAAa,GACb,WAAW,GACX,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,aAAa,CAAW;IAE3B;;;OAGG;IACyC,QAAQ,UAAS;IAE7D;;;OAGG;IACyC,OAAO,UAAS;IAE5D;;;OAGG;IACyC,MAAM,UAAS;IAE3D;;;OAGG;IAC0B,IAAI,SAAM;IAEvC;;;OAGG;IACsD,YAAY,SAAM;IAE3E;;;;OAIG;IAEH,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAY;IAE/C;;;OAGG;IAC0B,IAAI,SAAM;IAEvC;;;OAGG;IAC0B,MAAM,SAAM;IAEhC,iBAAiB,IAAI,IAAI;IAclC,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,sBAAsB;IAK9B,OAAgB,MAAM,4BA8jBpB;IAEF;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACM,MAAM;CAmFf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,aAAa,EAAE,UAAU,CAAC;KAC1B;CACD;AAED,eAAe,UAAU,CAAC"}
@@ -172,6 +172,7 @@ let MoniButton = class MoniButton extends MoniElement {
172
172
  .button {
173
173
  box-sizing: var(--moni-button-box-sizing, border-box);
174
174
  display: inline-flex;
175
+ inline-size: var(--moni-button-inline-size, auto);
175
176
  --_button-visual-height: 3.5rem;
176
177
  align-items: center;
177
178
  justify-content: center;
@@ -1 +1 @@
1
- {"version":3,"file":"moni-loading-indicator.d.ts","sourceRoot":"","sources":["../../src/components/moni-loading-indicator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,WAAW,EAAgB,MAAM,kBAAkB,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBACa,oBAAqB,SAAQ,WAAW;IAEpD,OAAO,EAAE,aAAa,GAAG,WAAW,CAAiB;IAEhC,OAAO,CAAC,UAAU,CAAC,CAAc;IAEtD;;;;OAIG;IACM,iBAAiB;IAQ1B;;;;OAIG;IACM,oBAAoB;IAK7B;;;;OAIG;cACgB,YAAY;IAI/B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAMxB,OAAgB,MAAM,4BAkIpB;IAEF;;;;;;;;;;;OAWG;IACM,MAAM;CASf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,wBAAwB,EAAE,oBAAoB,CAAC;KAC/C;CACD;AAED,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"moni-loading-indicator.d.ts","sourceRoot":"","sources":["../../src/components/moni-loading-indicator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,WAAW,EAAgB,MAAM,kBAAkB,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBACa,oBAAqB,SAAQ,WAAW;IAEpD,OAAO,EAAE,aAAa,GAAG,WAAW,CAAiB;IAEhC,OAAO,CAAC,UAAU,CAAC,CAAc;IAEtD;;;;OAIG;IACM,iBAAiB;IAQ1B;;;;OAIG;IACM,oBAAoB;IAK7B;;;;OAIG;cACgB,YAAY;IAI/B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAMxB,OAAgB,MAAM,4BAmJpB;IAEF;;;;;;;;;;;OAWG;IACM,MAAM;CAWf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,wBAAwB,EAAE,oBAAoB,CAAC;KAC/C;CACD;AAED,eAAe,oBAAoB,CAAC"}
@@ -99,7 +99,11 @@ let MoniLoadingIndicator = class MoniLoadingIndicator extends MoniElement {
99
99
  :host {
100
100
  display: inline-block;
101
101
  aspect-ratio: 1 / 1;
102
- contain: strict;
102
+ /* Paint containment clipped oversized morphs in the contained
103
+ * variant. Keep layout/style isolation while allowing the active
104
+ * indicator to animate beyond the container surface. */
105
+ contain: layout style;
106
+ overflow: visible;
103
107
  vertical-align: middle;
104
108
  }
105
109
 
@@ -131,6 +135,19 @@ let MoniLoadingIndicator = class MoniLoadingIndicator extends MoniElement {
131
135
  justify-content: center;
132
136
  border-radius: var(--moni-loading-indicator-container-shape, 9999px);
133
137
  box-sizing: border-box;
138
+ overflow: visible;
139
+ }
140
+
141
+ .active-indicator-wrapper {
142
+ overflow: visible;
143
+ }
144
+
145
+ .active-indicator-scale {
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ overflow: visible;
150
+ transform-origin: center;
134
151
  }
135
152
 
136
153
  .active-indicator {
@@ -240,7 +257,9 @@ let MoniLoadingIndicator = class MoniLoadingIndicator extends MoniElement {
240
257
  return html `
241
258
  <div class="container animate" aria-hidden="true">
242
259
  <div class="active-indicator-wrapper">
243
- <div class="active-indicator"></div>
260
+ <div class="active-indicator-scale">
261
+ <div class="active-indicator"></div>
262
+ </div>
244
263
  </div>
245
264
  </div>
246
265
  `;
@@ -1 +1 @@
1
- {"version":3,"file":"moni-time-picker.d.ts","sourceRoot":"","sources":["../../src/components/moni-time-picker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAa,cAAc,EAAE,MAAM,KAAK,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAgB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,gBAAgB,CAAC;AACxB,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBACa,cAAe,SAAQ,WAAW;IACjB,KAAK,SAAW;IACyB,SAAS,UAAS;IAC3D,IAAI,EAAE,MAAM,GAAG,OAAO,CAAU;IAChC,WAAW,EAAE,UAAU,GAAG,YAAY,GAAG,MAAM,CAAU;IACX,cAAc,UAAS;IAC1B,YAAY,UAAS;IACvC,SAAS,SAAU;IACjB,WAAW,SAAY;IAEtE,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAAK;IAEb,OAAO,CAAC,WAAW,CAAC,CAAiB;IAE3D,OAAO,CAAC,UAAU,CAAS;IAE3B;;;;;;OAMG;cACgB,UAAU,CAAC,iBAAiB,EAAE,cAAc;IAM/D;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAoBlB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAwBnB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAIpB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAMjB;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAQ9B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAgD9B;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAiB7B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAY/B,OAAgB,MAAM,4BAiZpB;IAEF;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAwI3B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACM,MAAM;CAsFf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,kBAAkB,EAAE,cAAc,CAAC;KACnC;CACD;AAED,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"moni-time-picker.d.ts","sourceRoot":"","sources":["../../src/components/moni-time-picker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAa,cAAc,EAAE,MAAM,KAAK,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAgB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,gBAAgB,CAAC;AACxB,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBACa,cAAe,SAAQ,WAAW;IACjB,KAAK,SAAW;IACyB,SAAS,UAAS;IAC3D,IAAI,EAAE,MAAM,GAAG,OAAO,CAAU;IAChC,WAAW,EAAE,UAAU,GAAG,YAAY,GAAG,MAAM,CAAU;IACX,cAAc,UAAS;IAC1B,YAAY,UAAS;IACvC,SAAS,SAAU;IACjB,WAAW,SAAY;IAEtE,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAAK;IAEb,OAAO,CAAC,WAAW,CAAC,CAAiB;IAE3D,OAAO,CAAC,UAAU,CAAS;IAE3B;;;;;;OAMG;cACgB,UAAU,CAAC,iBAAiB,EAAE,cAAc;IAM/D;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAoBlB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAwBnB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAIpB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAMjB;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAQ9B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAgD9B;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAiB7B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAY/B,OAAgB,MAAM,4BAqZpB;IAEF;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAwI3B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACM,MAAM;CAsFf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,kBAAkB,EAAE,cAAc,CAAC;KACnC;CACD;AAED,eAAe,cAAc,CAAC"}
@@ -316,17 +316,19 @@ let MoniTimePicker = class MoniTimePicker extends MoniElement {
316
316
  }
317
317
 
318
318
  :host([orientation="horizontal"]) {
319
- inline-size: auto;
319
+ inline-size: min(40rem, 100%);
320
320
  }
321
321
  :host([orientation="horizontal"]) .main-content {
322
322
  flex-direction: row;
323
323
  align-items: center;
324
+ justify-content: center;
324
325
  gap: 2rem;
325
326
  }
326
327
  :host([orientation="horizontal"]) .left-pane {
327
328
  display: flex;
328
329
  flex-direction: column;
329
330
  justify-content: center;
331
+ flex: none;
330
332
  }
331
333
 
332
334
  /* Main Content (Default vertical) */
@@ -356,6 +358,7 @@ let MoniTimePicker = class MoniTimePicker extends MoniElement {
356
358
  :host(:not([orientation])) .main-content {
357
359
  flex-direction: row;
358
360
  align-items: center;
361
+ justify-content: center;
359
362
  gap: 2rem;
360
363
  }
361
364
  :host([orientation='auto']) .left-pane,
@@ -363,6 +366,7 @@ let MoniTimePicker = class MoniTimePicker extends MoniElement {
363
366
  display: flex;
364
367
  flex-direction: column;
365
368
  justify-content: center;
369
+ flex: none;
366
370
  }
367
371
  }
368
372
 
@@ -118,25 +118,45 @@ h6,
118
118
  background-size: 200px 200px;
119
119
  }
120
120
 
121
- /* Scrollbar discreto */
121
+ /* Material 3 scrollbar for document-level scroll containers. */
122
+ * {
123
+ scrollbar-width: thin;
124
+ scrollbar-color:
125
+ var(--moni-scrollbar-thumb, color-mix(in srgb, var(--moni-on-surface-variant, #49454f) 38%, transparent))
126
+ var(--moni-scrollbar-track, transparent);
127
+ }
128
+
122
129
  *::-webkit-scrollbar {
123
- width: 8px;
124
- height: 8px;
130
+ width: var(--moni-scrollbar-size, 0.625rem);
131
+ height: var(--moni-scrollbar-size, 0.625rem);
125
132
  }
126
133
 
127
134
  *::-webkit-scrollbar-track {
128
- background: transparent;
135
+ background: var(--moni-scrollbar-track, transparent);
136
+ border-radius: 999px;
129
137
  }
130
138
 
131
139
  *::-webkit-scrollbar-thumb {
132
- background: var(--color-outline-variant);
133
- border-radius: 4px;
140
+ min-height: 2.5rem;
141
+ background: var(--moni-scrollbar-thumb, color-mix(in srgb, var(--moni-on-surface-variant, #49454f) 38%, transparent));
142
+ background-clip: padding-box;
143
+ border: 0.1875rem solid transparent;
144
+ border-radius: 999px;
134
145
  }
135
146
 
136
147
  *::-webkit-scrollbar-thumb:hover {
137
- background: var(--color-outline);
148
+ background: var(--moni-scrollbar-thumb-hover, color-mix(in srgb, var(--moni-on-surface-variant, #49454f) 62%, transparent));
149
+ background-clip: padding-box;
150
+ border-width: 0.125rem;
138
151
  }
139
152
 
153
+ *::-webkit-scrollbar-thumb:active {
154
+ background: var(--moni-scrollbar-thumb-active, color-mix(in srgb, var(--moni-primary, #6750a4) 76%, transparent));
155
+ background-clip: padding-box;
156
+ }
157
+
158
+ *::-webkit-scrollbar-corner { background: transparent; }
159
+
140
160
  /* Focus visible */
141
161
  :focus-visible {
142
162
  outline: 2px solid var(--color-primary);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moni-labs/moni-ui",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Design system unificado para Moni — Material Design 3 Expressive, mobile-first.",
@@ -0,0 +1,15 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { sharedStyles } from './shared-styles.js';
3
+
4
+ describe('sharedStyles Material scrollbar', () => {
5
+ it('styles scroll containers inside every component shadow root', () => {
6
+ const styles = sharedStyles.cssText;
7
+
8
+ expect(styles).toContain('--moni-scrollbar-size');
9
+ expect(styles).toContain('scrollbar-width: thin');
10
+ expect(styles).toContain('*::-webkit-scrollbar-thumb');
11
+ expect(styles).toContain('*::-webkit-scrollbar-thumb:hover');
12
+ expect(styles).toContain('*::-webkit-scrollbar-thumb:active');
13
+ expect(styles).toContain('border-radius: 999px');
14
+ });
15
+ });
@@ -206,6 +206,62 @@ export const sharedStyles = css`
206
206
  --ease-emphasized: cubic-bezier(0.3, 0, 0, 1);
207
207
  --duration-fast: 150ms;
208
208
  --duration-normal: 250ms;
209
+
210
+ /* ─── Material 3 scrollbar ─── */
211
+ --_scrollbar-size: var(--moni-scrollbar-size, 0.625rem);
212
+ --_scrollbar-track: var(--moni-scrollbar-track, transparent);
213
+ --_scrollbar-thumb: var(
214
+ --moni-scrollbar-thumb,
215
+ color-mix(in srgb, var(--on-surface-variant) 38%, transparent)
216
+ );
217
+ --_scrollbar-thumb-hover: var(
218
+ --moni-scrollbar-thumb-hover,
219
+ color-mix(in srgb, var(--on-surface-variant) 62%, transparent)
220
+ );
221
+ --_scrollbar-thumb-active: var(
222
+ --moni-scrollbar-thumb-active,
223
+ color-mix(in srgb, var(--primary) 76%, transparent)
224
+ );
225
+ }
226
+
227
+ /* Firefox uses its native thin geometry and the same Material color roles. */
228
+ * {
229
+ scrollbar-width: thin;
230
+ scrollbar-color: var(--_scrollbar-thumb) var(--_scrollbar-track);
231
+ }
232
+
233
+ /* Chromium, Safari and Edge: 4px pill inside a 10px interaction gutter. */
234
+ *::-webkit-scrollbar {
235
+ inline-size: var(--_scrollbar-size);
236
+ block-size: var(--_scrollbar-size);
237
+ }
238
+
239
+ *::-webkit-scrollbar-track {
240
+ background: var(--_scrollbar-track);
241
+ border-radius: 999px;
242
+ }
243
+
244
+ *::-webkit-scrollbar-thumb {
245
+ min-block-size: 2.5rem;
246
+ background: var(--_scrollbar-thumb);
247
+ background-clip: padding-box;
248
+ border: 0.1875rem solid transparent;
249
+ border-radius: 999px;
250
+ }
251
+
252
+ *::-webkit-scrollbar-thumb:hover {
253
+ background: var(--_scrollbar-thumb-hover);
254
+ background-clip: padding-box;
255
+ border-width: 0.125rem;
256
+ }
257
+
258
+ *::-webkit-scrollbar-thumb:active {
259
+ background: var(--_scrollbar-thumb-active);
260
+ background-clip: padding-box;
261
+ }
262
+
263
+ *::-webkit-scrollbar-corner {
264
+ background: transparent;
209
265
  }
210
266
 
211
267
  /* ─── 4. Utility classes ─────────────────────────────────────────────── */
@@ -122,6 +122,82 @@ describe('moni-button-group', () => {
122
122
  expect(btn2.active).toBe(true);
123
123
  });
124
124
 
125
+ it('expande el botón activo independientemente de su contenido', () => {
126
+ const cssText = ((group.constructor as typeof HTMLElement & { styles: unknown[] }).styles)
127
+ .map((style: any) => style?.cssText ?? '')
128
+ .join(' ');
129
+ expect(cssText).toContain("[variant='standard'][size='small']) ::slotted(moni-button[active])");
130
+ expect(cssText).not.toContain("[variant='connected'][size='small']) ::slotted(moni-button[active])");
131
+ expect(cssText).toContain('--moni-button-padding');
132
+ });
133
+
134
+ it('no permite deseleccionar el último botón cuando la selección es obligatoria', async () => {
135
+ group.selectionRequired = true;
136
+ const button = document.createElement('moni-button') as MoniButton;
137
+ button.active = true;
138
+ group.append(button);
139
+ await group.updateComplete;
140
+ button.click();
141
+ await button.updateComplete;
142
+ expect(button.active).toBe(true);
143
+ });
144
+
145
+ it('propaga la forma cuadrada y conserva anchos uniformes en connected', async () => {
146
+ group.variant = 'connected';
147
+ group.shape = 'square';
148
+ const first = document.createElement('moni-button') as MoniButton;
149
+ const second = document.createElement('moni-button') as MoniButton;
150
+ group.append(first, second);
151
+ await group.updateComplete;
152
+ expect(first.shape).toBe('square');
153
+ expect(second.shape).toBe('square');
154
+ const cssText = ((group.constructor as typeof HTMLElement & { styles: unknown[] }).styles)
155
+ .map((style: any) => style?.cssText ?? '')
156
+ .join(' ');
157
+ expect(cssText).toContain("[variant='connected']) ::slotted(moni-button)");
158
+ expect(cssText).toContain('flex: 1 1 0');
159
+ expect(cssText).toContain('--moni-button-inline-size: 100%');
160
+ });
161
+
162
+ it('marca temporalmente el botón presionado y sus vecinos en standard', async () => {
163
+ const buttons = Array.from({ length: 3 }, () => document.createElement('moni-button') as MoniButton);
164
+ group.append(...buttons);
165
+ await group.updateComplete;
166
+ buttons[1].dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, composed: true }));
167
+ expect(buttons[1].hasAttribute('data-group-pressed')).toBe(true);
168
+ expect(buttons[0].hasAttribute('data-group-adjacent-pressed')).toBe(true);
169
+ expect(buttons[2].hasAttribute('data-group-adjacent-pressed')).toBe(true);
170
+ buttons[1].dispatchEvent(new PointerEvent('pointerup', { bubbles: true, composed: true }));
171
+ expect(buttons.every((button) => !button.hasAttribute('data-group-pressed') && !button.hasAttribute('data-group-adjacent-pressed'))).toBe(true);
172
+ });
173
+
174
+ it('redistribuye el ancho seleccionado hacia sus vecinos sin afectar connected', async () => {
175
+ const buttons = Array.from({ length: 3 }, () => document.createElement('moni-button') as MoniButton);
176
+ group.append(...buttons);
177
+ await group.updateComplete;
178
+ buttons[1].active = true;
179
+ await buttons[1].updateComplete;
180
+ await new Promise((resolve) => setTimeout(resolve, 0));
181
+ expect(buttons[0].getAttribute('data-group-adjacent-selected')).toBe('shared');
182
+ expect(buttons[2].getAttribute('data-group-adjacent-selected')).toBe('shared');
183
+
184
+ group.variant = 'connected';
185
+ await group.updateComplete;
186
+ expect(buttons[0].hasAttribute('data-group-adjacent-selected')).toBe(false);
187
+ expect(buttons[2].hasAttribute('data-group-adjacent-selected')).toBe(false);
188
+ });
189
+
190
+ it('reparte por igual la reducción cuando se selecciona un botón del extremo', async () => {
191
+ const buttons = Array.from({ length: 3 }, () => document.createElement('moni-button') as MoniButton);
192
+ group.append(...buttons);
193
+ await group.updateComplete;
194
+ buttons[2].active = true;
195
+ await buttons[2].updateComplete;
196
+ await new Promise((resolve) => setTimeout(resolve, 0));
197
+ expect(buttons[0].getAttribute('data-group-adjacent-selected')).toBe('shared');
198
+ expect(buttons[1].getAttribute('data-group-adjacent-selected')).toBe('shared');
199
+ });
200
+
125
201
  it('renderiza role="group" en el contenedor por defecto (accesibilidad M3)', async () => {
126
202
  await group.updateComplete;
127
203
  const container = group.shadowRoot?.querySelector('[part="container"]');