@kubex/zinc 1.1.22 → 1.1.24

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 (48) hide show
  1. package/custom-elements-manifest.config.js +2 -4
  2. package/dist/custom-elements.json +1172 -78
  3. package/dist/vscode.html-custom-data.json +34 -13
  4. package/dist/web-types.json +135 -26
  5. package/dist/zn.d.ts +867 -1
  6. package/dist/zn.min.js +893 -481
  7. package/docs/_includes/default.njk +1 -0
  8. package/docs/_includes/full-page.njk +38 -0
  9. package/docs/pages/components/flow-builder-demo.njk +194 -0
  10. package/docs/pages/components/flow-builder-troubleshooter-demo.njk +282 -0
  11. package/docs/pages/components/flow-builder.md +377 -0
  12. package/package.json +1 -1
  13. package/src/components/button/button.component.ts +1 -2
  14. package/src/components/button/button.scss +6 -16
  15. package/src/components/collapsible/collapsible.component.ts +11 -6
  16. package/src/components/data-table/data-table.component.ts +12 -12
  17. package/src/components/flow-builder/flow-builder.component.ts +1171 -0
  18. package/src/components/flow-builder/flow-builder.scss +489 -0
  19. package/src/components/flow-builder/flow-builder.test.ts +59 -0
  20. package/src/components/flow-builder/flow-layout.ts +139 -0
  21. package/src/components/flow-builder/flow-registry.ts +52 -0
  22. package/src/components/flow-builder/flow.types.ts +407 -0
  23. package/src/components/flow-builder/index.ts +14 -0
  24. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +1086 -0
  25. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +371 -0
  26. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.test.ts +39 -0
  27. package/src/components/flow-builder/modules/flow-canvas/index.ts +12 -0
  28. package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +174 -0
  29. package/src/components/flow-builder/modules/flow-node/flow-node.scss +180 -0
  30. package/src/components/flow-builder/modules/flow-node/flow-node.test.ts +50 -0
  31. package/src/components/flow-builder/modules/flow-node/index.ts +12 -0
  32. package/src/components/flow-builder/modules/flow-step/flow-step.component.ts +88 -0
  33. package/src/components/flow-builder/modules/flow-step/flow-step.scss +52 -0
  34. package/src/components/flow-builder/modules/flow-step/flow-step.test.ts +21 -0
  35. package/src/components/flow-builder/modules/flow-step/index.ts +12 -0
  36. package/src/components/flow-builder/modules/flow-step-group/flow-step-group.component.ts +35 -0
  37. package/src/components/flow-builder/modules/flow-step-group/flow-step-group.scss +28 -0
  38. package/src/components/flow-builder/modules/flow-step-group/flow-step-group.test.ts +20 -0
  39. package/src/components/flow-builder/modules/flow-step-group/index.ts +12 -0
  40. package/src/components/flow-builder/modules/flow-steps/flow-steps.component.ts +88 -0
  41. package/src/components/flow-builder/modules/flow-steps/flow-steps.scss +24 -0
  42. package/src/components/flow-builder/modules/flow-steps/flow-steps.test.ts +17 -0
  43. package/src/components/flow-builder/modules/flow-steps/index.ts +12 -0
  44. package/src/events/events.ts +3 -0
  45. package/src/events/zn-flow-change.ts +9 -0
  46. package/src/events/zn-flow-connect.ts +9 -0
  47. package/src/events/zn-flow-selection-change.ts +7 -0
  48. package/src/zinc.ts +4 -0
@@ -0,0 +1,377 @@
1
+ ---
2
+ meta:
3
+ title: Flow Builder
4
+ description: A drag-and-drop visual automation builder with a steps panel, a pan/zoom canvas, and a configuration inspector. Extend it with your own node types via a JS registry.
5
+ layout: component
6
+ fullWidth: true
7
+ ---
8
+
9
+ The Flow Builder is a three-panel editor for visual automations, with an optional header action bar:
10
+
11
+ - **Header** (optional) — a full-width action bar rendered only when `slot="header-left"` / `slot="header-right"`
12
+ content is provided (e.g. Close / Undo All Changes on the left, Apply Changes on the right).
13
+ - **Left panel** — the steps panel (searchable, tabbed by step group).
14
+ - **Canvas** (center) — a pannable, zoomable surface. Connections are **append-only**: click any **output port**
15
+ on a node to start a **stray branch** — the wire follows your cursor until you click a node (or its output
16
+ port) to attach it. Fan-in is allowed, and so are **loops** — a branch may point back to an earlier step (e.g.
17
+ an answer that restarts the questioning); only wiring a node directly to itself is refused. Cancel by clicking
18
+ empty canvas, pressing Esc, or leaving the window. A connected output port spawns a brand-new branch on click, so extra arrows never require
19
+ config forms. Open outputs draw nothing while idle — their **`+`** targets appear only while dragging a step in
20
+ or moving one. Every existing wire has a **`+`** at its midpoint to insert a step between two nodes. New
21
+ steps are created by dragging them from the panel onto the canvas or a `+`. Reposition nodes by dragging, and add
22
+ resizable sticky notes. Scroll to pan (side-scroll or Shift+scroll pans horizontally); Ctrl/Cmd+scroll or
23
+ trackpad pinch zooms toward the cursor. Toolbar: undo, redo, add-note, **untangle** (auto-arranges a messy flow
24
+ into evenly spaced layers that follow the connections — undoable), zoom-in, reset, zoom-out.
25
+ - **Right panel** — the sidebar by default: a slot (`slot="sidebar"`) for status / version history, with a built-in
26
+ Configuration Errors summary derived from `errorNodes`. Selecting a node slides that step's configuration in from
27
+ the right (driven by the node type's `renderConfig`); clicking a **branch pill** on a wire slides in the branch
28
+ editor instead — rename the branch and configure the filters/conditions for taking that path (the node type's
29
+ `renderBranchConfig`, persisted on the output port's `data`).
30
+
31
+ A node's **`…`** menu offers Delete, Duplicate, and **Move** — Move detaches the step (with its subtree) so you can
32
+ re-attach it to a different `+` slot.
33
+
34
+ Every node except an entrypoint (a node with `inputs=""`) can have as many inputs and outputs as it likes — outputs
35
+ are per-node overridable, so a step's config can add and remove branches at runtime.
36
+
37
+ It ships with **no built-in node types** — every step you see below is registered by the consumer. This keeps the
38
+ component generic and lets it be extended for any future workflow without changing the library.
39
+
40
+ <zn-button href="/components/flow-builder-demo/" target="_blank" icon="open_in_new" style="margin-bottom: 1.5rem;">
41
+ Open the full-page demo
42
+ </zn-button>
43
+ <zn-button href="/components/flow-builder-troubleshooter-demo/" target="_blank" icon="open_in_new" style="margin-bottom: 1.5rem;">
44
+ Open the Troubleshoot Q&A demo
45
+ </zn-button>
46
+
47
+ ```html:preview
48
+
49
+ <div style="height: 900px;">
50
+ <zn-flow-builder
51
+ id="flow-demo"
52
+ heading="Retention – Annual Cancellation_v1"
53
+ subheading="Last updated 16th June 2026"
54
+ triggers-hint="Automations require a trigger to start. Choose a trigger below and drag it to the canvas to begin."
55
+ actions-hint="Actions run as the automation progresses. Drag one onto the canvas or a + slot."
56
+ rules-hint="Rules branch the flow based on conditions you configure on each step.">
57
+
58
+ <zn-flow-step type="webhook" group="entrypoint" category="Events"
59
+ icon="webhook@lu" color="rgb(236, 68, 91)"
60
+ description="User hits webhook XYZ">Cancellation Requested
61
+ </zn-flow-step>
62
+ <zn-flow-step type="manual" group="entrypoint" category="Contacts"
63
+ icon="user-plus@lu" color="rgb(236, 68, 91)">Contact added manually
64
+ </zn-flow-step>
65
+ <zn-flow-step type="list-add" group="entrypoint" category="Contacts"
66
+ icon="list-plus@lu" color="rgb(236, 68, 91)">Contact added to a list
67
+ </zn-flow-step>
68
+
69
+ <zn-flow-step type="email-parser" group="action" category="Email"
70
+ icon="mail@lu" color="rgb(43, 192, 145)">IMAP Email Parser
71
+ </zn-flow-step>
72
+ <zn-flow-step type="send-reply" group="action" category="Email"
73
+ icon="send@lu" color="rgb(43, 192, 145)">Send Reply
74
+ </zn-flow-step>
75
+ <zn-flow-step type="claude" group="action" category="AI"
76
+ icon="bot@lu" color="rgb(43, 192, 145)">Claude
77
+ </zn-flow-step>
78
+ <zn-flow-step type="notify" group="action" category="Support"
79
+ icon="user@lu" color="rgb(43, 192, 145)">Notify User
80
+ </zn-flow-step>
81
+
82
+ <zn-flow-step type="split" group="rule" category="Logic"
83
+ icon="split@lu" color="rgb(105, 54, 245)"
84
+ outputs='[{"id":"true","label":"TRUE"},{"id":"false","label":"FALSE"}]'>Conditional Split
85
+ </zn-flow-step>
86
+
87
+ <div slot="sidebar" class="flow-sidebar-demo">
88
+ <label>Status
89
+ <zn-select>
90
+ <zn-option selected>Active</zn-option>
91
+ <zn-option>Paused</zn-option>
92
+ <zn-option>Draft</zn-option>
93
+ </zn-select>
94
+ </label>
95
+ <h4>Version History</h4>
96
+ <ul>
97
+ <li><strong>John Smith</strong><span>16th June 2026 · 14:03</span></li>
98
+ <li><strong>John Smith</strong><span>16th June 2026 · 11:20</span></li>
99
+ <li><strong>Jane Doe</strong><span>15th June 2026 · 09:48</span></li>
100
+ </ul>
101
+ </div>
102
+ </zn-flow-builder>
103
+ </div>
104
+
105
+ <style>
106
+ .flow-sidebar-demo {
107
+ margin-top: 16px;
108
+ font-size: 0.8125rem;
109
+ }
110
+
111
+ .flow-sidebar-demo label {
112
+ display: flex;
113
+ flex-direction: column;
114
+ gap: 4px;
115
+ font-weight: 600;
116
+ }
117
+
118
+ .flow-sidebar-demo select {
119
+ padding: 6px 8px;
120
+ border: 1px solid rgb(var(--zn-border-color));
121
+ border-radius: 6px;
122
+ }
123
+
124
+ .flow-sidebar-demo h4 {
125
+ margin: 16px 0 8px;
126
+ }
127
+
128
+ .flow-sidebar-demo ul {
129
+ list-style: none;
130
+ margin: 0;
131
+ padding: 0;
132
+ }
133
+
134
+ .flow-sidebar-demo li {
135
+ display: flex;
136
+ flex-direction: column;
137
+ padding: 8px 0;
138
+ border-top: 1px solid rgb(var(--zn-border-color));
139
+ }
140
+
141
+ .flow-sidebar-demo li span {
142
+ color: rgb(var(--zn-color-muted-text));
143
+ font-size: 0.75rem;
144
+ }
145
+ </style>
146
+
147
+ <script type="module">
148
+ const initial = {
149
+ nodes: [
150
+ {id: 'n1', type: 'webhook', x: 280, y: 60, data: {subtitle: 'User hits webhook XYZ'}},
151
+ {id: 'n2', type: 'split', x: 280, y: 220, data: {subtitle: 'ARR Threshold'}},
152
+ {id: 'n3', type: 'email-parser', x: 80, y: 480, data: {subtitle: 'Import Email Attachment'}},
153
+ {id: 'n4', type: 'notify', x: 80, y: 640, data: {subtitle: 'Notify Frank Furt'}},
154
+ {id: 'n5', type: 'claude', x: 480, y: 480, data: {subtitle: 'Generate Reply'}},
155
+ {id: 'n6', type: 'send-reply', x: 480, y: 640, data: {subtitle: 'Generate Reply'}}
156
+ ],
157
+ connections: [
158
+ {id: 'c1', source: {node: 'n1', port: 'out'}, target: {node: 'n2', port: 'in'}},
159
+ {id: 'c2', source: {node: 'n2', port: 'true'}, target: {node: 'n3', port: 'in'}},
160
+ {id: 'c3', source: {node: 'n2', port: 'false'}, target: {node: 'n5', port: 'in'}},
161
+ {id: 'c4', source: {node: 'n3', port: 'out'}, target: {node: 'n4', port: 'in'}},
162
+ {id: 'c5', source: {node: 'n5', port: 'out'}, target: {node: 'n6', port: 'in'}}
163
+ ],
164
+ notes: [
165
+ {id: 'note1', x: 760, y: 440, text: 'This is a sticky note.'}
166
+ ]
167
+ };
168
+
169
+ // The Conditional Split's outputs are user-configurable per node: its renderConfig
170
+ // edits node.outputs (add / rename / remove branches). The canvas re-renders the
171
+ // outputs and the builder prunes connections to any removed branch.
172
+ const splitType = {
173
+ type: 'split',
174
+ label: 'Conditional Split',
175
+ group: 'rule',
176
+ category: 'Logic',
177
+ icon: 'split@lu',
178
+ color: 'rgb(105, 54, 245)',
179
+ outputs: [{id: 'true', label: 'TRUE'}, {id: 'false', label: 'FALSE'}],
180
+ renderConfig: (node, update) =>
181
+ {
182
+ const branches = node.outputs ?? [{id: 'true', label: 'TRUE'}, {id: 'false', label: 'FALSE'}];
183
+ const setBranches = (next) =>
184
+ {
185
+ node.outputs = next;
186
+ update({});
187
+ };
188
+
189
+ const wrap = document.createElement('div');
190
+ wrap.style.cssText = 'display:flex;flex-direction:column;gap:8px';
191
+
192
+ const heading = document.createElement('p');
193
+ heading.textContent = 'Branches';
194
+ heading.style.cssText = 'margin:0;font-weight:600;font-size:0.8125rem';
195
+ wrap.appendChild(heading);
196
+
197
+ branches.forEach((branch, i) =>
198
+ {
199
+ const row = document.createElement('div');
200
+ row.style.cssText = 'display:flex;gap:6px;align-items:center';
201
+
202
+ const input = document.createElement('zn-input');
203
+ input.setAttribute('size', 'small');
204
+ input.value = branch.label ?? branch.id;
205
+ input.style.flex = '1';
206
+ input.addEventListener('change', () =>
207
+ setBranches(branches.map((b, j) => (j === i ? {...b, label: input.value} : b))));
208
+
209
+ const remove = document.createElement('zn-button');
210
+ remove.setAttribute('icon', 'x@lu');
211
+ remove.setAttribute('icon-button', 'small');
212
+ remove.setAttribute('plain', '');
213
+ remove.addEventListener('click', () => setBranches(branches.filter((_, j) => j !== i)));
214
+
215
+ row.append(input, remove);
216
+ wrap.appendChild(row);
217
+ });
218
+
219
+ const add = document.createElement('zn-button');
220
+ add.setAttribute('size', 'small');
221
+ add.setAttribute('icon', 'plus@lu');
222
+ add.textContent = 'Add branch';
223
+ add.addEventListener('click', () =>
224
+ setBranches([...branches, {id: 'branch-' + (branches.length + 1), label: 'New branch'}]));
225
+ wrap.appendChild(add);
226
+
227
+ return wrap;
228
+ }
229
+ };
230
+
231
+ customElements.whenDefined('zn-flow-builder').then(() =>
232
+ {
233
+ const el = document.getElementById('flow-demo');
234
+ el.registerNodeType(splitType);
235
+ el.setState(initial);
236
+ el.errorNodes = ['n3'];
237
+ });
238
+ </script>
239
+ ```
240
+
241
+ ## Examples
242
+
243
+ ### Empty builder
244
+
245
+ With no node types registered, the steps panel is empty. Register types to populate it.
246
+
247
+ ```html:preview
248
+
249
+ <div style="height: 560px;">
250
+ <zn-flow-builder heading="New Flow" subheading="Drafting"></zn-flow-builder>
251
+ </div>
252
+ ```
253
+
254
+ ### Declaring the steps panel
255
+
256
+ The builder owns the steps panel: **Entrypoints / Triggers / Actions / Rules** tabs with a search box.
257
+ You don't author the tabs or their layout — you just declare the node types, and each is routed to the
258
+ right tab by its `group` and grouped under a collapsible `category`. A tab only renders when its
259
+ group has at least one registered step, so unused groups never appear.
260
+
261
+ Declare each type as a `<zn-flow-step>` (the items themselves are never shown —
262
+ they're declarations that drive the rendered panel):
263
+
264
+ - `type` (required) — the node type id.
265
+ - `group` — `entrypoint` | `trigger` | `action` | `rule`; picks the tab (defaults to `action`).
266
+ - `category` — collapsible group within the tab (e.g. "Email").
267
+ - label — the slotted text (or a `label` attribute).
268
+ - `icon`, `icon-library`, `color`, `description`.
269
+ - `inputs` — a JSON array of input ports. Omit for a single default input; use `inputs=""` for a
270
+ trigger with no input.
271
+ - `outputs` — a JSON array of ports, each a string id or a `{"id","label"}` object (e.g.
272
+ `outputs='[{"id":"true","label":"TRUE"},{"id":"false","label":"FALSE"}]'`). Omit for a single default output.
273
+
274
+ Set an optional per-tab hint with the `triggers-hint` / `actions-hint` / `rules-hint` attributes on the
275
+ builder. (A node's inspector `renderConfig` can't be expressed in markup — supply it via
276
+ `registerNodeTypes()` for the same `type` id if you need a custom config panel.)
277
+
278
+ ```html
279
+
280
+ <zn-flow-builder triggers-hint="Choose a trigger and drag it to the canvas.">
281
+ <zn-flow-step type="webhook" group="trigger" category="Events"
282
+ icon="webhook@lu" color="rgb(236,68,91)" inputs=""
283
+ description="User hits webhook XYZ">Cancellation Requested</zn-flow-step>
284
+
285
+ <zn-flow-step type="split" group="rule" category="Logic" icon="split@lu"
286
+ outputs='[{"id":"true","label":"TRUE"},{"id":"false","label":"FALSE"}]'>Conditional Split</zn-flow-step>
287
+ </zn-flow-builder>
288
+ ```
289
+
290
+ ### Registering custom node types
291
+
292
+ Node types are the modular extension point. Instead of (or alongside) slotted steps, register an
293
+ array of `FlowNodeType` objects with `registerNodeTypes()` (or set the `.nodeTypes` property) — the two
294
+ sources merge into the same panel. Each type's `group`/`category` place it in the tabs; its icon,
295
+ color, input/output ports, and optional `renderConfig` define the node's behavior and inspector.
296
+
297
+ ```js
298
+ import {html} from 'lit';
299
+
300
+ const builder = document.querySelector('zn-flow-builder');
301
+
302
+ builder.registerNodeTypes([
303
+ {
304
+ type: 'send-email',
305
+ label: 'Send Email',
306
+ group: 'action', // 'trigger' | 'action' | 'rule' — which steps-panel tab
307
+ category: 'Email', // collapsible group within the tab
308
+ icon: 'mail@lu',
309
+ color: 'rgb(43, 192, 145)',
310
+ defaultData: {subject: ''},
311
+ // Inspector UI for a selected node of this type:
312
+ renderConfig: (node, update) => html`
313
+ <zn-input
314
+ label="Subject"
315
+ .value=${node.data.subject ?? ''}
316
+ @zn-input=${e => update({subject: e.target.value})}></zn-input>
317
+ `,
318
+ },
319
+ {
320
+ type: 'ab-split',
321
+ label: 'A/B Split',
322
+ group: 'rule',
323
+ icon: 'split@lu',
324
+ // Multiple outputs become labelled connection points (TRUE / FALSE here):
325
+ outputs: [{id: 'true', label: 'TRUE'}, {id: 'false', label: 'FALSE'}],
326
+ },
327
+ ]);
328
+ ```
329
+
330
+ A node type with `inputs: []` is a starting point (a trigger) with no incoming port. A type with
331
+ multiple `outputs` renders one labeled port per output, so a branch can fan out to different steps.
332
+
333
+ #### User-configurable outputs
334
+
335
+ A type's `outputs` are the default. For steps whose branches are defined by the user (e.g. a conditional
336
+ split), set **`node.outputs`** on the instance from `renderConfig` — the canvas renders those ports, and
337
+ the builder prunes any connection whose branch is removed. `node.inputs` works the same way. Set the new
338
+ ports then call `update({})` to commit:
339
+
340
+ ```js
341
+ renderConfig: (node, update) => {
342
+ const branches = node.outputs ?? [{id: 'true', label: 'TRUE'}, {id: 'false', label: 'FALSE'}];
343
+ const addBranch = () => {
344
+ node.outputs = [...branches, {id: crypto.randomUUID(), label: 'New branch'}];
345
+ update({}); // commit — re-renders the outputs, prunes dropped branches' connections
346
+ };
347
+ // …render an editor for `branches` that calls addBranch / removes / renames…
348
+ }
349
+ ```
350
+
351
+ ### Reacting to changes
352
+
353
+ The builder emits events as the flow is edited. Use `zn-flow-change` to persist, or read `.value`
354
+ (a JSON string of the full `FlowState`) at any time.
355
+
356
+ ```js
357
+ const builder = document.querySelector('zn-flow-builder');
358
+
359
+ builder.addEventListener('zn-flow-change', e =>
360
+ {
361
+ console.log('flow updated', e.detail.state);
362
+ });
363
+
364
+ builder.addEventListener('zn-flow-selection-change', e =>
365
+ {
366
+ console.log('selected node', e.detail.nodeId);
367
+ });
368
+
369
+ builder.addEventListener('zn-flow-connect', e =>
370
+ {
371
+ console.log('new connection', e.detail.connection);
372
+ });
373
+
374
+ // Persist / restore:
375
+ localStorage.setItem('flow', builder.value);
376
+ builder.value = localStorage.getItem('flow');
377
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.1.22",
3
+ "version": "1.1.24",
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",
@@ -60,7 +60,7 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
60
60
  @queryAll('.loading-countdown') countdownContainer: HTMLElement[];
61
61
 
62
62
  @property({}) color: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' |
63
- 'transparent' | 'star' | 'white' | (string & Record<never, never>) = 'default';
63
+ 'transparent' | 'star' | (string & Record<never, never>) = 'default';
64
64
  @property({attribute: 'hover-color'}) hoverColor: string;
65
65
  @property({type: Boolean}) text = false;
66
66
  @property({type: Boolean}) outline = false;
@@ -388,7 +388,6 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
388
388
  'button--warning': this.color === 'warning',
389
389
  'button--transparent': this.color === 'transparent',
390
390
  'button--star': this.color === 'star',
391
- 'button--white': this.color === 'white',
392
391
  'button--outline': this.outline,
393
392
  'button--text': (this.text && !this.outline),
394
393
  'button--icon-button': !!this.iconButton,
@@ -244,22 +244,6 @@
244
244
  }
245
245
  }
246
246
 
247
- /**
248
- * White: white panel background with a neutral border, matching the
249
- * icon-button look but for normal (auto-width, text) buttons.
250
- *
251
- * Doubled-up selector so it out-cascades the standard/secondary combos.
252
- */
253
- .button.button--white {
254
- background: rgb(var(--zn-panel));
255
- border-color: var(--zn-button-border-color, rgb(var(--zn-border-color)));
256
- color: rgb(var(--zn-text));
257
-
258
- &:hover:not([disabled]), &:active:not([disabled]) {
259
- background: rgba(var(--zn-color-tab), 0.5);
260
- }
261
- }
262
-
263
247
  /**
264
248
  * Icon buttons
265
249
  *
@@ -489,4 +473,10 @@
489
473
 
490
474
  .button--panel-bg {
491
475
  background: rgb(var(--zn-panel));
476
+ border-color: var(--zn-button-border-color, rgb(var(--zn-border-color)));
477
+ color: rgb(var(--zn-text));
478
+
479
+ &:hover:not([disabled]), &:active:not([disabled]) {
480
+ background: rgba(var(--zn-color-tab), 0.5);
481
+ }
492
482
  }
@@ -20,6 +20,8 @@ import styles from './collapsible.scss';
20
20
  *
21
21
  * @slot header - Clicking will toggle the show state of the data
22
22
  *
23
+ * @csspart header - The header row (toggle).
24
+ * @csspart caption - The caption text.
23
25
  */
24
26
  export default class ZnCollapsible extends ZincElement {
25
27
  static styles: CSSResultGroup = unsafeCSS(styles);
@@ -51,7 +53,7 @@ export default class ZnCollapsible extends ZincElement {
51
53
 
52
54
  protected _store: Store;
53
55
 
54
- private readonly hasSlotController = new HasSlotController(this, '[default]', 'header', 'caption', 'label');
56
+ private readonly hasSlotController = new HasSlotController(this, '[default]', 'header', 'caption', 'label', 'description');
55
57
 
56
58
  private readonly observer = new MutationController(this, {
57
59
  target: null,
@@ -137,14 +139,17 @@ export default class ZnCollapsible extends ZincElement {
137
139
 
138
140
  return html`
139
141
  <div @click="${() => (!this.expanded ? (this.expanded = true) : '')}">
140
- <slot name="header" class="header" @click="${this.handleCollapse}">
142
+ <slot name="header" part="header" class="header" @click="${this.handleCollapse}">
141
143
  <div class="header__left">
142
- <p class="caption">
144
+ <p part="caption" class="caption">
143
145
  <slot name="caption">${this.caption}</slot>
144
146
  </p>
145
- <p class="description">
146
- <slot name="description">${this.description}</slot>
147
- </p>
147
+ ${this.description || this.hasSlotController.test('description')
148
+ ? html`
149
+ <p class="description">
150
+ <slot name="description">${this.description}</slot>
151
+ </p>`
152
+ : ''}
148
153
  </div>
149
154
  <div class="header__right">
150
155
  <slot name="label"><p class="label">${this.label}</p></slot>
@@ -722,14 +722,14 @@ export default class ZnDataTable extends ZincElement {
722
722
 
723
723
  return html`
724
724
  <div class="table__footer__pagination-buttons">
725
- <zn-button color="white"
726
- @click="${this.page !== 1 ? this.goToFirstPage : undefined}"
725
+ <zn-button @click="${this.page !== 1 ? this.goToFirstPage : undefined}"
727
726
  ?disabled="${this.page === 1}"
728
- icon="arrow_left@lu">First
727
+ icon="arrow_left@lu"
728
+ panel-bg>First
729
729
  </zn-button>
730
- <zn-button color="white"
731
- @click="${this.page !== 1 ? this.goToPreviousPage : undefined}"
732
- ?disabled="${this.page === 1}">Back
730
+ <zn-button @click="${this.page !== 1 ? this.goToPreviousPage : undefined}"
731
+ ?disabled="${this.page === 1}"
732
+ panel-bg>Back
733
733
  </zn-button>
734
734
  ${this.getPageRange().map((p) => p === 'ellipsis'
735
735
  ? html`<span class="table__footer__pagination-ellipsis">…</span>`
@@ -739,15 +739,15 @@ export default class ZnDataTable extends ZincElement {
739
739
  class="${p === this.page ? 'table__footer__pagination-page--active' : ''}">${p}
740
740
  </zn-button>`
741
741
  )}
742
- <zn-button color="white"
743
- @click="${this.page !== this.totalPages ? this.goToNextPage : undefined}"
744
- ?disabled="${this.page === this.totalPages}">Next
742
+ <zn-button @click="${this.page !== this.totalPages ? this.goToNextPage : undefined}"
743
+ ?disabled="${this.page === this.totalPages}"
744
+ panel-bg>Next
745
745
  </zn-button>
746
- <zn-button color="white"
747
- @click="${this.page !== this.totalPages ? this.goToLastPage : undefined}"
746
+ <zn-button @click="${this.page !== this.totalPages ? this.goToLastPage : undefined}"
748
747
  ?disabled="${this.page === this.totalPages}"
749
748
  icon="arrow_right@lu"
750
- icon-position="right">Last
749
+ icon-position="right"
750
+ panel-bg>Last
751
751
  </zn-button>
752
752
  </div>`;
753
753
  }