@kubex/zinc 1.1.91 → 1.1.94
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/dist/custom-elements.json +1738 -110
- package/dist/vscode.html-custom-data.json +136 -4
- package/dist/web-types.json +307 -4
- package/dist/zn.d.ts +298 -2
- package/dist/zn.min.js +708 -516
- package/docs/pages/components/page-builder.md +22 -0
- package/docs/pages/components/schedule-builder.md +345 -0
- package/package.json +1 -1
- package/src/components/alert/alert.scss +9 -13
- package/src/components/chip/chip.scss +1 -1
- package/src/components/content-block/content-block.component.ts +3 -2
- package/src/components/icon-picker/icon-picker.component.ts +1 -1
- package/src/components/linked-select/linked-select.component.ts +22 -5
- package/src/components/page/page.scss +13 -7
- package/src/components/page-builder/page-builder.component.ts +52 -7
- package/src/components/page-builder/page-builder.scss +166 -9
- package/src/components/page-builder/page-builder.test.ts +134 -0
- package/src/components/page-builder/page.types.ts +23 -3
- package/src/components/page-nav/page-nav.scss +9 -1
- package/src/components/panel/panel.component.ts +5 -1
- package/src/components/priority-list/priority-list.component.ts +1 -0
- package/src/components/priority-list/priority-list.scss +2 -1
- package/src/components/schedule-builder/index.ts +12 -0
- package/src/components/schedule-builder/schedule-builder.component.ts +1543 -0
- package/src/components/schedule-builder/schedule-builder.scss +448 -0
- package/src/components/schedule-builder/schedule-builder.test.ts +344 -0
- package/src/components/toggle/toggle.component.ts +2 -1
- package/src/zinc.ts +1 -0
- package/docs/superpowers/plans/2026-08-03-theme-editor.md +0 -1536
- package/docs/superpowers/specs/2026-08-03-theme-editor-design.md +0 -327
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
--inspector-col: 0px;
|
|
51
51
|
|
|
52
52
|
.inspector {
|
|
53
|
-
padding: 0;
|
|
54
53
|
border-left: none;
|
|
55
54
|
}
|
|
56
55
|
}
|
|
@@ -63,8 +62,10 @@
|
|
|
63
62
|
--palette-col: 240px;
|
|
64
63
|
}
|
|
65
64
|
|
|
65
|
+
// 300, not 280: below that the inspector's toggle rows wrap their switch
|
|
66
|
+
// under the label and the field rhythm breaks down.
|
|
66
67
|
.builder:not(.builder--inspector-collapsed) {
|
|
67
|
-
--inspector-col:
|
|
68
|
+
--inspector-col: 300px;
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -249,21 +250,177 @@ zn-page-palette-item {
|
|
|
249
250
|
rgb(var(--zn-body));
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
// Right column — anatomy shared with flow-builder's inspector (icon tile +
|
|
254
|
+
// title/type head over a scrolling body), so the two builders read alike.
|
|
252
255
|
.inspector {
|
|
253
256
|
display: flex;
|
|
254
257
|
flex-direction: column;
|
|
255
|
-
gap: 12px;
|
|
256
|
-
padding: 14px;
|
|
257
258
|
border-left: 1px solid rgb(var(--zn-border-color));
|
|
258
|
-
overflow
|
|
259
|
+
overflow: hidden; // the body scrolls, not the panel — the head stays put
|
|
260
|
+
animation: inspector-in 0.18s ease;
|
|
261
|
+
|
|
262
|
+
&-head {
|
|
263
|
+
display: flex;
|
|
264
|
+
align-items: center;
|
|
265
|
+
gap: 12px;
|
|
266
|
+
flex: 0 0 auto;
|
|
267
|
+
padding: 16px;
|
|
268
|
+
border-bottom: 1px solid rgb(var(--zn-border-color));
|
|
269
|
+
|
|
270
|
+
&__icon {
|
|
271
|
+
--section-accent: rgb(var(--zn-color-primary));
|
|
272
|
+
display: flex;
|
|
273
|
+
align-items: center;
|
|
274
|
+
justify-content: center;
|
|
275
|
+
flex: 0 0 auto;
|
|
276
|
+
width: 34px;
|
|
277
|
+
height: 34px;
|
|
278
|
+
border-radius: 8px;
|
|
279
|
+
color: var(--section-accent);
|
|
280
|
+
background: color-mix(in srgb, var(--section-accent) 15%, transparent);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Long section names truncate rather than pushing the close button out.
|
|
284
|
+
&__text {
|
|
285
|
+
flex: 1 1 auto;
|
|
286
|
+
min-width: 0;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
&__title {
|
|
290
|
+
font-size: 0.875rem;
|
|
291
|
+
font-weight: 600;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
&__type {
|
|
295
|
+
font-size: 0.75rem;
|
|
296
|
+
color: rgb(var(--zn-color-muted-text));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
&__title,
|
|
300
|
+
&__type {
|
|
301
|
+
overflow: hidden;
|
|
302
|
+
text-overflow: ellipsis;
|
|
303
|
+
white-space: nowrap;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Field typography and rhythm for the stamped config form. Host themes size
|
|
308
|
+
// form labels for full-width forms, which reads oversized in a ~343px panel —
|
|
309
|
+
// the --zn-input-* tokens inherit into every zinc control's shadow root, so
|
|
310
|
+
// retuning them here needs no per-control ::part() list.
|
|
311
|
+
&-body {
|
|
312
|
+
--pb-field-label-size: 0.8125rem;
|
|
313
|
+
--pb-field-note-size: 0.75rem;
|
|
314
|
+
--pb-field-gap: 22px;
|
|
315
|
+
--pb-label-gap: 6px;
|
|
316
|
+
--pb-note-gap: 5px;
|
|
317
|
+
--zn-input-label-font-size-medium: var(--pb-field-label-size);
|
|
318
|
+
--zn-input-label-font-weight: 600;
|
|
319
|
+
--zn-input-label-line-height: 1.3;
|
|
320
|
+
--zn-input-label-color: rgb(var(--zn-text));
|
|
321
|
+
--zn-input-help-text-font-size-medium: var(--pb-field-note-size);
|
|
322
|
+
--zn-input-help-text-color: rgb(var(--zn-color-muted-text));
|
|
323
|
+
--zn-toggle-margin: 0;
|
|
324
|
+
|
|
325
|
+
display: flex;
|
|
326
|
+
flex-direction: column;
|
|
327
|
+
flex: 1;
|
|
328
|
+
min-height: 0;
|
|
329
|
+
overflow-y: auto;
|
|
330
|
+
// Extra bottom padding so the last field isn't flush against the scroll end.
|
|
331
|
+
padding: 18px 16px 24px;
|
|
332
|
+
gap: var(--pb-field-gap);
|
|
333
|
+
|
|
334
|
+
// A field's own internal spacing comes from --zn-spacing-* tokens, which
|
|
335
|
+
// zinc leaves to the host theme — undefined here, so those declarations
|
|
336
|
+
// drop out and every field collapses onto the flex gap alone. Set the
|
|
337
|
+
// rhythm explicitly instead: tight inside a field, wide between them, so
|
|
338
|
+
// a label reads as belonging to the control below it rather than floating
|
|
339
|
+
// between two.
|
|
340
|
+
::part(form-control-label) {
|
|
341
|
+
margin-bottom: var(--pb-label-gap);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
::part(form-control-input) {
|
|
345
|
+
margin: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
::part(form-control-help-text) {
|
|
349
|
+
margin-top: var(--pb-note-gap);
|
|
350
|
+
}
|
|
259
351
|
|
|
352
|
+
// zn-toggle sizes its own label and description from a fixed type scale
|
|
353
|
+
// rather than the --zn-input-label-* tokens, so bring it into line through
|
|
354
|
+
// its parts — otherwise a toggle row outweighs every field around it.
|
|
355
|
+
zn-toggle::part(label) {
|
|
356
|
+
font-size: var(--pb-field-label-size);
|
|
357
|
+
font-weight: 600;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
zn-toggle::part(description) {
|
|
361
|
+
font-size: var(--pb-field-note-size);
|
|
362
|
+
margin-top: var(--pb-note-gap);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// The icon picker's trigger is a content-width button, which reads as a
|
|
366
|
+
// stranded chip in a column of full-width fields. Fill the row (the clear
|
|
367
|
+
// button, when there is a value, still takes its space beside it).
|
|
368
|
+
zn-icon-picker::part(trigger) {
|
|
369
|
+
flex: 1 1 auto;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
> *,
|
|
373
|
+
.inspector__form > * {
|
|
374
|
+
margin-block: 0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// The stamped wrapper is a layout box, not a field — it carries the same
|
|
378
|
+
// rhythm as the body so the host's controls space identically.
|
|
379
|
+
> .inspector__form {
|
|
380
|
+
display: flex;
|
|
381
|
+
flex-direction: column;
|
|
382
|
+
gap: var(--pb-field-gap);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
&-hint {
|
|
387
|
+
margin: 0;
|
|
388
|
+
font-size: 0.75rem;
|
|
389
|
+
color: rgb(var(--zn-color-muted-text));
|
|
390
|
+
}
|
|
260
391
|
}
|
|
261
392
|
|
|
262
|
-
.
|
|
263
|
-
.inspector__form {
|
|
393
|
+
.inspector-close {
|
|
264
394
|
display: flex;
|
|
265
|
-
|
|
266
|
-
|
|
395
|
+
align-items: center;
|
|
396
|
+
justify-content: center;
|
|
397
|
+
flex: 0 0 auto;
|
|
398
|
+
width: 28px;
|
|
399
|
+
height: 28px;
|
|
400
|
+
padding: 0;
|
|
401
|
+
border: none;
|
|
402
|
+
border-radius: 6px;
|
|
403
|
+
color: rgb(var(--zn-color-muted-text));
|
|
404
|
+
background: transparent;
|
|
405
|
+
cursor: pointer;
|
|
406
|
+
|
|
407
|
+
&:hover {
|
|
408
|
+
color: rgb(var(--zn-text));
|
|
409
|
+
background: rgb(var(--zn-color-tab));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// The panel slides in from the right as a section is selected (as flow-builder).
|
|
414
|
+
@keyframes inspector-in {
|
|
415
|
+
from {
|
|
416
|
+
opacity: 0;
|
|
417
|
+
transform: translateX(8px);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
to {
|
|
421
|
+
opacity: 1;
|
|
422
|
+
transform: translateX(0);
|
|
423
|
+
}
|
|
267
424
|
}
|
|
268
425
|
|
|
269
426
|
.declarations {
|
|
@@ -112,6 +112,35 @@ describe('<zn-page-builder>', () => {
|
|
|
112
112
|
expect(el.state.sections[0].type).to.equal('gone'); // preserved through save
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
+
it('should summarise a card with the chosen option label, not the stored value', async () => {
|
|
116
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
117
|
+
<zn-page-builder
|
|
118
|
+
config='{"sections":[{"id":"s1","type":"tile","data":{"icon":"folder","article":"1a2b3c"}}]}'>
|
|
119
|
+
<template type="tile" slot="config" label="Article">
|
|
120
|
+
<zn-icon-picker name="icon"></zn-icon-picker>
|
|
121
|
+
<zn-select name="article">
|
|
122
|
+
<zn-option value="1a2b3c">Getting Started</zn-option>
|
|
123
|
+
</zn-select>
|
|
124
|
+
</template>
|
|
125
|
+
</zn-page-builder>`);
|
|
126
|
+
await el.updateComplete;
|
|
127
|
+
|
|
128
|
+
const card = el.shadowRoot?.querySelector('zn-page-section-card');
|
|
129
|
+
expect(card?.getAttribute('summary')).to.equal('Getting Started');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should fall back to the first string value when nothing matches an option', async () => {
|
|
133
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
134
|
+
<zn-page-builder config='{"sections":[{"id":"s1","type":"hero","data":{"title":"Welcome"}}]}'>
|
|
135
|
+
<template type="hero" slot="config" label="Hero">
|
|
136
|
+
<input name="title">
|
|
137
|
+
</template>
|
|
138
|
+
</zn-page-builder>`);
|
|
139
|
+
await el.updateComplete;
|
|
140
|
+
|
|
141
|
+
expect(el.shadowRoot?.querySelector('zn-page-section-card')?.getAttribute('summary')).to.equal('Welcome');
|
|
142
|
+
});
|
|
143
|
+
|
|
115
144
|
it('should round-trip section data through the inspector name binding', async () => {
|
|
116
145
|
const el = await fixture<ZnPageBuilder>(html`
|
|
117
146
|
<zn-page-builder config='{"sections":[{"id":"s1","type":"hero","data":{"title":"Before"}}]}'>
|
|
@@ -206,6 +235,34 @@ describe('<zn-page-builder>', () => {
|
|
|
206
235
|
expect(el.state.sections[0].data.categories).to.deep.equal(['billing', 'setup']);
|
|
207
236
|
});
|
|
208
237
|
|
|
238
|
+
// zn-icon-picker keeps `value` as a plain accessor over its reactive `icon`,
|
|
239
|
+
// so it only prefills if the stamped element is upgraded before assignment —
|
|
240
|
+
// otherwise the assignment shadows the setter and the picker renders empty.
|
|
241
|
+
it('should prefill a control whose value is a plain accessor', async () => {
|
|
242
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
243
|
+
<zn-page-builder config='{"sections":[{"id":"s1","type":"hero","data":{"icon":"receipt"}}]}'>
|
|
244
|
+
<template type="hero" slot="config" label="Hero">
|
|
245
|
+
<zn-icon-picker name="icon" label="Icon" no-color no-library></zn-icon-picker>
|
|
246
|
+
</template>
|
|
247
|
+
</zn-page-builder>`);
|
|
248
|
+
await el.updateComplete;
|
|
249
|
+
|
|
250
|
+
el.shadowRoot?.querySelector('zn-page-section-card')?.dispatchEvent(new Event('click'));
|
|
251
|
+
await el.updateComplete;
|
|
252
|
+
|
|
253
|
+
const picker = el.shadowRoot!.querySelector<HTMLElement & { value: string; icon: string }>(
|
|
254
|
+
'.inspector__form zn-icon-picker[name="icon"]')!;
|
|
255
|
+
expect(picker, 'stamped icon picker').to.exist;
|
|
256
|
+
expect(picker.value).to.equal('receipt');
|
|
257
|
+
expect(picker.icon, 'reached the reactive property, not an own shadowing one').to.equal('receipt');
|
|
258
|
+
|
|
259
|
+
picker.value = 'payments';
|
|
260
|
+
picker.dispatchEvent(new CustomEvent('zn-change', {bubbles: true}));
|
|
261
|
+
await el.updateComplete;
|
|
262
|
+
|
|
263
|
+
expect(el.state.sections[0].data.icon).to.equal('payments');
|
|
264
|
+
});
|
|
265
|
+
|
|
209
266
|
it('should rename a section from the inspector', async () => {
|
|
210
267
|
const el = await fixture<ZnPageBuilder>(html`
|
|
211
268
|
<zn-page-builder config='{"sections":[{"id":"s1","type":"hero","data":{}}]}'>
|
|
@@ -224,6 +281,83 @@ describe('<zn-page-builder>', () => {
|
|
|
224
281
|
expect(el.state.sections[0].label).to.equal('My Hero');
|
|
225
282
|
});
|
|
226
283
|
|
|
284
|
+
it('should head the inspector with the section name and its type', async () => {
|
|
285
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
286
|
+
<zn-page-builder config='{"sections":[
|
|
287
|
+
{"id":"s1","type":"hero","label":"Top banner","data":{}},
|
|
288
|
+
{"id":"s2","type":"hero","data":{}}]}'>
|
|
289
|
+
<template type="hero" slot="config" label="Hero" icon="star">
|
|
290
|
+
<zn-input name="title" label="Title"></zn-input>
|
|
291
|
+
</template>
|
|
292
|
+
</zn-page-builder>`);
|
|
293
|
+
await el.updateComplete;
|
|
294
|
+
|
|
295
|
+
const cards = el.shadowRoot?.querySelectorAll('zn-page-section-card');
|
|
296
|
+
cards?.[0].dispatchEvent(new Event('click'));
|
|
297
|
+
await el.updateComplete;
|
|
298
|
+
|
|
299
|
+
const head = el.shadowRoot?.querySelector('[part="inspector-header"]');
|
|
300
|
+
expect(head, 'inspector header').to.exist;
|
|
301
|
+
expect(head?.querySelector('.inspector-head__title')?.textContent?.trim()).to.equal('Top banner');
|
|
302
|
+
expect(head?.querySelector('.inspector-head__type')?.textContent?.trim()).to.equal('Hero');
|
|
303
|
+
expect(head?.querySelector('zn-icon')?.getAttribute('src')).to.equal('star');
|
|
304
|
+
expect(el.shadowRoot?.querySelector('[part="inspector-body"] .inspector__rename'), 'rename in body').to.exist;
|
|
305
|
+
|
|
306
|
+
// An unnamed section's title already is the type label — don't print it twice.
|
|
307
|
+
cards?.[1].dispatchEvent(new Event('click'));
|
|
308
|
+
await el.updateComplete;
|
|
309
|
+
const unnamed = el.shadowRoot?.querySelector('[part="inspector-header"]');
|
|
310
|
+
expect(unnamed?.querySelector('.inspector-head__title')?.textContent?.trim()).to.equal('Hero');
|
|
311
|
+
expect(unnamed?.querySelector('.inspector-head__type'), 'no duplicate type line').to.not.exist;
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should clear the selection from the inspector close button', async () => {
|
|
315
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
316
|
+
<zn-page-builder config='{"sections":[{"id":"s1","type":"hero","data":{}}]}'>
|
|
317
|
+
<template type="hero" slot="config" label="Hero"></template>
|
|
318
|
+
</zn-page-builder>`);
|
|
319
|
+
await el.updateComplete;
|
|
320
|
+
|
|
321
|
+
el.shadowRoot?.querySelector('zn-page-section-card')?.dispatchEvent(new Event('click'));
|
|
322
|
+
await el.updateComplete;
|
|
323
|
+
expect(el.shadowRoot?.querySelector('[part="inspector"]')).to.exist;
|
|
324
|
+
|
|
325
|
+
el.shadowRoot?.querySelector<HTMLButtonElement>('.inspector-close')?.click();
|
|
326
|
+
await el.updateComplete;
|
|
327
|
+
expect(el.shadowRoot?.querySelector('[part="inspector"]'), 'inspector closes').to.not.exist;
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should hint when the selected type has no settings', async () => {
|
|
331
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
332
|
+
<zn-page-builder config='{"sections":[{"id":"s1","type":"divider","data":{}}]}'>
|
|
333
|
+
<template type="divider" slot="config" label="Divider"></template>
|
|
334
|
+
</zn-page-builder>`);
|
|
335
|
+
await el.updateComplete;
|
|
336
|
+
|
|
337
|
+
el.shadowRoot?.querySelector('zn-page-section-card')?.dispatchEvent(new Event('click'));
|
|
338
|
+
await el.updateComplete;
|
|
339
|
+
|
|
340
|
+
expect(el.shadowRoot?.querySelector('.inspector-hint')).to.exist;
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should lay stamped toggles out as a row, honouring a host-set position', async () => {
|
|
344
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
345
|
+
<zn-page-builder config='{"sections":[{"id":"s1","type":"hero","data":{}}]}'>
|
|
346
|
+
<template type="hero" slot="config" label="Hero">
|
|
347
|
+
<zn-toggle name="showSearch" label="Show search"></zn-toggle>
|
|
348
|
+
<zn-toggle name="showNav" label="Show nav" label-position="top"></zn-toggle>
|
|
349
|
+
</template>
|
|
350
|
+
</zn-page-builder>`);
|
|
351
|
+
await el.updateComplete;
|
|
352
|
+
|
|
353
|
+
el.shadowRoot?.querySelector('zn-page-section-card')?.dispatchEvent(new Event('click'));
|
|
354
|
+
await el.updateComplete;
|
|
355
|
+
|
|
356
|
+
const form = el.shadowRoot?.querySelector('.inspector__form');
|
|
357
|
+
expect(form?.querySelector('zn-toggle[name="showSearch"]')?.getAttribute('label-position')).to.equal('left');
|
|
358
|
+
expect(form?.querySelector('zn-toggle[name="showNav"]')?.getAttribute('label-position')).to.equal('top');
|
|
359
|
+
});
|
|
360
|
+
|
|
227
361
|
it('should undo and redo a section add', async () => {
|
|
228
362
|
const el = await fixture<ZnPageBuilder>(html`
|
|
229
363
|
<zn-page-builder>
|
|
@@ -68,8 +68,28 @@ export function generateSectionId(): string {
|
|
|
68
68
|
return `s-${Date.now().toString(36)}-${(sectionCounter++).toString(36)}`;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* The visible text of the option a value was chosen from, so a card summarises a
|
|
73
|
+
* select by what the user picked rather than by the opaque id it stores.
|
|
74
|
+
*/
|
|
75
|
+
function optionLabel(type: PageSectionType | undefined, name: string, value: string): string {
|
|
76
|
+
const control = type?.configTemplate?.content.querySelector(`[name="${CSS.escape(name)}"]`);
|
|
77
|
+
const escaped = CSS.escape(value);
|
|
78
|
+
const option = control?.querySelector(`option[value="${escaped}"], zn-option[value="${escaped}"]`);
|
|
79
|
+
return option?.textContent?.trim() ?? '';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Card summary: the label of the first value chosen from a select, else the first
|
|
84
|
+
* non-empty string value, else the type description. Options win over field order
|
|
85
|
+
* so a tile reads as its linked item however its other fields were filled in.
|
|
86
|
+
*/
|
|
72
87
|
export function sectionSummary(section: PageSection, type?: PageSectionType): string {
|
|
73
|
-
const
|
|
74
|
-
|
|
88
|
+
const strings = Object.entries(section.data)
|
|
89
|
+
.filter((entry): entry is [string, string] => typeof entry[1] === 'string' && entry[1].trim() !== '');
|
|
90
|
+
for (const [name, value] of strings) {
|
|
91
|
+
const label = optionLabel(type, name, value);
|
|
92
|
+
if (label) return label;
|
|
93
|
+
}
|
|
94
|
+
return strings[0]?.[1] ?? type?.description ?? '';
|
|
75
95
|
}
|
|
@@ -56,16 +56,23 @@
|
|
|
56
56
|
display: flex;
|
|
57
57
|
flex-direction: column;
|
|
58
58
|
gap: var(--zn-spacing-medium);
|
|
59
|
+
|
|
60
|
+
padding-top: var(--zn-spacing-medium);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.navigation-group:first-of-type {
|
|
64
|
+
padding-top: 0;
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
.navigation-group h4 {
|
|
62
68
|
margin: 0;
|
|
63
|
-
padding:
|
|
69
|
+
padding: 0;
|
|
64
70
|
color: rgb(var(--zn-text-heading));
|
|
65
71
|
font-size: var(--zn-font-size-medium);
|
|
66
72
|
font-weight: var(--zn-font-weight-semibold);
|
|
67
73
|
}
|
|
68
74
|
|
|
75
|
+
|
|
69
76
|
.navigation-group .navigation-item {
|
|
70
77
|
color: rgb(var(--zn-text));
|
|
71
78
|
font-size: var(--zn-font-size-medium);
|
|
@@ -73,6 +80,7 @@
|
|
|
73
80
|
display: flex;
|
|
74
81
|
align-items: center;
|
|
75
82
|
gap: var(--zn-spacing-small);
|
|
83
|
+
min-height: var(--zn-icon-size);
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
.navigation-item.active {
|
|
@@ -76,6 +76,10 @@ export default class ZnPanel extends ZincElement {
|
|
|
76
76
|
const hasActionSlot = this.hasSlotController.test('actions');
|
|
77
77
|
const hasFooterSlot = this.hasSlotController.test('footer');
|
|
78
78
|
const hasHeader = this.caption || hasActionSlot;
|
|
79
|
+
// Transparent panels with no vertical body padding have nothing to separate the header from,
|
|
80
|
+
// so the underline is just a stray rule across the page.
|
|
81
|
+
const isFlushY = this.flush || this.tabbed || this.flushY;
|
|
82
|
+
const underlineHeader = !this.headerBorderless && !(this.transparent && isFlushY);
|
|
79
83
|
|
|
80
84
|
return html`
|
|
81
85
|
<div class="${classMap({
|
|
@@ -98,7 +102,7 @@ export default class ZnPanel extends ZincElement {
|
|
|
98
102
|
${hasHeader ? html`
|
|
99
103
|
<zn-header class="${classMap({
|
|
100
104
|
"panel__header": true,
|
|
101
|
-
"panel__header--underline":
|
|
105
|
+
"panel__header--underline": underlineHeader,
|
|
102
106
|
})}"
|
|
103
107
|
icon="${this.icon}"
|
|
104
108
|
caption="${this.caption}"
|
|
@@ -51,6 +51,7 @@ export interface PriorityItem {
|
|
|
51
51
|
*
|
|
52
52
|
* @cssproperty --zn-priority-list-item-gap - The gap between list items. Defaults to `var(--zn-spacing-2x-small)`.
|
|
53
53
|
* @cssproperty --zn-priority-list-item-padding - The padding inside each item. Defaults to `var(--zn-spacing-small) var(--zn-spacing-medium)`.
|
|
54
|
+
* @cssproperty --zn-priority-list-actions-gap - The gap between an item's slotted actions. Defaults to `var(--zn-spacing-x-small)`.
|
|
54
55
|
* @cssproperty --zn-priority-list-handle-color - The color of the drag handle. Defaults to `var(--zn-color-neutral-500)`.
|
|
55
56
|
* @cssproperty --zn-priority-list-priority-color - The color of the priority number. Defaults to `var(--zn-color-neutral-600)`.
|
|
56
57
|
*/
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
--zn-priority-list-item-gap: var(--zn-spacing-2x-small);
|
|
9
9
|
--zn-priority-list-item-padding: var(--zn-spacing-small) var(--zn-spacing-medium);
|
|
10
|
+
--zn-priority-list-actions-gap: var(--zn-spacing-small);
|
|
10
11
|
--zn-priority-list-handle-color: var(--zn-color-neutral-400);
|
|
11
12
|
--zn-priority-list-priority-color: var(--zn-color-neutral-600);
|
|
12
13
|
}
|
|
@@ -134,7 +135,7 @@
|
|
|
134
135
|
.priority-list__actions {
|
|
135
136
|
display: flex;
|
|
136
137
|
align-items: center;
|
|
137
|
-
gap: var(--zn-
|
|
138
|
+
gap: var(--zn-priority-list-actions-gap);
|
|
138
139
|
flex-shrink: 0;
|
|
139
140
|
// Re-enable pointer events so action buttons are clickable
|
|
140
141
|
pointer-events: auto;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ZnScheduleBuilder from './schedule-builder.component';
|
|
2
|
+
|
|
3
|
+
export * from './schedule-builder.component';
|
|
4
|
+
export default ZnScheduleBuilder;
|
|
5
|
+
|
|
6
|
+
ZnScheduleBuilder.define('zn-schedule-builder');
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
interface HTMLElementTagNameMap {
|
|
10
|
+
'zn-schedule-builder': ZnScheduleBuilder;
|
|
11
|
+
}
|
|
12
|
+
}
|