@kubex/zinc 1.1.136 → 1.1.138

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": "@kubex/zinc",
3
- "version": "1.1.136",
3
+ "version": "1.1.138",
4
4
  "description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
5
5
  "keywords": [
6
6
  "web components",
@@ -31,6 +31,16 @@
31
31
  gap: var(--menu-gap);
32
32
  margin: 0;
33
33
  max-width: calc(100% - 34px);
34
+
35
+ &:empty {
36
+ display: none;
37
+ }
38
+ }
39
+
40
+ // Buttons are hoisted into __container, so anything left in the default slot
41
+ // is non-visual (zn-confirm) and must not claim a flex gap.
42
+ ::slotted(*) {
43
+ display: contents;
34
44
  }
35
45
 
36
46
  zn-button {
@@ -1,7 +1,7 @@
1
1
  @use "../../wc";
2
2
 
3
3
  :host {
4
- display: inline-block;
4
+ display: inline-flex;
5
5
  vertical-align: middle;
6
6
  }
7
7
 
@@ -167,7 +167,7 @@
167
167
  display: flex;
168
168
  align-items: flex-start;
169
169
  justify-content: space-between;
170
- gap: 2px;
170
+ gap: var(--zn-spacing-x-small);
171
171
  max-width: 100%;
172
172
  min-width: 0;
173
173
  min-height: 36px;
@@ -212,6 +212,7 @@
212
212
  gap: var(--zn-spacing-2x-small);
213
213
  align-items: center;
214
214
  text-wrap: auto;
215
+ min-height: 36px; // Keep in step with the actions row: both bands share a centre line.
215
216
  }
216
217
 
217
218
  &__description {
@@ -257,6 +258,7 @@
257
258
  slot[name="actions"] {
258
259
  justify-content: end;
259
260
  flex-wrap: nowrap;
261
+ min-height: 36px;
260
262
  }
261
263
 
262
264
  slot[name="actions"]::slotted(zn-button-menu) {
@@ -21,6 +21,9 @@
21
21
  }
22
22
 
23
23
  .card {
24
+ --card-actions-inset: 14px;
25
+
26
+ position: relative;
24
27
  display: flex;
25
28
  align-items: center;
26
29
  gap: 12px;
@@ -90,11 +93,21 @@
90
93
  opacity: 0.45;
91
94
  }
92
95
 
93
- // Space is always reserved (visibility, not display) so the card never
94
- // changes size or shifts its text when the actions appear on hover.
96
+ :host([locked]) .card {
97
+ // Clear the always-visible lock: its 14px icon plus the row gap.
98
+ --card-actions-inset: 40px;
99
+ }
100
+
101
+ // Overlaid rather than laid out, so the text gets the full card width and never
102
+ // reflows on hover; the gradient pad hides whatever text runs underneath.
95
103
  .card__actions {
104
+ position: absolute;
105
+ inset: 0 var(--card-actions-inset) 0 auto;
96
106
  display: flex;
97
- gap: 4px;
107
+ align-items: center;
108
+ gap: var(--zn-spacing-2x-small);
109
+ padding-left: 28px;
110
+ background: linear-gradient(to right, rgba(var(--zn-panel), 0), rgb(var(--zn-panel)) 28px);
98
111
  visibility: hidden;
99
112
  }
100
113
 
@@ -684,6 +684,9 @@ export default class ZnPageBuilder extends ZincElement {
684
684
  const [removed, sections] = this._extract(id);
685
685
  if (!removed) return;
686
686
  this._pushHistory();
687
+ // Cards are rendered unkeyed, so the focused action button survives the
688
+ // re-render as part of whichever section shifts into this slot.
689
+ this._blurActiveCard();
687
690
  // Clear selection for the removed section AND anything inside it.
688
691
  if (this._selectedId === id || removed.children?.some(c => c?.id === this._selectedId)) {
689
692
  this._select(null);
@@ -702,6 +705,7 @@ export default class ZnPageBuilder extends ZincElement {
702
705
  sections.splice(index + 1, 0, copy);
703
706
  this._commit({ sections });
704
707
  this._select(copy.id);
708
+ void this._focusCard(copy.id);
705
709
  return;
706
710
  }
707
711
  // A slotted child duplicates into its container's next empty slot, if any.
@@ -719,10 +723,32 @@ export default class ZnPageBuilder extends ZincElement {
719
723
  this._pushHistory();
720
724
  this._commit({ sections: this._patchSection(s.id, x => ({ ...x, children })) });
721
725
  this._select(copy.id);
726
+ void this._focusCard(copy.id);
722
727
  return;
723
728
  }
724
729
  }
725
730
 
731
+ /**
732
+ * Hands focus to a card, moving it off the action button that was just used.
733
+ * Cards render unkeyed, so focus left behind stays with the original card's
734
+ * element and keeps its hover actions showing after the pointer leaves.
735
+ */
736
+ private async _focusCard(id: string) {
737
+ this._blurActiveCard();
738
+ await this.updateComplete;
739
+ this.shadowRoot?.querySelector<HTMLElement>(`zn-page-section-card[data-id="${id}"]`)?.focus();
740
+ }
741
+
742
+ private _blurActiveCard() {
743
+ const focused = this.shadowRoot?.activeElement;
744
+ if (!(focused instanceof HTMLElement) || !focused.closest('zn-page-section-card')) return;
745
+ let active: HTMLElement = focused;
746
+ // activeElement retargets to the card host; the real focus is the action
747
+ // button nested in its shadow root, and only that responds to blur().
748
+ while (active.shadowRoot?.activeElement instanceof HTMLElement) active = active.shadowRoot.activeElement;
749
+ active.blur();
750
+ }
751
+
726
752
  /** Moves a section (top-level or slotted) to a top-level position. */
727
753
  private _moveSection(id: string, index: number) {
728
754
  if (this._isPinned(id)) return;
@@ -993,6 +1019,7 @@ export default class ZnPageBuilder extends ZincElement {
993
1019
  return html`
994
1020
  <zn-page-section-card
995
1021
  class="${extraClass}"
1022
+ data-id="${section.id}"
996
1023
  draggable="${pinned ? 'false' : 'true'}"
997
1024
  tabindex="0"
998
1025
  label="${section.label ?? type?.label ?? section.type}"