@ctrliq/quantic-components 1.84.1 → 1.85.1

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { describe, expect, it } from 'vitest';
5
+ const SRC_DIR = join(dirname(fileURLToPath(import.meta.url)), '..');
6
+ // quantic-action-group, quantic-tag-group and quantic-badge-group are the same component
7
+ // with different children. They exist to carry one accessible name and one gap over a run
8
+ // of siblings, and they deliberately take no layout props: Cluster was deprecated rather
9
+ // than have three copies of its axis and align. That makes ::part(group) the only way out
10
+ // for a caller who needs a column, so it has to be on all three or the escape hatch is a
11
+ // coin flip.
12
+ const GROUPS = [
13
+ 'action-group/action-group.component.ts',
14
+ 'tag/tag-group.component.ts',
15
+ 'badge/badge-group.component.ts',
16
+ ];
17
+ const LAYOUT_PROPS = /attribute:\s*'(axis|direction|align|orientation|gap|wrap)'/;
18
+ function source(file) {
19
+ return readFileSync(join(SRC_DIR, file), 'utf-8');
20
+ }
21
+ describe('sibling groups', () => {
22
+ it.each(GROUPS)('%s exposes the role container as ::part(group)', (file) => {
23
+ const roleContainer = /<div[^>]*role="group"[^>]*>/.exec(source(file));
24
+ expect(roleContainer, 'expected a div carrying role="group"').not.toBeNull();
25
+ expect(roleContainer?.[0]).toContain('part="group"');
26
+ });
27
+ it.each(GROUPS)('%s takes no layout prop', (file) => {
28
+ expect(LAYOUT_PROPS.exec(source(file))?.[1]).toBeUndefined();
29
+ });
30
+ it('gives all three the same public surface', () => {
31
+ const surfaces = GROUPS.map((file) => {
32
+ const text = source(file);
33
+ return {
34
+ props: [...text.matchAll(/attribute:\s*'([a-z-]+)'/g)].map(([, name]) => name).sort(),
35
+ parts: [...text.matchAll(/part="([a-z-]+)"/g)].map(([, name]) => name).sort(),
36
+ };
37
+ });
38
+ expect(surfaces[0]).toEqual({ props: ['aria-label'], parts: ['group'] });
39
+ for (const surface of surfaces.slice(1))
40
+ expect(surface).toEqual(surfaces[0]);
41
+ });
42
+ });
package/dist/meta.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generated": "2026-08-27T21:15:44.818Z",
3
+ "generated": "2026-08-28T16:42:21.311Z",
4
4
  "components": [
5
5
  {
6
6
  "tag": "quantic-action-group",
7
- "description": "A row of related actions sharing one accessible name and a consistent gap. Takes quantic-button, and quantic-dropdown-menu or quantic-popover when the trigger is a button. Wraps when the row runs out of width. Gap: --quantic-component-action-group-gap. Use quantic-button-bar when the buttons should read as one joined control.",
7
+ "description": "A row of related actions sharing one accessible name and a consistent gap. Takes quantic-button, and quantic-dropdown-menu or quantic-popover when the trigger is a button. Wraps when the row runs out of width. Gap: --quantic-component-action-group-gap. Layout: ::part(group), for a column or a different alignment. Use quantic-button-bar when the buttons should read as one joined control.",
8
8
  "category": "action",
9
9
  "props": [
10
10
  {
@@ -183,7 +183,7 @@
183
183
  },
184
184
  {
185
185
  "tag": "quantic-badge-group",
186
- "description": "A run of quantic-badge or quantic-status-badge sharing one accessible name and a consistent gap. Wraps across lines, and each badge keeps its own size. Gap: --quantic-component-badge-group-gap. Use quantic-tag-group for tags.",
186
+ "description": "A run of quantic-badge or quantic-status-badge sharing one accessible name and a consistent gap. Wraps across lines, and each badge keeps its own size. Gap: --quantic-component-badge-group-gap. Layout: ::part(group), for a column or a different alignment. Use quantic-tag-group for tags.",
187
187
  "category": "display",
188
188
  "props": [
189
189
  {
@@ -4774,7 +4774,7 @@
4774
4774
  },
4775
4775
  {
4776
4776
  "tag": "quantic-tag-group",
4777
- "description": "A run of quantic-tag, quantic-status-tag or quantic-count-tag sharing one accessible name and a consistent gap. Wraps across lines, and each tag keeps its own size. Gap: --quantic-component-tag-group-gap. Use quantic-badge-group for badges.",
4777
+ "description": "A run of quantic-tag, quantic-status-tag or quantic-count-tag sharing one accessible name and a consistent gap. Wraps across lines, and each tag keeps its own size. Gap: --quantic-component-tag-group-gap. Layout: ::part(group), for a column or a different alignment. Use quantic-badge-group for badges.",
4778
4778
  "category": "display",
4779
4779
  "props": [
4780
4780
  {
@@ -322,13 +322,20 @@
322
322
  *
323
323
  * Chrome doesn't re-evaluate :has() in shadow DOM when light-DOM children
324
324
  * arrive asynchronously (e.g. React CSR). A capture listener on the shadow
325
- * root intercepts all slotchange events internally and sets inline styles to
326
- * override the CSS — no wiring required in component templates.
325
+ * root intercepts all slotchange events internally and marks filled slots with
326
+ * `data-slot-filled`, no wiring required in component templates.
327
+ *
328
+ * That reveal has to be a rule inside `quantic-slot-visibility`, not an inline
329
+ * style. Inline declarations sit in the implicit unlayered layer, which the
330
+ * cascade orders after every explicit layer, so an inline
331
+ * `display: revert-layer` reverts to the `display: none` above rather than
332
+ * past it.
327
333
  *
328
334
  * @example
329
335
  * class MyElement extends AutoHideEmptySlotsMixin(QuanticElement, ['header', 'footer']) {}
330
336
  * class Toast extends AutoHideEmptySlotsMixin(QuanticElement, ['title', '', 'action']) {}
331
337
  */
338
+ const FILLED_ATTRIBUTE = 'data-slot-filled';
332
339
  const AutoHideEmptySlotsMixin = (superClass, slots = []) => {
333
340
  var _AutoHideEmptySlotsClass_instances, _AutoHideEmptySlotsClass_slotListenerAttached, _AutoHideEmptySlotsClass_isMeaningful, _AutoHideEmptySlotsClass_onSlotChange, _AutoHideEmptySlotsClass_attachSlotListener, _AutoHideEmptySlotsClass_detachSlotListener;
334
341
  if (slots.length === 0)
@@ -346,6 +353,7 @@
346
353
  `${sel} { display: none; }`,
347
354
  whenSlotPresent(name, r$6(`${sel} { display: revert-layer; }`))
348
355
  .cssText,
356
+ `${sel}[${FILLED_ATTRIBUTE}] { display: revert-layer; }`,
349
357
  ].join(' ');
350
358
  })
351
359
  .join(' ');
@@ -367,7 +375,7 @@
367
375
  const hasContent = slot
368
376
  .assignedNodes({ flatten: true })
369
377
  .some(__classPrivateFieldGet(this, _AutoHideEmptySlotsClass_isMeaningful, "f"));
370
- slot.style.display = hasContent ? 'revert-layer' : '';
378
+ slot.toggleAttribute(FILLED_ATTRIBUTE, hasContent);
371
379
  });
372
380
  }
373
381
  static finalizeStyles(styles) {
@@ -2956,9 +2964,10 @@
2956
2964
 
2957
2965
  .icon {
2958
2966
  display: inline-flex;
2967
+ align-items: center;
2959
2968
  flex-shrink: 0;
2969
+ height: var(--quantic-line-height-body-sm);
2960
2970
  color: var(--quantic-component-alert-icon-color);
2961
- line-height: 0;
2962
2971
  }
2963
2972
 
2964
2973
  .content {
@@ -5761,7 +5770,7 @@
5761
5770
  }
5762
5771
  render() {
5763
5772
  return b `
5764
- <div class="group" role="group" aria-label=${this.ariaLabel ?? 'Badges'}>
5773
+ <div class="group" part="group" role="group" aria-label=${this.ariaLabel ?? 'Badges'}>
5765
5774
  <slot></slot>
5766
5775
  </div>
5767
5776
  `;
@@ -5771,6 +5780,7 @@
5771
5780
  tag: 'quantic-badge-group',
5772
5781
  description: 'A run of quantic-badge or quantic-status-badge sharing one accessible name and a consistent gap. ' +
5773
5782
  'Wraps across lines, and each badge keeps its own size. Gap: --quantic-component-badge-group-gap. ' +
5783
+ 'Layout: ::part(group), for a column or a different alignment. ' +
5774
5784
  'Use quantic-tag-group for tags.',
5775
5785
  category: 'display',
5776
5786
  slots: {
@@ -6003,7 +6013,7 @@
6003
6013
  }
6004
6014
  render() {
6005
6015
  return b `
6006
- <div class="group" role="group" aria-label=${this.ariaLabel ?? 'Actions'}>
6016
+ <div class="group" part="group" role="group" aria-label=${this.ariaLabel ?? 'Actions'}>
6007
6017
  <slot></slot>
6008
6018
  </div>
6009
6019
  `;
@@ -6014,6 +6024,7 @@
6014
6024
  description: 'A row of related actions sharing one accessible name and a consistent gap. ' +
6015
6025
  'Takes quantic-button, and quantic-dropdown-menu or quantic-popover when the trigger is a button. ' +
6016
6026
  'Wraps when the row runs out of width. Gap: --quantic-component-action-group-gap. ' +
6027
+ 'Layout: ::part(group), for a column or a different alignment. ' +
6017
6028
  'Use quantic-button-bar when the buttons should read as one joined control.',
6018
6029
  category: 'action',
6019
6030
  slots: {
@@ -70057,7 +70068,7 @@
70057
70068
  }
70058
70069
  render() {
70059
70070
  return b `
70060
- <div class="group" role="group" aria-label=${this.ariaLabel ?? 'Tags'}>
70071
+ <div class="group" part="group" role="group" aria-label=${this.ariaLabel ?? 'Tags'}>
70061
70072
  <slot></slot>
70062
70073
  </div>
70063
70074
  `;
@@ -70067,6 +70078,7 @@
70067
70078
  tag: 'quantic-tag-group',
70068
70079
  description: 'A run of quantic-tag, quantic-status-tag or quantic-count-tag sharing one accessible name and a consistent gap. ' +
70069
70080
  'Wraps across lines, and each tag keeps its own size. Gap: --quantic-component-tag-group-gap. ' +
70081
+ 'Layout: ::part(group), for a column or a different alignment. ' +
70070
70082
  'Use quantic-badge-group for badges.',
70071
70083
  category: 'display',
70072
70084
  slots: {