@kubex/zinc 1.1.47 → 1.1.49
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 +383 -74
- package/dist/vscode.html-custom-data.json +54 -11
- package/dist/web-types.json +127 -23
- package/dist/zn.d.ts +69 -0
- package/dist/zn.min.js +342 -318
- package/docs/pages/components/color-select.md +34 -0
- package/package.json +1 -1
- package/src/components/channel-tile/channel-tile.component.ts +18 -2
- package/src/components/channel-tile/channel-tile.scss +2 -2
- package/src/components/color-select/color-select.component.ts +123 -0
- package/src/components/color-select/color-select.scss +78 -0
- package/src/components/color-select/color-select.test.ts +28 -0
- package/src/components/color-select/index.ts +12 -0
- package/src/components/file/file.component.ts +16 -8
- package/src/components/file/file.test.ts +27 -3
- package/src/components/textarea/textarea.scss +6 -0
- package/src/zinc.ts +1 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
meta:
|
|
3
|
+
title: Color Select
|
|
4
|
+
description: A simple color picker. The dropdown lists color swatches with names; the closed control shows only the selected color's swatch.
|
|
5
|
+
layout: component
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
```html:preview
|
|
9
|
+
<zn-color-select label="Color" name="color"></zn-color-select>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Examples
|
|
13
|
+
|
|
14
|
+
### With a Default Value
|
|
15
|
+
|
|
16
|
+
Set the selected color with the `value` attribute (lowercase color name).
|
|
17
|
+
|
|
18
|
+
```html:preview
|
|
19
|
+
<zn-color-select label="Color" name="color" value="blue"></zn-color-select>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Clearable
|
|
23
|
+
|
|
24
|
+
Add the `clearable` attribute to show a clear button when a color is selected.
|
|
25
|
+
|
|
26
|
+
```html:preview
|
|
27
|
+
<zn-color-select label="Color" name="color" value="green" clearable></zn-color-select>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Help Text
|
|
31
|
+
|
|
32
|
+
```html:preview
|
|
33
|
+
<zn-color-select label="Color" name="color" help-text="Pick a color for the label."></zn-color-select>
|
|
34
|
+
```
|
package/package.json
CHANGED
|
@@ -176,6 +176,13 @@ export default class ZnChannelTile extends ZincElement {
|
|
|
176
176
|
return Math.min(100, Math.max(0, (elapsed / total) * 100));
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
private _remainingSeconds(): number | null {
|
|
180
|
+
// Only surfaced for auto-accept countdowns, not plain reservation windows.
|
|
181
|
+
const until = this._reservedUntilMs();
|
|
182
|
+
if (!until || !this.autoAcceptDelay) return null;
|
|
183
|
+
return Math.max(0, Math.ceil((until - Date.now()) / 1000));
|
|
184
|
+
}
|
|
185
|
+
|
|
179
186
|
private _handleReject = (e: MouseEvent) => {
|
|
180
187
|
e.preventDefault();
|
|
181
188
|
e.stopPropagation();
|
|
@@ -193,6 +200,7 @@ export default class ZnChannelTile extends ZincElement {
|
|
|
193
200
|
protected render(): unknown {
|
|
194
201
|
const title = this.title || (this.available ? 'Available' : '');
|
|
195
202
|
const countdown = this._isCountingDown() ? this._countdownPercent() : null;
|
|
203
|
+
const remaining = this._isCountingDown() ? this._remainingSeconds() : null;
|
|
196
204
|
|
|
197
205
|
return html`
|
|
198
206
|
<div
|
|
@@ -215,8 +223,16 @@ export default class ZnChannelTile extends ZincElement {
|
|
|
215
223
|
<div class="channel-tile__body">
|
|
216
224
|
${this._renderLeading()}
|
|
217
225
|
<div class="channel-tile__content">
|
|
218
|
-
|
|
219
|
-
|
|
226
|
+
${remaining !== null
|
|
227
|
+
? this.title
|
|
228
|
+
? html`
|
|
229
|
+
<h3 class="channel-tile__title"><slot name="title">${this.title}</slot></h3>
|
|
230
|
+
<p class="channel-tile__subtitle">${this.subtitle} (${remaining}s)</p>`
|
|
231
|
+
: html`
|
|
232
|
+
<h3 class="channel-tile__title">${this.subtitle} (${remaining}s)</h3>`
|
|
233
|
+
: html`
|
|
234
|
+
<h3 class="channel-tile__title"><slot name="title">${title}</slot></h3>
|
|
235
|
+
<p class="channel-tile__subtitle"><slot name="subtitle">${this.subtitle}</slot></p>`}
|
|
220
236
|
</div>
|
|
221
237
|
<slot name="footer" class="channel-tile__footer"></slot>
|
|
222
238
|
</div>
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
|
|
70
70
|
.channel-tile__overlay {
|
|
71
71
|
position: absolute;
|
|
72
|
-
top:
|
|
72
|
+
top: 24px; // sit below the header strip (fixed 24px)
|
|
73
73
|
left: 0;
|
|
74
74
|
bottom: 0;
|
|
75
75
|
width: 0;
|
|
76
|
-
background-color:
|
|
76
|
+
background-color: rgb(154, 232, 162);
|
|
77
77
|
transition: width 0.2s linear;
|
|
78
78
|
pointer-events: none;
|
|
79
79
|
z-index: 0;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {colors} from "../data-select/providers/color-data-provider";
|
|
2
|
+
import {type CSSResultGroup, html, nothing, unsafeCSS} from 'lit';
|
|
3
|
+
import {FormControlController} from "../../internal/form";
|
|
4
|
+
import {property, query} from 'lit/decorators.js';
|
|
5
|
+
import {watch} from "../../internal/watch";
|
|
6
|
+
import ZincElement from '../../internal/zinc-element';
|
|
7
|
+
import ZnOption from "../option";
|
|
8
|
+
import ZnSelect from "../select";
|
|
9
|
+
import type {ZincFormControl} from '../../internal/zinc-element';
|
|
10
|
+
|
|
11
|
+
import styles from './color-select.scss';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @summary A simple color picker. The dropdown lists color swatches with names; the closed control shows only the selected color's swatch.
|
|
15
|
+
* @documentation https://zinc.style/components/color-select
|
|
16
|
+
* @status experimental
|
|
17
|
+
* @since 1.0
|
|
18
|
+
*
|
|
19
|
+
* @dependency zn-select
|
|
20
|
+
* @dependency zn-option
|
|
21
|
+
*
|
|
22
|
+
* @event zn-input - Emitted when the selected color changes.
|
|
23
|
+
* @event zn-clear - Emitted when the clear button is activated.
|
|
24
|
+
*
|
|
25
|
+
* @csspart swatch - The color swatch shown in the closed control.
|
|
26
|
+
* @csspart combobox - The container that wraps the swatch and expand button (forwarded from zn-select).
|
|
27
|
+
* @csspart form-control-help-text - The help text's wrapper (forwarded from zn-select).
|
|
28
|
+
* @csspart form-control-input - The select's wrapper (forwarded from zn-select).
|
|
29
|
+
*/
|
|
30
|
+
export default class ZnColorSelect extends ZincElement implements ZincFormControl {
|
|
31
|
+
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
32
|
+
static dependencies = {
|
|
33
|
+
'zn-select': ZnSelect,
|
|
34
|
+
'zn-option': ZnOption,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
@query('#select') select: ZnSelect;
|
|
38
|
+
|
|
39
|
+
/** The name of the select. Used for form submission. */
|
|
40
|
+
@property() name: string;
|
|
41
|
+
|
|
42
|
+
/** The selected color (lowercase name, e.g. "red"). Used for form submission. */
|
|
43
|
+
@property() value = '';
|
|
44
|
+
|
|
45
|
+
/** The select's label. */
|
|
46
|
+
@property() label = '';
|
|
47
|
+
|
|
48
|
+
/** The select's help text. */
|
|
49
|
+
@property({attribute: 'help-text'}) helpText = '';
|
|
50
|
+
|
|
51
|
+
/** Disables the select. */
|
|
52
|
+
@property({type: Boolean, reflect: true}) disabled = false;
|
|
53
|
+
|
|
54
|
+
/** Makes the select a required field. */
|
|
55
|
+
@property({type: Boolean, reflect: true}) required = false;
|
|
56
|
+
|
|
57
|
+
/** Shows a clear button when a color is selected. */
|
|
58
|
+
@property({type: Boolean}) clearable = false;
|
|
59
|
+
|
|
60
|
+
private readonly formControlController = new FormControlController(this);
|
|
61
|
+
|
|
62
|
+
get validity(): ValidityState {
|
|
63
|
+
return this.select.validity;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get validationMessage(): string {
|
|
67
|
+
return this.select.validationMessage;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
checkValidity(): boolean {
|
|
71
|
+
return this.select.checkValidity();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getForm(): HTMLFormElement | null {
|
|
75
|
+
return this.formControlController.getForm();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
reportValidity(): boolean {
|
|
79
|
+
return this.select.reportValidity();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setCustomValidity(message: string): void {
|
|
83
|
+
this.select.setCustomValidity(message);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@watch('value', {waitUntilFirstUpdate: true})
|
|
87
|
+
handleValueChange() {
|
|
88
|
+
this.formControlController.updateValidity();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private handleInput = (e: Event) => {
|
|
92
|
+
const target = e.target as ZnSelect;
|
|
93
|
+
this.value = (target.value as string) ?? '';
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
private handleClear = () => {
|
|
97
|
+
this.value = '';
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
protected render() {
|
|
101
|
+
return html`
|
|
102
|
+
<zn-select id="select"
|
|
103
|
+
placement="bottom-start"
|
|
104
|
+
label="${this.label}"
|
|
105
|
+
help-text="${this.helpText}"
|
|
106
|
+
name="${this.name}"
|
|
107
|
+
clearable=${this.clearable || nothing}
|
|
108
|
+
required=${this.required || nothing}
|
|
109
|
+
?disabled="${this.disabled}"
|
|
110
|
+
.value="${this.value}"
|
|
111
|
+
@zn-input="${this.handleInput}"
|
|
112
|
+
@zn-clear="${this.handleClear}"
|
|
113
|
+
exportparts="combobox,form-control-help-text,form-control-input">
|
|
114
|
+
<div slot="prefix" part="swatch"
|
|
115
|
+
class="color-swatch ${this.value ? `color-swatch--${this.value}` : 'color-swatch--empty'}"></div>
|
|
116
|
+
${colors.map(color => html`
|
|
117
|
+
<zn-option value="${color.toLowerCase()}">
|
|
118
|
+
<div slot="prefix" class="color-swatch color-swatch--${color.toLowerCase()}"></div>
|
|
119
|
+
${color}
|
|
120
|
+
</zn-option>`)}
|
|
121
|
+
</zn-select>`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
@use "../../wc";
|
|
2
|
+
|
|
3
|
+
// Same palette as data-select's color provider
|
|
4
|
+
$colors: (
|
|
5
|
+
red: var(--zn-color-red-500),
|
|
6
|
+
blue: var(--zn-color-blue-500),
|
|
7
|
+
orange: var(--zn-color-orange-500),
|
|
8
|
+
yellow: var(--zn-color-yellow-400),
|
|
9
|
+
indigo: var(--zn-color-indigo-500),
|
|
10
|
+
violet: var(--zn-color-violet-500),
|
|
11
|
+
green: var(--zn-color-green-500),
|
|
12
|
+
pink: var(--zn-color-pink-500),
|
|
13
|
+
gray: var(--zn-color-gray-500)
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
// Only wide enough for the swatch and the expand icon
|
|
17
|
+
:host {
|
|
18
|
+
display: inline-block;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.color-swatch {
|
|
22
|
+
width: var(--zn-spacing-small);
|
|
23
|
+
height: var(--zn-spacing-small);
|
|
24
|
+
display: block;
|
|
25
|
+
border-radius: var(--zn-border-radius-circle);
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
|
|
28
|
+
@each $name, $color in $colors {
|
|
29
|
+
&--#{"" + $name} {
|
|
30
|
+
background-color: #{$color};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&--empty {
|
|
35
|
+
border: 1px dashed var(--zn-color-neutral-400);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Never stretch to label/help-text width — only fit the swatch and controls
|
|
40
|
+
zn-select::part(combobox) {
|
|
41
|
+
width: fit-content;
|
|
42
|
+
padding-inline-end: var(--zn-spacing-2x-small);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Preview shows only the swatch — collapse the text input entirely
|
|
46
|
+
zn-select::part(display-input) {
|
|
47
|
+
width: 0;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
color: transparent;
|
|
50
|
+
caret-color: transparent;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Let the dropdown grow to fit swatch + name instead of matching the control's width
|
|
54
|
+
zn-select::part(listbox) {
|
|
55
|
+
min-width: max-content;
|
|
56
|
+
max-height: 300px;
|
|
57
|
+
padding-block: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Tighten the combobox: small, even gaps between swatch, clear button and chevron
|
|
61
|
+
.color-swatch[part="swatch"] {
|
|
62
|
+
margin-inline-start: var(--zn-spacing-x-small);
|
|
63
|
+
margin-inline-end: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Gap between swatch and name in the dropdown list
|
|
67
|
+
zn-option .color-swatch {
|
|
68
|
+
margin-inline-end: var(--zn-spacing-x-small);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
zn-select::part(clear-button),
|
|
72
|
+
zn-select::part(expand-icon) {
|
|
73
|
+
margin-inline-start: var(--zn-spacing-2x-small);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
zn-select::part(clear-button) {
|
|
77
|
+
margin-inline-start: var(--zn-spacing-x-small);
|
|
78
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import '../../../dist/zn.min.js';
|
|
2
|
+
import {expect, fixture, html} from '@open-wc/testing';
|
|
3
|
+
|
|
4
|
+
describe('<zn-color-select>', () => {
|
|
5
|
+
it('should render a component', async () => {
|
|
6
|
+
const el = await fixture(html`<zn-color-select></zn-color-select>`);
|
|
7
|
+
expect(el).to.exist;
|
|
8
|
+
expect(el.shadowRoot).to.exist;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should list the nine built-in colors', async () => {
|
|
12
|
+
const el = await fixture(html`<zn-color-select></zn-color-select>`);
|
|
13
|
+
const options = el.shadowRoot!.querySelectorAll('zn-option');
|
|
14
|
+
expect(options.length).to.equal(9);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should show the selected color swatch in the preview', async () => {
|
|
18
|
+
const el = await fixture(html`<zn-color-select value="red"></zn-color-select>`);
|
|
19
|
+
const swatch = el.shadowRoot!.querySelector('[slot="prefix"]')!;
|
|
20
|
+
expect(swatch.classList.contains('color-swatch--red')).to.be.true;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should show an empty swatch when no color is selected', async () => {
|
|
24
|
+
const el = await fixture(html`<zn-color-select></zn-color-select>`);
|
|
25
|
+
const swatch = el.shadowRoot!.querySelector('[slot="prefix"]')!;
|
|
26
|
+
expect(swatch.classList.contains('color-swatch--empty')).to.be.true;
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ZnColorSelect from './color-select.component';
|
|
2
|
+
|
|
3
|
+
export * from './color-select.component';
|
|
4
|
+
export default ZnColorSelect;
|
|
5
|
+
|
|
6
|
+
ZnColorSelect.define('zn-color-select');
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
interface HTMLElementTagNameMap {
|
|
10
|
+
'zn-color-select': ZnColorSelect;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -421,18 +421,26 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
|
|
|
421
421
|
// Use the transferred file list from the drag drop interface
|
|
422
422
|
const hasTrigger = this.hasSlotController.test('trigger');
|
|
423
423
|
if (!hasTrigger) {
|
|
424
|
-
const
|
|
425
|
-
const
|
|
424
|
+
const inputChosen = this.inputChosen;
|
|
425
|
+
const dropareaIcon = this.dropareaIcon;
|
|
426
426
|
|
|
427
|
-
if (this.droparea) {
|
|
428
|
-
const dropIconAnimation = getAnimation(
|
|
427
|
+
if (this.droparea && dropareaIcon) {
|
|
428
|
+
const dropIconAnimation = getAnimation(dropareaIcon, 'file.iconDrop', {dir: this.localize.dir()});
|
|
429
429
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
430
|
-
animateTo(
|
|
430
|
+
animateTo(dropareaIcon, dropIconAnimation.keyframes, dropIconAnimation.options);
|
|
431
431
|
}
|
|
432
|
-
|
|
433
|
-
|
|
432
|
+
|
|
433
|
+
if (inputChosen) {
|
|
434
|
+
const disappearAnimation = getAnimation(inputChosen, 'file.text.disappear', {dir: this.localize.dir()});
|
|
435
|
+
await animateTo(inputChosen, disappearAnimation.keyframes, disappearAnimation.options);
|
|
436
|
+
}
|
|
437
|
+
|
|
434
438
|
this.handleFiles(files);
|
|
435
|
-
|
|
439
|
+
|
|
440
|
+
if (inputChosen) {
|
|
441
|
+
const appearAnimation = getAnimation(inputChosen, 'file.text.appear', {dir: this.localize.dir()});
|
|
442
|
+
await animateTo(inputChosen, appearAnimation.keyframes, appearAnimation.options);
|
|
443
|
+
}
|
|
436
444
|
} else {
|
|
437
445
|
this.handleFiles(files);
|
|
438
446
|
}
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
|
-
import {
|
|
2
|
+
import {expect, fixture, html, oneEvent} from '@open-wc/testing';
|
|
3
|
+
import type ZnFile from './file.component';
|
|
3
4
|
|
|
4
|
-
describe('<zn-
|
|
5
|
+
describe('<zn-file>', () => {
|
|
5
6
|
it('should render a component', async () => {
|
|
6
|
-
const el = await fixture(html
|
|
7
|
+
const el = await fixture(html`<zn-file></zn-file>`);
|
|
7
8
|
|
|
8
9
|
expect(el).to.exist;
|
|
9
10
|
});
|
|
11
|
+
|
|
12
|
+
for (const droparea of [false, true]) {
|
|
13
|
+
it(`accepts a drop when the ${droparea ? 'droparea' : 'standard input'} has no animation target`, async () => {
|
|
14
|
+
const el = await fixture<ZnFile>(html`<zn-file ?droparea=${droparea}></zn-file>`);
|
|
15
|
+
const dataTransfer = new DataTransfer();
|
|
16
|
+
dataTransfer.items.add(new File(['content'], 'example.txt', {type: 'text/plain'}));
|
|
17
|
+
const dropHandler = el as unknown as {
|
|
18
|
+
handleTransferItems: (items: DataTransferItemList | null) => Promise<FileList>;
|
|
19
|
+
};
|
|
20
|
+
dropHandler.handleTransferItems = () => Promise.resolve(dataTransfer.files);
|
|
21
|
+
|
|
22
|
+
const changed = oneEvent(el, 'zn-change');
|
|
23
|
+
el.shadowRoot!.querySelector<HTMLElement>('.form-control')!.dispatchEvent(new DragEvent('drop', {
|
|
24
|
+
bubbles: true,
|
|
25
|
+
cancelable: true,
|
|
26
|
+
dataTransfer
|
|
27
|
+
}));
|
|
28
|
+
await changed;
|
|
29
|
+
|
|
30
|
+
expect(el.files).to.have.length(1);
|
|
31
|
+
expect(el.files![0].name).to.equal('example.txt');
|
|
32
|
+
});
|
|
33
|
+
}
|
|
10
34
|
});
|
package/src/zinc.ts
CHANGED
|
@@ -73,6 +73,7 @@ export { default as File } from './components/file';
|
|
|
73
73
|
export { default as CheckboxGroup } from './components/checkbox-group';
|
|
74
74
|
export { default as Item } from './components/item';
|
|
75
75
|
export { default as DataSelect } from './components/data-select';
|
|
76
|
+
export { default as ColorSelect } from './components/color-select';
|
|
76
77
|
export { default as ButtonMenu } from './components/button-menu';
|
|
77
78
|
export { default as HoverContainer } from './components/hover-container';
|
|
78
79
|
export { default as Slideout } from './components/slideout';
|