@kubex/zinc 1.1.11 → 1.1.13

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,168 @@
1
+ ---
2
+ meta:
3
+ title: Channel Tile
4
+ description: A channel/queue slot tile with an active (occupied) face showing an in-progress item, and an available (empty) face advertising capacity with optional auto-accept.
5
+ layout: component
6
+ ---
7
+
8
+ The channel tile represents a single slot in a queue or channel rail. It has two faces:
9
+
10
+ - **Active** — the slot is occupied by an in-progress item (header, leading icon, title/subtitle, and a progress/SLA bar).
11
+ - **Available** — the slot is empty and advertising capacity. When `incoming` is set it reserves an arriving item with a countdown that can auto-accept.
12
+
13
+ Tiles are designed to sit side by side in a horizontal rail; each is a fixed-width block with a right divider.
14
+
15
+ ```html:preview
16
+ <div style="display: flex;">
17
+ <zn-channel-tile
18
+ header="Acme Support"
19
+ icon="chat"
20
+ color="info"
21
+ title="Jane Cooper"
22
+ subtitle="Billing question"
23
+ progress="60"
24
+ progress-color="rgb(var(--zn-color-info))">
25
+ </zn-channel-tile>
26
+ <zn-channel-tile available header="Acme Support" subtitle="Waiting for the next chat"></zn-channel-tile>
27
+ </div>
28
+ ```
29
+
30
+ ## Examples
31
+
32
+ ### Active Tile
33
+
34
+ The default (occupied) face. Set `header` for the top bar, `icon` + `color` for the leading badge, and `title`/`subtitle` for the item. The `progress` value (0–100) fills the SLA bar along the bottom edge.
35
+
36
+ ```html:preview
37
+ <div style="display: flex;">
38
+ <zn-channel-tile
39
+ header="Acme Support"
40
+ icon="chat"
41
+ color="info"
42
+ title="Jane Cooper"
43
+ subtitle="Billing question"
44
+ progress="45"
45
+ progress-color="rgb(var(--zn-color-info))">
46
+ </zn-channel-tile>
47
+ </div>
48
+ ```
49
+
50
+ ### Available Tile
51
+
52
+ Set `available` to render the empty face. When `title` is omitted it defaults to "Available". The leading slot shows a built-in accept button.
53
+
54
+ ```html:preview
55
+ <div style="display: flex;">
56
+ <zn-channel-tile available subtitle="Ready for the next interaction"></zn-channel-tile>
57
+ </div>
58
+ ```
59
+
60
+ ### Accepting an Item
61
+
62
+ Provide `accept-uri` and the built-in accept button becomes a link to it. When a tile is accepted, a cancelable `zn-accept` event fires before the fetch — call `preventDefault()` on it to take over the accept yourself.
63
+
64
+ ```html:preview
65
+ <div style="display: flex;">
66
+ <zn-channel-tile
67
+ available
68
+ fid="chat-4821"
69
+ accept-uri="/queue/accept/chat-4821"
70
+ subtitle="Click to accept">
71
+ </zn-channel-tile>
72
+ </div>
73
+ ```
74
+
75
+ ### Incoming with Auto-Accept
76
+
77
+ An `available` tile with `incoming` reserves an arriving item. Provide `reserved-until` (an epoch in seconds or milliseconds marking the end of the window) and `auto-accept-delay` (the window length in milliseconds) to drive the countdown overlay. When the window elapses the tile auto-accepts via `accept-uri`.
78
+
79
+ ```html:preview
80
+ <div style="display: flex;">
81
+ <zn-channel-tile
82
+ available
83
+ incoming
84
+ rejectable
85
+ fid="chat-5500"
86
+ title="Incoming chat"
87
+ subtitle="Auto-accepting…"
88
+ accept-uri="/queue/accept/chat-5500"
89
+ auto-accept-delay="15000">
90
+ </zn-channel-tile>
91
+ </div>
92
+
93
+ <script>
94
+ // Set the reservation window relative to now so the countdown animates.
95
+ const tile = document.currentScript.previousElementSibling.querySelector('zn-channel-tile');
96
+ tile.reservedUntil = Date.now() + 15000;
97
+ </script>
98
+ ```
99
+
100
+ ### Rejectable
101
+
102
+ Add `rejectable` to an incoming tile to offer a reject control. Pressing it emits `zn-reject` (and suppresses accept).
103
+
104
+ ```html:preview
105
+ <div style="display: flex;">
106
+ <zn-channel-tile
107
+ available
108
+ incoming
109
+ rejectable
110
+ fid="call-7700"
111
+ title="Incoming call"
112
+ subtitle="Reject to pass"
113
+ reject-icon="call_end"
114
+ reject-label="Reject call">
115
+ </zn-channel-tile>
116
+ </div>
117
+ ```
118
+
119
+ ### Colors
120
+
121
+ The `color` attribute sets the accent (the leading icon badge and the `--channel-tile-color` custom property). Available colors are: `default`, `primary`, `info`, `success`, `warning`, `error`, and `disabled`.
122
+
123
+ ```html:preview
124
+ <div style="display: flex; flex-wrap: wrap;">
125
+ <zn-channel-tile icon="chat" color="primary" title="Primary" subtitle="Accent example"></zn-channel-tile>
126
+ <zn-channel-tile icon="chat" color="info" title="Info" subtitle="Accent example"></zn-channel-tile>
127
+ <zn-channel-tile icon="chat" color="success" title="Success" subtitle="Accent example"></zn-channel-tile>
128
+ <zn-channel-tile icon="chat" color="warning" title="Warning" subtitle="Accent example"></zn-channel-tile>
129
+ <zn-channel-tile icon="chat" color="error" title="Error" subtitle="Accent example"></zn-channel-tile>
130
+ <zn-channel-tile icon="chat" color="disabled" title="Disabled" subtitle="Accent example"></zn-channel-tile>
131
+ </div>
132
+ ```
133
+
134
+ ### Custom Leading Content
135
+
136
+ Use the `leading` slot to replace the leading icon in the active state.
137
+
138
+ ```html:preview
139
+ <div style="display: flex;">
140
+ <zn-channel-tile header="Acme Support" title="Jane Cooper" subtitle="VIP customer">
141
+ <zn-icon slot="leading" library="avatar" src="Jane Cooper" size="36" round></zn-icon>
142
+ </zn-channel-tile>
143
+ </div>
144
+ ```
145
+
146
+ ### Custom Action
147
+
148
+ Use the `action` slot to replace the default accept button on an available tile — for example with a form.
149
+
150
+ ```html:preview
151
+ <div style="display: flex;">
152
+ <zn-channel-tile available subtitle="Pick a queue to join">
153
+ <zn-button slot="action" icon="login" icon-size="20" icon-button="round" color="success"></zn-button>
154
+ </zn-channel-tile>
155
+ </div>
156
+ ```
157
+
158
+ ### Footer
159
+
160
+ Use the `footer` slot for trailing content such as status badges in the active state.
161
+
162
+ ```html:preview
163
+ <div style="display: flex;">
164
+ <zn-channel-tile header="Acme Support" icon="chat" color="info" title="Jane Cooper" subtitle="Billing question">
165
+ <zn-chip slot="footer" type="warning">SLA</zn-chip>
166
+ </zn-channel-tile>
167
+ </div>
168
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.1.11",
3
+ "version": "1.1.13",
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",
@@ -24,6 +24,7 @@ $text-styles: (
24
24
  "h1": ("size": 16px, "line": 16px, "spacing": 18px, "weight": semibold, "color": default, "transform": none),
25
25
  "h2": ("size": 14px, "line": 16px, "spacing": 15px, "weight": semibold, "color": default, "transform": none),
26
26
  "h3": ("size": 11px, "line": 16px, "spacing": 15px, "weight": semibold, "color": default, "transform": none),
27
+ "heading-section": ("size": 14px, "line": 16px, "spacing": 15px, "weight": semibold, "color": default, "transform": none),
27
28
  "subheading": ("size": 14px, "line": 16px, "spacing": 15px, "weight": normal, "color": muted, "transform": none),
28
29
  "subheading-small": ("size": 13px, "line": 16px, "spacing": 15px, "weight": normal, "color": muted, "transform": none),
29
30
  "input-placeholder": ("size": 14px, "line": 24px, "spacing": 18px, "weight": normal, "color": muted, "transform": none),
@@ -319,6 +319,10 @@
319
319
  background: rgb(var(--zn-panel));
320
320
  }
321
321
 
322
+ &.button--icon-button-round:hover:not([disabled]), &.button--icon-button-round:active:not([disabled]) {
323
+ background: rgb(var(--zn-color-tab));
324
+ }
325
+
322
326
  &.button--plain:hover:not([disabled]), &.button--plain:active:not([disabled]) {
323
327
  background: transparent;
324
328
  }
@@ -0,0 +1,280 @@
1
+ import {classMap} from 'lit/directives/class-map.js';
2
+ import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
3
+ import {property} from 'lit/decorators.js';
4
+ import ZincElement from '../../internal/zinc-element';
5
+
6
+ import styles from './channel-tile.scss';
7
+
8
+ export type ChannelTileColor =
9
+ 'default' | 'primary' | 'info' | 'success' | 'warning' | 'error' | 'disabled';
10
+
11
+ /**
12
+ * @summary A channel/queue slot tile with two faces: an active (occupied) state
13
+ * showing an in-progress item, and an available (empty) state advertising
14
+ * capacity — optionally reserving an incoming item with an auto-accept countdown.
15
+ * @documentation https://zinc.style/components/channel-tile
16
+ * @status experimental
17
+ * @since 1.0
18
+ *
19
+ * @dependency zn-button
20
+ * @dependency zn-icon
21
+ *
22
+ * @event zn-accept - Emitted when an available tile is accepted (click or auto-accept).
23
+ * Cancelable — call `preventDefault()` to suppress the built-in `accept-uri` fetch.
24
+ * @event zn-reject - Emitted when the reject control is pressed.
25
+ *
26
+ * @slot leading - Replaces the leading icon in the active state.
27
+ * @slot action - Action content for the available state (e.g. a form). Falls back to a default accept button.
28
+ * @slot footer - Trailing content (e.g. status badges) in the active state.
29
+ *
30
+ * @csspart base - The component's base wrapper.
31
+ * @csspart progress - The progress indicator (incoming countdown overlay or active progress bar).
32
+ *
33
+ * @cssproperty --channel-tile-color - Resolved accent color (set from the `color` property).
34
+ */
35
+ export default class ZnChannelTile extends ZincElement {
36
+ static styles: CSSResultGroup = unsafeCSS(styles);
37
+
38
+ /** Renders the empty/available face instead of the active face. */
39
+ @property({type: Boolean, reflect: true}) available: boolean = false;
40
+
41
+ /** (Available only) the tile is reserving an incoming item awaiting acceptance. */
42
+ @property({type: Boolean, reflect: true}) incoming: boolean = false;
43
+
44
+ /** Free-form grouping/theming key (reflected so consumers can query/style by it). */
45
+ @property({reflect: true}) variant: string = '';
46
+
47
+ /** Top-bar text (e.g. brand). */
48
+ @property() header: string = '';
49
+
50
+ /** Leading icon (active state). */
51
+ @property() icon: string = '';
52
+
53
+ /** Accent color driving the leading icon and `--channel-tile-color`. */
54
+ @property({reflect: true}) color: ChannelTileColor = 'default';
55
+
56
+ /** Primary line. Defaults to "Available" in the available state when unset. */
57
+ @property() title: string = '';
58
+
59
+ /** Secondary line. */
60
+ @property() subtitle: string = '';
61
+
62
+ /** (Active only) progress bar fill, 0–100. */
63
+ @property({type: Number}) progress: number = 0;
64
+
65
+ /** (Active only) CSS color for the progress bar fill. */
66
+ @property({attribute: 'progress-color'}) progressColor: string = '';
67
+
68
+ /** Identifier carried in `zn-accept` / `zn-reject` event details. */
69
+ @property() fid: string = '';
70
+
71
+ /** (Available only) when set, accepting fetches this URI unless `zn-accept` is canceled. */
72
+ @property({attribute: 'accept-uri'}) acceptUri: string = '';
73
+
74
+ /** (Available/incoming) epoch (seconds or millis) at which the reservation window ends. */
75
+ @property({type: Number, attribute: 'reserved-until'}) reservedUntil: number = 0;
76
+
77
+ /** (Available/incoming) auto-accept window length in milliseconds. */
78
+ @property({type: Number, attribute: 'auto-accept-delay'}) autoAcceptDelay: number = 0;
79
+
80
+ /** (Available/incoming) whether a reject control is offered. */
81
+ @property({type: Boolean, reflect: true}) rejectable: boolean = false;
82
+
83
+ /** Icon for the built-in accept button. */
84
+ @property({attribute: 'accept-icon'}) acceptIcon: string = 'plus@lu';
85
+
86
+ /** Optional analytics id forwarded to the built-in accept button. */
87
+ @property({attribute: 'accept-gaid'}) acceptGaid: string = '';
88
+
89
+ /** Icon for the reject control. */
90
+ @property({attribute: 'reject-icon'}) rejectIcon: string = 'call_end';
91
+
92
+ /** Accessible label for the reject control. */
93
+ @property({attribute: 'reject-label'}) rejectLabel: string = 'Reject';
94
+
95
+ private _tickInterval: number | undefined;
96
+ private _autoAcceptFired: string = '';
97
+
98
+ protected firstUpdated(changed: PropertyValues) {
99
+ super.firstUpdated(changed);
100
+ this._startTicker();
101
+ }
102
+
103
+ disconnectedCallback() {
104
+ this._stopTicker();
105
+ super.disconnectedCallback();
106
+ }
107
+
108
+ protected updated(changed: PropertyValues) {
109
+ super.updated(changed);
110
+ if (changed.has('fid') && this.fid !== this._autoAcceptFired) {
111
+ this._autoAcceptFired = '';
112
+ }
113
+ }
114
+
115
+ private _isCountingDown(): boolean {
116
+ return this.available && this.incoming;
117
+ }
118
+
119
+ private _startTicker() {
120
+ if (this._tickInterval !== undefined) return;
121
+ this._tickInterval = window.setInterval(() => {
122
+ // Only the incoming countdown needs a self-driven re-render; active
123
+ // tiles are refreshed by the consumer setting `progress`/`color`.
124
+ if (!this._isCountingDown()) return;
125
+ this.requestUpdate();
126
+ this._maybeAutoAccept();
127
+ }, 200);
128
+ }
129
+
130
+ private _stopTicker() {
131
+ if (this._tickInterval !== undefined) {
132
+ clearInterval(this._tickInterval);
133
+ this._tickInterval = undefined;
134
+ }
135
+ }
136
+
137
+ private _maybeAutoAccept() {
138
+ if (!this._isCountingDown()) return;
139
+ if (!this.acceptUri) return;
140
+ if (!this.autoAcceptDelay || !this.reservedUntil) return;
141
+ if (this._autoAcceptFired === this.fid) return;
142
+
143
+ const until = this._reservedUntilMs();
144
+ if (!until || Date.now() < until) return;
145
+
146
+ this._autoAcceptFired = this.fid;
147
+ this._accept();
148
+ }
149
+
150
+ private _accept(): void {
151
+ const event = this.emit('zn-accept', {
152
+ cancelable: true,
153
+ detail: {fid: this.fid, acceptUri: this.acceptUri},
154
+ });
155
+ if (event.defaultPrevented || !this.acceptUri) return;
156
+ fetch(this.acceptUri, {credentials: 'same-origin'}).catch((err) => {
157
+ console.error('Error accepting interaction', err);
158
+ });
159
+ }
160
+
161
+ private _reservedUntilMs(): number {
162
+ if (!this.reservedUntil) return 0;
163
+ return this.reservedUntil.toString().length === 10
164
+ ? this.reservedUntil * 1000
165
+ : this.reservedUntil;
166
+ }
167
+
168
+ private _countdownPercent(): number {
169
+ const until = this._reservedUntilMs();
170
+ if (!until || !this.autoAcceptDelay) return 0;
171
+ const total = this.autoAcceptDelay;
172
+ const remaining = Math.max(0, until - Date.now());
173
+ const elapsed = Math.max(0, total - remaining);
174
+ return Math.min(100, Math.max(0, (elapsed / total) * 100));
175
+ }
176
+
177
+ private _handleReject = (e: MouseEvent) => {
178
+ e.preventDefault();
179
+ e.stopPropagation();
180
+ this.emit('zn-reject', {detail: {fid: this.fid, variant: this.variant}});
181
+ };
182
+
183
+ private _handleClick = (e: MouseEvent) => {
184
+ if (!this._isCountingDown() || !this.acceptUri) return;
185
+ if (e.defaultPrevented) return;
186
+ e.preventDefault();
187
+ e.stopPropagation();
188
+ this._accept();
189
+ };
190
+
191
+ protected render(): unknown {
192
+ const title = this.title || (this.available ? 'Available' : '');
193
+ const countdown = this._isCountingDown() ? this._countdownPercent() : null;
194
+
195
+ return html`
196
+ <div
197
+ part="base"
198
+ class=${classMap({
199
+ 'channel-tile': true,
200
+ 'channel-tile--available': this.available,
201
+ 'channel-tile--active': !this.available,
202
+ 'channel-tile--incoming': this.available && this.incoming,
203
+ [`channel-tile--${this.color}`]: true,
204
+ })}
205
+ role="button"
206
+ tabindex="0"
207
+ @click=${this._handleClick}>
208
+ <div class="channel-tile__header">${this.header ? html`<h3>${this.header}</h3>` : ''}</div>
209
+ ${countdown !== null
210
+ ? html`
211
+ <div part="progress" class="channel-tile__overlay" style="width: ${countdown}%"></div>`
212
+ : ''}
213
+ <div class="channel-tile__body">
214
+ ${this._renderLeading()}
215
+ <div class="channel-tile__content">
216
+ <h3 class="channel-tile__title">${title}</h3>
217
+ <p class="channel-tile__subtitle">${this.subtitle}</p>
218
+ </div>
219
+ <slot name="footer" class="channel-tile__footer"></slot>
220
+ </div>
221
+ ${!this.available
222
+ ? html`
223
+ <div part="progress" class="channel-tile__bar">
224
+ <div class="channel-tile__bar-fill"
225
+ style="width: ${this.progress}%; background-color: ${this.progressColor || 'transparent'};"></div>
226
+ </div>`
227
+ : ''}
228
+ </div>`;
229
+ }
230
+
231
+ private _renderLeading() {
232
+ if (this.available) {
233
+ return this._renderAvailableAction();
234
+ }
235
+
236
+ return html`
237
+ <slot name="leading">
238
+ ${this.icon
239
+ ? html`
240
+ <zn-button icon=${this.icon}
241
+ icon-size="20"
242
+ icon-button="round"
243
+ color=${this.color}
244
+ no-hover></zn-button>`
245
+ : ''}
246
+ </slot>`;
247
+ }
248
+
249
+ private _renderAvailableAction() {
250
+ if (this.incoming && this.rejectable) {
251
+ return html`
252
+ <button class="channel-tile__reject" type="button" aria-label=${this.rejectLabel}
253
+ @click=${this._handleReject}>
254
+ <zn-icon src=${this.rejectIcon} size="22"></zn-icon>
255
+ </button>`;
256
+ }
257
+
258
+ if (this.acceptUri) {
259
+ return html`
260
+ <zn-button href=${this.acceptUri}
261
+ icon=${this.acceptIcon}
262
+ icon-size="20"
263
+ icon-button="round"
264
+ gaid=${this.acceptGaid}
265
+ loading-text="Accepting"
266
+ no-hover>
267
+ </zn-button>`;
268
+ }
269
+
270
+ return html`
271
+ <slot name="action">
272
+ <zn-button icon=${this.acceptIcon}
273
+ icon-size="20"
274
+ icon-button="round"
275
+ gaid=${this.acceptGaid}
276
+ no-hover>
277
+ </zn-button>
278
+ </slot>`;
279
+ }
280
+ }
@@ -0,0 +1,197 @@
1
+ :host {
2
+ --channel-tile-color: var(--zn-color-border);
3
+
4
+ flex: 1 1 280px;
5
+ width: 280px;
6
+ min-width: 180px;
7
+ max-width: 320px;
8
+ display: block;
9
+ }
10
+
11
+ .channel-tile {
12
+ position: relative;
13
+ display: flex;
14
+ flex-direction: column;
15
+ height: 100%;
16
+ overflow: hidden;
17
+ cursor: pointer;
18
+ border-right: 1px solid rgb(var(--zn-border-color));
19
+ background-color: rgb(var(--zn-body));
20
+ transition: background-color 0.15s ease;
21
+ user-select: none;
22
+
23
+ &:focus-visible {
24
+ outline: 2px solid rgb(var(--zn-primary));
25
+ outline-offset: -2px;
26
+ }
27
+ }
28
+
29
+ :host(.active) .channel-tile {
30
+ background-color: rgb(var(--zn-panel));
31
+ cursor: default;
32
+ }
33
+
34
+ :host(:not(.active)) .channel-tile--active:hover {
35
+ background-color: rgb(var(--zn-panel));
36
+ }
37
+
38
+ .channel-tile--incoming {
39
+ background-color: rgba(var(--zn-color-success), 0.15);
40
+
41
+ &:hover {
42
+ background-color: rgba(var(--zn-color-success), 0.22);
43
+ }
44
+ }
45
+
46
+ .channel-tile--disabled .channel-tile__body {
47
+ opacity: 0.6;
48
+ }
49
+
50
+ .channel-tile__header {
51
+ display: flex;
52
+ align-items: center;
53
+ flex-shrink: 0;
54
+ height: 24px;
55
+ padding: 0 var(--zn-spacing-small);
56
+ background-color: rgb(var(--zn-color-tab));
57
+
58
+ h3 {
59
+ margin: 0;
60
+ font-size: var(--zn-text-h3-font-size);
61
+ line-height: var(--zn-text-h3-line-height);
62
+ font-weight: var(--zn-text-h3-font-weight);
63
+ color: rgb(var(--zn-text-heading));
64
+ text-overflow: ellipsis;
65
+ white-space: nowrap;
66
+ overflow: hidden;
67
+ }
68
+ }
69
+
70
+ .channel-tile__overlay {
71
+ position: absolute;
72
+ top: 0;
73
+ left: 0;
74
+ bottom: 0;
75
+ width: 0;
76
+ background-color: rgba(var(--zn-color-success), 0.55);
77
+ transition: width 0.2s linear;
78
+ pointer-events: none;
79
+ z-index: 0;
80
+ }
81
+
82
+ .channel-tile__body {
83
+ position: relative;
84
+ flex: 1;
85
+ display: flex;
86
+ align-items: center;
87
+ gap: var(--zn-spacing-small);
88
+ padding: var(--zn-spacing-x-small) var(--zn-spacing-small);
89
+ width: 100%;
90
+ overflow: hidden;
91
+ z-index: 1;
92
+ }
93
+
94
+ .channel-tile__content {
95
+ display: flex;
96
+ flex-direction: column;
97
+ flex-grow: 1;
98
+ gap: 4px;
99
+ overflow: hidden;
100
+
101
+ @container (max-width: 200px) {
102
+ display: none;
103
+ }
104
+ }
105
+
106
+ .channel-tile__title {
107
+ margin: 0;
108
+ font-size: var(--zn-text-heading-section-font-size);
109
+ line-height: var(--zn-text-heading-section-line-height);
110
+ font-weight: var(--zn-text-heading-section-font-weight);
111
+ color: var(--zn-text-heading-section-color);
112
+ text-transform: var(--zn-text-heading-section-transform);
113
+ text-overflow: ellipsis;
114
+ white-space: nowrap;
115
+ overflow: hidden;
116
+ }
117
+
118
+ .channel-tile__subtitle {
119
+ margin: 0;
120
+ font-size: var(--zn-text-subheading-small-font-size);
121
+ line-height: var(--zn-text-subheading-small-line-height);
122
+ font-weight: var(--zn-text-subheading-small-font-weight);
123
+ color: var(--zn-text-subheading-small-color);
124
+ text-transform: var(--zn-text-subheading-small-transform);
125
+ text-overflow: ellipsis;
126
+ white-space: nowrap;
127
+ overflow: hidden;
128
+ }
129
+
130
+ .channel-tile__footer {
131
+ display: flex;
132
+ align-items: center;
133
+ align-self: flex-start;
134
+ gap: var(--zn-spacing-small);
135
+ }
136
+
137
+ .channel-tile__reject {
138
+ flex-shrink: 0;
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ width: 36px;
143
+ height: 36px;
144
+ border-radius: 50%;
145
+ border: none;
146
+ padding: 0;
147
+ margin: 0;
148
+ background-color: rgb(var(--zn-panel));
149
+ color: rgb(var(--zn-color-error));
150
+ cursor: pointer;
151
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
152
+
153
+ &:hover {
154
+ background-color: rgba(var(--zn-color-error), 0.1);
155
+ }
156
+ }
157
+
158
+ .channel-tile__bar {
159
+ position: absolute;
160
+ bottom: 0;
161
+ left: 0;
162
+ right: 0;
163
+ height: 3px;
164
+ background-color: rgba(var(--zn-shadow), 0.2);
165
+ overflow: hidden;
166
+ z-index: 1;
167
+
168
+ &-fill {
169
+ height: 100%;
170
+ width: 0;
171
+ transition: width 0.5s linear, background-color 0.5s ease;
172
+ }
173
+ }
174
+
175
+ .channel-tile--primary {
176
+ --channel-tile-color: var(--zn-color-primary);
177
+ }
178
+
179
+ .channel-tile--info {
180
+ --channel-tile-color: var(--zn-color-info);
181
+ }
182
+
183
+ .channel-tile--success {
184
+ --channel-tile-color: var(--zn-color-success);
185
+ }
186
+
187
+ .channel-tile--warning {
188
+ --channel-tile-color: var(--zn-color-warning);
189
+ }
190
+
191
+ .channel-tile--error {
192
+ --channel-tile-color: var(--zn-color-error);
193
+ }
194
+
195
+ .channel-tile--disabled {
196
+ --channel-tile-color: var(--zn-color-disabled, 204, 204, 204);
197
+ }
@@ -0,0 +1,12 @@
1
+ import ZnChannelTile from './channel-tile.component';
2
+
3
+ export * from './channel-tile.component';
4
+ export default ZnChannelTile;
5
+
6
+ ZnChannelTile.define('zn-channel-tile');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-channel-tile': ZnChannelTile;
11
+ }
12
+ }
@@ -99,7 +99,7 @@
99
99
  display: flex;
100
100
  justify-content: flex-start;
101
101
  flex-direction: column;
102
- gap: 4px;
102
+ gap: var(--zn-spacing-x-small);
103
103
  min-height: 36px;
104
104
  flex-shrink: 1;
105
105
  max-width: 100%;