Skip to content

Commit 0e5138d

Browse files
committed
Add button to remove files
item supports inline for smaller sizes
1 parent 8b806c0 commit 0e5138d

File tree

6 files changed

+162
-42
lines changed

6 files changed

+162
-42
lines changed

docs/pages/components/file.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ layout: component
66
---
77

88
```html:preview
9-
<zn-file></zn-file>
9+
<zn-file multiple clearable></zn-file>
1010
```
1111

1212
## Examples

docs/pages/components/item.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,35 @@ layout: component
3737
</zn-item>
3838
```
3939

40+
### Icons
41+
42+
```html:preview
43+
44+
<zn-item caption="Full name" icon="person">
45+
This is the content
46+
</zn-item>
47+
48+
<zn-cols layout="1, 1">
49+
<zn-item inline icon="person">This is the content</zn-item>
50+
<zn-item inline icon="person">This is the content</zn-item>
51+
</zn-cols>
52+
```
53+
4054
### Multiple Actions
4155

4256
Example of multiple actions in a description item.
4357

4458
```html:preview
4559
4660
<zn-item caption="Address">
47-
<p><strong>Company</strong> Example here </p>
48-
<p><strong>Line 1</strong> Example here </p>
49-
<p><strong>Line 2</strong> Example here </p>
50-
<p><strong>Line 3</strong> Example here </p>
51-
<p><strong>Town</strong> Example here </p>
52-
<p><strong>County</strong> Example here </p>
53-
<p><strong>Postal Code</strong> Example here </p>
54-
<p><strong>Country</strong> Example here </p>
61+
<div><strong>Company</strong> Example here </div>
62+
<div><strong>Line 1</strong> Example here </div>
63+
<div><strong>Line 2</strong> Example here </div>
64+
<div><strong>Line 3</strong> Example here </div>
65+
<div><strong>Town</strong> Example here </div>
66+
<div><strong>County</strong> Example here </div>
67+
<div><strong>Postal Code</strong> Example here </div>
68+
<div><strong>Country</strong> Example here </div>
5569
5670
<zn-button id="address-modal" slot="actions" size="x-small" color="secondary" modal>Edit</zn-button>
5771
<zn-button id="address-modal" slot="actions" size="x-small" color="secondary" modal>Edit</zn-button>

src/components/file/file.component.ts

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {HasSlotController} from "../../internal/slot";
88
import {ifDefined} from "lit/directives/if-defined.js";
99
import {LocalizeController} from "../../utilities/localize";
1010
import {property, query, state} from 'lit/decorators.js';
11+
import {repeat} from 'lit/directives/repeat.js';
1112
import {watch} from "../../internal/watch";
1213
import ZincElement, {type ZincFormControl} from '../../internal/zinc-element';
1314
import type ZnButton from "../button";
@@ -142,6 +143,9 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
142143
/** The file control's label. If you need to display HTML, use the `label` slot instead. */
143144
@property() label = '';
144145

146+
/** If this is set, then the only way to remove files is to click the cross next to them. */
147+
@property({type: Boolean}) clearable = false;
148+
145149
/**
146150
* The file control's help text.
147151
* If you need to display HTML, use the `help-text` slot instead.
@@ -413,30 +417,60 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
413417
this.emit('zn-blur');
414418
}
415419

416-
private renderValue(): HTMLTemplateResult {
417-
let hasFiles = false;
418-
// @ts-expect-error variadic function
419-
let fileChosenLabel = this.localize.term('numFilesSelected', 0, this.webkitdirectory);
420-
421-
if (this.files && this.files?.length > 0) {
422-
hasFiles = true;
423-
fileChosenLabel = this.files.length === 1
424-
? this.files[0].name
425-
// @ts-expect-error variadic function
426-
: this.localize.term('numFilesSelected', this.files.length, this.webkitdirectory);
420+
/**
421+
* Remove a file from the list of files
422+
*/
423+
private removeFile = (e: Event) => {
424+
e.preventDefault();
425+
e.stopPropagation();
426+
427+
const files = Array.from(this.files || []);
428+
const file = (e.target as ZnButton).parentElement?.getAttribute('data-file-name');
429+
430+
const index = files.findIndex(f => f.name === file);
431+
432+
if (index !== -1) {
433+
files.splice(index, 1);
427434
}
428435

436+
const dataTransfer = new DataTransfer();
437+
files.forEach(f => dataTransfer.items.add(f));
438+
this.files = dataTransfer.files;
439+
this.input.dispatchEvent(new Event('change'));
440+
}
441+
442+
private renderFileValueWithDelete(): HTMLTemplateResult {
443+
// files as an array
444+
const files = Array.from(this.files || []);
445+
const isClearable = this.clearable;
446+
447+
// @ts-expect-error variadic
448+
const fileChosenLabel = this.localize.term('numFilesSelected', 0, this.webkitdirectory);
429449
return html`
430-
<span
431-
class=${classMap({
432-
input__value: true,
433-
'input__value--hidden': this.hideValue,
434-
'input__value--placeholder': !hasFiles,
435-
})}
436-
part="value">
437-
${fileChosenLabel}
438-
</span>
439-
`;
450+
${files ? html`
451+
<div class="file_wrapper">
452+
${repeat(files, (file: File) => html`
453+
<zn-chip
454+
class=${classMap({
455+
'input__value': true,
456+
'input__value--hidden': this.hideValue,
457+
})}
458+
data-file-name="${file.name}"
459+
part="value">
460+
${file.name}
461+
${isClearable ? html`
462+
<zn-button
463+
slot="action"
464+
class="input__delete"
465+
color="transparent"
466+
icon="close"
467+
size="content"
468+
icon-size="18"
469+
@click="${this.removeFile}"
470+
part="delete"></zn-button>` : ''}
471+
</zn-chip>
472+
`)}
473+
</div>` : html`${fileChosenLabel}`}`;
440474
}
441475

442476
private renderDroparea(): HTMLTemplateResult {
@@ -457,11 +491,10 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
457491
<zn-icon src="upload_file"></zn-icon>
458492
</slot>
459493
</span>
460-
<p
461-
class="droparea__text"
462-
part="droparea-value">
494+
<p class="droparea__text"
495+
part="droparea-value">
463496
<strong>${this.localize.term(this.webkitdirectory ? 'folderDragDrop' : 'fileDragDrop')}</strong>
464-
${this.renderValue()}
497+
${this.renderFileValueWithDelete()}
465498
</p>
466499
</div>
467500
</div>
@@ -491,7 +524,7 @@ export default class ZnFile extends ZincElement implements ZincFormControl {
491524
outline>
492525
${buttonText}
493526
</zn-button>
494-
${this.renderValue()}
527+
${this.renderFileValueWithDelete()}
495528
</div>`;
496529
}
497530

src/components/file/file.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
* The value of the chosen file(s) or the placeholder text
5252
*/
5353
.input__value {
54+
display: inline-flex;
55+
justify-content: center;
56+
align-items: center;
57+
gap: var(--zn-spacing-small);
5458
color: var(--zn-input-color);
5559
font-size: var(--zn-input-label-font-size-medium);
5660
}
@@ -59,6 +63,16 @@
5963
display: none;
6064
}
6165

66+
.input__delete {
67+
cursor: pointer;
68+
color: var(--zn-input-color);
69+
font-size: var(--zn-input-label-font-size-medium);
70+
71+
&:hover {
72+
color: var(--zn-color-primary-600);
73+
}
74+
}
75+
6276
.input__value--placeholder {
6377
color: var(--zn-input-help-text-color);
6478
}
@@ -121,6 +135,12 @@
121135
font-size: var(--zn-spacing-3x-large);
122136
}
123137

138+
.file-wrapper {
139+
display: flex;
140+
flex-direction: row;
141+
flex-wrap: wrap;
142+
}
143+
124144
.droparea__text {
125145
display: flex;
126146
flex-direction: column;

src/components/item/item.component.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export default class ZnItem extends ZincElement {
3939

4040
@property() icon: string;
4141

42+
@property({type: Boolean}) inline: boolean;
43+
4244
connectedCallback() {
4345
super.connectedCallback();
4446
this.setAttribute('role', 'listitem');
@@ -59,15 +61,21 @@ export default class ZnItem extends ZincElement {
5961
'description-item': true,
6062
'description-item--stacked': this.stacked,
6163
'description-item--edit-on-hover': this.editOnHover,
64+
'description-item--inline': this.inline,
6265
'description-item--small': this.size === 'small',
6366
'description-item--medium': this.size === 'medium',
6467
'description-item--large': this.size === 'large',
6568
})}>
66-
<div class="description-item__icon">
67-
${this.icon ? html`
68-
<zn-icon src="${this.icon}" size="20"></zn-icon>` : ''}
69-
</div>
70-
<div class="description-item__caption">${this.caption}</div>
69+
70+
${this.icon ? html`
71+
<div class="description-item__header">
72+
<div class="description-item__icon">
73+
<zn-icon src="${this.icon}" size="20"></zn-icon>
74+
</div>
75+
<div class="description-item__caption">${this.caption}</div>
76+
</div>` : html`
77+
<div class="description-item__caption">${this.caption}</div>`}
78+
7179
<div class="description-item__content">
7280
<div class="description-item__content-inner">
7381
<slot></slot>

src/components/item/item.scss

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@
3434
color: rgb(var(--zn-text-heading));
3535
}
3636

37+
38+
&__header {
39+
display: flex;
40+
max-width: var(--zn-item-label-max-width);
41+
width: 100%;
42+
43+
.description-item__caption {
44+
width: unset;
45+
max-width: unset;
46+
}
47+
}
48+
3749
&__content {
3850
width: 100%;
3951
display: flex;
@@ -53,6 +65,10 @@
5365
max-width: var(--zn-item-label-max-width);
5466
width: 100%;
5567
flex-shrink: 0;
68+
69+
@include wc.container-query(null, sm) {
70+
line-height: var(--zn-line-height-normal);
71+
}
5672
}
5773

5874
&__action-wrapper {
@@ -69,12 +85,16 @@
6985
// Sizes
7086
&--small {
7187
&.description-item {
72-
padding: var(--zn-spacing-small);
88+
padding: var(--zn-spacing-x-small);
7389
}
7490

75-
.description-item__caption {
91+
.description-item__caption, .description-item__content-inner {
7692
font-size: var(--zn-font-size-small);
7793
}
94+
95+
@include wc.container-query(null, sm) {
96+
gap: var(--zn-spacing-2x-small);
97+
}
7898
}
7999

80100
&--stacked {
@@ -102,4 +122,29 @@
102122
}
103123
}
104124
}
125+
126+
&--inline {
127+
128+
gap: var(--zn-spacing-small);
129+
130+
.description-item__header {
131+
width: unset;
132+
max-width: unset;
133+
}
134+
135+
.description-item__icon {
136+
margin-right: 0;
137+
}
138+
139+
.description-item__caption {
140+
width: unset;
141+
max-width: unset;
142+
}
143+
144+
@include wc.container-query(null, sm) {
145+
flex-direction: row;
146+
align-items: center;
147+
gap: var(--zn-spacing-small);
148+
}
149+
}
105150
}

0 commit comments

Comments
 (0)