@kubex/zinc 1.1.15 → 1.1.17
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 +96 -76
- package/dist/vscode.html-custom-data.json +11 -11
- package/dist/web-types.json +24 -24
- package/dist/zn.d.ts +7 -0
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +105 -100
- package/package.json +1 -1
- package/scss/shared/_table.scss +0 -1
- package/src/components/chat-message/chat-message.component.ts +24 -2
- package/src/components/chat-message/chat-message.scss +4 -10
- package/src/components/chat-message-attachment/chat-message-attachment.component.ts +6 -8
- package/src/components/chat-message-attachment/chat-message-attachment.test.ts +11 -0
- package/src/components/data-table/data-table.component.ts +2 -2
- package/src/components/data-table/data-table.scss +0 -1
- package/src/components/editor/editor.component.ts +11 -1
- package/src/components/editor/modules/attachment/attachment.ts +29 -18
- package/src/components/input/input.scss +4 -4
- package/src/components/select/select.scss +3 -3
- package/src/components/toggle/toggle.scss +2 -2
- package/src/form-control.scss +2 -2
package/package.json
CHANGED
package/scss/shared/_table.scss
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {cleanHTML} from "./clean-message";
|
|
2
2
|
import {type CSSResultGroup, html, nothing, unsafeCSS} from 'lit';
|
|
3
|
-
import {property} from "lit/decorators.js";
|
|
3
|
+
import {property, state} from "lit/decorators.js";
|
|
4
4
|
import {unsafeHTML} from "lit/directives/unsafe-html.js";
|
|
5
5
|
import ZincElement from '../../internal/zinc-element';
|
|
6
6
|
import ZnIcon from "../icon";
|
|
@@ -80,6 +80,9 @@ export default class ZnChatMessage extends ZincElement {
|
|
|
80
80
|
/** Marks the message as initiated by an agent (affects styling and grouping). */
|
|
81
81
|
@property({type: Boolean, reflect: true, attribute: 'agent-initiated'}) agentInitiated = false;
|
|
82
82
|
|
|
83
|
+
/** Whether any element is assigned to the `attachments` slot — drives the attachments row visibility. */
|
|
84
|
+
@state() private hasAttachments = false;
|
|
85
|
+
|
|
83
86
|
connectedCallback() {
|
|
84
87
|
const isContent = !this.actionType || this.actionType === 'internal';
|
|
85
88
|
const agentInitiated = isContent && this.agentInitiated;
|
|
@@ -122,7 +125,12 @@ export default class ZnChatMessage extends ZincElement {
|
|
|
122
125
|
<div part="bubble" class="message__bubble ${sending ? 'message__bubble--sending' : ''}">
|
|
123
126
|
<div class="message__main">
|
|
124
127
|
<div part="content" class="message__content">${this.renderContent()}</div>
|
|
125
|
-
<slot
|
|
128
|
+
<slot
|
|
129
|
+
name="attachments"
|
|
130
|
+
part="attachments"
|
|
131
|
+
class="message__attachments ${this.hasAttachments ? 'message__attachments--visible' : ''}"
|
|
132
|
+
@slotchange=${this.handleAttachmentsSlotChange}
|
|
133
|
+
></slot>
|
|
126
134
|
</div>
|
|
127
135
|
${sending ? html`
|
|
128
136
|
<div class="message__meta">Sending...</div>` : html`
|
|
@@ -136,6 +144,20 @@ export default class ZnChatMessage extends ZincElement {
|
|
|
136
144
|
`;
|
|
137
145
|
}
|
|
138
146
|
|
|
147
|
+
protected firstUpdated() {
|
|
148
|
+
this.syncHasAttachments();
|
|
149
|
+
Promise.resolve().then(() => this.syncHasAttachments());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private handleAttachmentsSlotChange() {
|
|
153
|
+
this.syncHasAttachments();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private syncHasAttachments() {
|
|
157
|
+
const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name="attachments"]');
|
|
158
|
+
this.hasAttachments = !!slot && slot.assignedElements().length > 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
139
161
|
private isSystemMessage() {
|
|
140
162
|
return ZnChatMessage.SYSTEM_ACTION_TYPES.includes(this.actionType);
|
|
141
163
|
}
|
|
@@ -65,15 +65,16 @@
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
&__attachments {
|
|
68
|
-
// Always rendered so projection is reliable regardless of when children are
|
|
69
|
-
// parsed/added, but kept out of flow (no phantom gap below the bubble) until
|
|
70
|
-
// an attachment actually exists — toggled on via :host(:has(...)) below.
|
|
71
68
|
display: none;
|
|
72
69
|
flex-direction: row;
|
|
73
70
|
flex-wrap: wrap;
|
|
74
71
|
align-items: center;
|
|
75
72
|
gap: var(--zn-spacing-small);
|
|
76
73
|
|
|
74
|
+
&--visible {
|
|
75
|
+
display: flex;
|
|
76
|
+
}
|
|
77
|
+
|
|
77
78
|
::slotted(zn-chat-message-attachment) {
|
|
78
79
|
min-width: 0;
|
|
79
80
|
}
|
|
@@ -168,13 +169,6 @@
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
// Reveal the attachments row only when an attachment is actually present. Using
|
|
172
|
-
// :has on the host keeps this live and parse-order independent — no reliance on
|
|
173
|
-
// conditional rendering that could miss a child added during parsing.
|
|
174
|
-
:host(:has([slot="attachments"])) .message__attachments {
|
|
175
|
-
display: flex;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
172
|
// Bubble tints by message origin / type.
|
|
179
173
|
:host([agent-initiated]) .message__bubble {
|
|
180
174
|
background: var(--message-background, rgba(var(--zn-color-info), 0.1));
|
|
@@ -35,7 +35,7 @@ export default class ZnChatMessageAttachment extends ZincElement {
|
|
|
35
35
|
@property() name: string = '';
|
|
36
36
|
|
|
37
37
|
/** The leading icon name. */
|
|
38
|
-
@property() icon: string = '
|
|
38
|
+
@property() icon: string = 'paperclip@lu';
|
|
39
39
|
|
|
40
40
|
/** Where to open the link. Defaults to a new tab. */
|
|
41
41
|
@property() target: string = '_blank';
|
|
@@ -46,13 +46,11 @@ export default class ZnChatMessageAttachment extends ZincElement {
|
|
|
46
46
|
connectedCallback() {
|
|
47
47
|
super.connectedCallback();
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (!this.closest('zn-chat-message')) {
|
|
49
|
+
if (this.closest('zn-chat-message')) {
|
|
50
|
+
if (!this.slot) {
|
|
51
|
+
this.slot = 'attachments';
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
56
54
|
console.warn('<zn-chat-message-attachment> can only be used inside a <zn-chat-message>.', this);
|
|
57
55
|
}
|
|
58
56
|
}
|
|
@@ -22,6 +22,17 @@ describe('<zn-chat-message-attachment>', () => {
|
|
|
22
22
|
expect(attachment?.getAttribute('slot')).to.equal('attachments');
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
it('should reveal the attachments row when an attachment is slotted in', async () => {
|
|
26
|
+
const el = await fixture<HTMLElement>(html`
|
|
27
|
+
<zn-chat-message message="Test">
|
|
28
|
+
<zn-chat-message-attachment href="#" name="file.pdf"></zn-chat-message-attachment>
|
|
29
|
+
</zn-chat-message>`);
|
|
30
|
+
|
|
31
|
+
const row = el.shadowRoot!.querySelector('.message__attachments') as HTMLElement;
|
|
32
|
+
// The row is display:none until something is slotted in; a slotted attachment must reveal it.
|
|
33
|
+
expect(getComputedStyle(row).display).to.not.equal('none');
|
|
34
|
+
});
|
|
35
|
+
|
|
25
36
|
it('should render the name in the link', async () => {
|
|
26
37
|
const el = await fixture<HTMLElement>(html`
|
|
27
38
|
<zn-chat-message>
|
|
@@ -1339,9 +1339,9 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1339
1339
|
<td class="table__cell table__cell--actions">
|
|
1340
1340
|
<zn-dropdown placement="bottom-end">
|
|
1341
1341
|
<zn-button slot="trigger"
|
|
1342
|
-
icon-button
|
|
1343
|
-
plain
|
|
1342
|
+
icon-button="small"
|
|
1344
1343
|
icon="ellipsis@lu"
|
|
1344
|
+
plain
|
|
1345
1345
|
aria-label="Row actions">
|
|
1346
1346
|
</zn-button>
|
|
1347
1347
|
<zn-menu>
|
|
@@ -261,6 +261,13 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
|
|
|
261
261
|
|
|
262
262
|
this.quillElement = quill;
|
|
263
263
|
|
|
264
|
+
this.getForm()?.addEventListener('submit', () => {
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
const attachmentModule = this.quillElement.getModule('attachment') as Attachment;
|
|
267
|
+
attachmentModule?.reset();
|
|
268
|
+
}, 0);
|
|
269
|
+
});
|
|
270
|
+
|
|
264
271
|
// @ts-expect-error getSelection is available it lies.
|
|
265
272
|
const hasShadowRootSelection = !!(document.createElement('div').attachShadow({mode: 'open'}).getSelection);
|
|
266
273
|
// Each browser engine has a different implementation for retrieving the Range
|
|
@@ -400,7 +407,10 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
|
|
|
400
407
|
|
|
401
408
|
if (this.interactionType === 'chat') {
|
|
402
409
|
const form = this.closest('form');
|
|
403
|
-
|
|
410
|
+
const hasText = !!this.value && this.value.trim().length > 0 && !empty(this.value);
|
|
411
|
+
const attachmentInput = form?.querySelector('input[name="attachments"]') as HTMLInputElement | null;
|
|
412
|
+
const hasAttachments = !!attachmentInput?.value && attachmentInput.value !== '[]';
|
|
413
|
+
if (form && (hasText || hasAttachments)) {
|
|
404
414
|
this.emit('zn-submit', {detail: {value: this.value, element: this}});
|
|
405
415
|
form.requestSubmit();
|
|
406
416
|
this.quillElement.setText('');
|
|
@@ -57,7 +57,7 @@ export default class Attachment {
|
|
|
57
57
|
public addAttachment(file: File, dataUrl?: string) {
|
|
58
58
|
const attachmentId = generateId();
|
|
59
59
|
const insertWithDataUrl = (base64: string) => {
|
|
60
|
-
this._insertAttachment({
|
|
60
|
+
this._insertAttachment({dataUrl: base64, file, id: attachmentId});
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
if (dataUrl) {
|
|
@@ -72,8 +72,8 @@ export default class Attachment {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (typeof this._options.upload === 'function') {
|
|
75
|
-
this._options.upload(file).then(({
|
|
76
|
-
this._uploadAttachment(file, url);
|
|
75
|
+
this._options.upload(file).then(async ({path, url}: { path: string; url: string }) => {
|
|
76
|
+
await this._uploadAttachment(file, url);
|
|
77
77
|
this._updateAttachment(attachmentId, url, path);
|
|
78
78
|
}).catch((err: { message: string }) => {
|
|
79
79
|
console.warn(err.message);
|
|
@@ -163,6 +163,15 @@ export default class Attachment {
|
|
|
163
163
|
return attachment;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
/** Clear every staged attachment preview and reset the backing form input. */
|
|
167
|
+
public reset() {
|
|
168
|
+
this._attachmentContainer.replaceChildren();
|
|
169
|
+
const attachments = this._options.attachmentInput;
|
|
170
|
+
if (attachments) {
|
|
171
|
+
attachments.value = '';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
166
175
|
private _removeAttachment(id: string) {
|
|
167
176
|
const attachment = this._quill.container.querySelector(`#${id}`);
|
|
168
177
|
if (attachment) {
|
|
@@ -184,24 +193,26 @@ export default class Attachment {
|
|
|
184
193
|
}
|
|
185
194
|
}
|
|
186
195
|
|
|
187
|
-
private _uploadAttachment(file: File, url: string) {
|
|
188
|
-
|
|
196
|
+
private _uploadAttachment(file: File, url: string): Promise<void> {
|
|
197
|
+
return new Promise<void>((resolve, reject) => {
|
|
198
|
+
const xhr = new XMLHttpRequest();
|
|
189
199
|
|
|
190
|
-
|
|
191
|
-
|
|
200
|
+
xhr.open('PUT', url, true);
|
|
201
|
+
xhr.setRequestHeader('Content-Type', file.type);
|
|
192
202
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
xhr.onload = () => {
|
|
204
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
205
|
+
resolve();
|
|
206
|
+
} else {
|
|
207
|
+
reject(new Error(`[Quill Attachment Module] Failed to upload file. Status: ${xhr.status}`));
|
|
208
|
+
}
|
|
209
|
+
};
|
|
200
210
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
xhr.onerror = () => {
|
|
212
|
+
reject(new Error(`[Quill Attachment Module] Error occurred while uploading file.`));
|
|
213
|
+
};
|
|
204
214
|
|
|
205
|
-
|
|
215
|
+
xhr.send(file);
|
|
216
|
+
});
|
|
206
217
|
}
|
|
207
218
|
}
|
|
@@ -167,7 +167,7 @@ input[type='text'].input__control[data-color-input] {
|
|
|
167
167
|
|
|
168
168
|
.input--x-small .input__control {
|
|
169
169
|
height: calc(var(--zn-input-height-x-small) - var(--zn-input-border-width) * 2);
|
|
170
|
-
padding: 0 var(--zn-input-spacing-small);
|
|
170
|
+
padding: 0 var(--zn-input-spacing-small) 0 var(--zn-spacing-small);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
.input--x-small .input__clear,
|
|
@@ -201,7 +201,7 @@ input[type='text'].input__control[data-color-input] {
|
|
|
201
201
|
|
|
202
202
|
.input--small .input__control {
|
|
203
203
|
height: calc(var(--zn-input-height-small) - var(--zn-input-border-width) * 2);
|
|
204
|
-
padding: 0 var(--zn-input-spacing-small);
|
|
204
|
+
padding: 0 var(--zn-input-spacing-small) 0 var(--zn-spacing-small);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
.input--small .input__clear,
|
|
@@ -235,7 +235,7 @@ input[type='text'].input__control[data-color-input] {
|
|
|
235
235
|
|
|
236
236
|
.input--medium .input__control {
|
|
237
237
|
height: calc(var(--zn-input-height-medium) - var(--zn-input-border-width) * 2);
|
|
238
|
-
padding: 0 var(--zn-input-spacing-small);
|
|
238
|
+
padding: 0 var(--zn-input-spacing-small) 0 var(--zn-spacing-small);
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
.input--medium .input__clear,
|
|
@@ -283,7 +283,7 @@ input[type='text'].input__control[data-color-input] {
|
|
|
283
283
|
|
|
284
284
|
.input--large .input__control {
|
|
285
285
|
height: calc(var(--zn-input-height-large) - var(--zn-input-border-width) * 2);
|
|
286
|
-
padding: 0 var(--zn-input-spacing-medium);
|
|
286
|
+
padding: 0 var(--zn-input-spacing-medium) 0 var(--zn-spacing-small);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
.input--large .input__clear,
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
font-size: var(--zn-input-font-size-small);
|
|
209
209
|
min-height: var(--zn-input-height-small);
|
|
210
210
|
padding-block: 0;
|
|
211
|
-
padding-inline: var(--zn-
|
|
211
|
+
padding-inline: var(--zn-spacing-small);
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
.select--small .select__clear {
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
font-size: var(--zn-input-font-size-medium);
|
|
234
234
|
min-height: var(--zn-input-height-medium);
|
|
235
235
|
padding-block: 0;
|
|
236
|
-
padding-inline: var(--zn-
|
|
236
|
+
padding-inline: var(--zn-spacing-small);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
.select--has-input-prefix.select--medium .select__combobox {
|
|
@@ -262,7 +262,7 @@
|
|
|
262
262
|
font-size: var(--zn-input-font-size-large);
|
|
263
263
|
min-height: var(--zn-input-height-large);
|
|
264
264
|
padding-block: 0;
|
|
265
|
-
padding-inline: var(--zn-
|
|
265
|
+
padding-inline: var(--zn-spacing-small);
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
.select--has-input-prefix.select--large .select__combobox {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
:host([checked]) .switch__input-wrapper {
|
|
33
|
-
background-color:
|
|
34
|
-
border-color:
|
|
33
|
+
background-color: rgb(var(--zn-color-primary));
|
|
34
|
+
border-color: rgb(var(--zn-color-primary));
|
|
35
35
|
|
|
36
36
|
.switch__control {
|
|
37
37
|
transform: translate(16px, -50%);
|
package/src/form-control.scss
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.form-control-input {
|
|
2
|
-
margin: var(--zn-spacing-2x-small) 3px 3px;
|
|
2
|
+
margin: var(--zn-spacing-2x-small) 3px 3px 0;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
.form-control-input--flush {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
color: var(--zn-input-label-color);
|
|
21
21
|
font-weight: var(--zn-input-label-font-weight);
|
|
22
22
|
line-height: var(--zn-input-label-line-height);
|
|
23
|
-
margin-bottom: var(--zn-spacing-
|
|
23
|
+
margin-bottom: var(--zn-spacing-x-small);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
.form-control--has-label.form-control--medium .form-control__label {
|