@umbraco-ui/uui-input-file 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 uui-app
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # uui-input-file
2
+
3
+ ![npm](https://img.shields.io/npm/v/@umbraco-ui/uui-input-file?logoColor=%231B264F)
4
+
5
+ Umbraco style input-file component.
6
+
7
+ ## Installation
8
+
9
+ ### ES imports
10
+
11
+ ```zsh
12
+ npm i @umbraco-ui/uui-input-file
13
+ ```
14
+
15
+ Import the registration of `<uui-input-file>` via:
16
+
17
+ ```javascript
18
+ import '@umbraco-ui/uui-input-file';
19
+ ```
20
+
21
+ When looking to leverage the `UUIInputFileElement` base class as a type and/or for extension purposes, do so via:
22
+
23
+ ```javascript
24
+ import { UUIInputFileElement } from '@umbraco-ui/uui-input-file';
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```html
30
+ <uui-input-file></uui-input-file>
31
+ ```
@@ -0,0 +1,148 @@
1
+ {
2
+ "version": "experimental",
3
+ "tags": [
4
+ {
5
+ "name": "uui-input-file",
6
+ "path": "./lib/uui-input-file.element.ts",
7
+ "attributes": [
8
+ {
9
+ "name": "accept",
10
+ "description": "Accepted filetypes. Will allow all types if empty.",
11
+ "type": "string",
12
+ "default": "\"false\""
13
+ },
14
+ {
15
+ "name": "multiple",
16
+ "description": "Allows for multiple files to be selected.",
17
+ "type": "boolean",
18
+ "default": "\"false\""
19
+ },
20
+ {
21
+ "name": "name",
22
+ "description": "This is a name property of the component.",
23
+ "type": "string",
24
+ "default": "\"''\""
25
+ },
26
+ {
27
+ "name": "value",
28
+ "description": "Value of this form control.",
29
+ "type": "string",
30
+ "default": "\"''\""
31
+ },
32
+ {
33
+ "name": "pristine",
34
+ "description": "Determines wether the form control has been touched or interacted with, this determines wether the validation-status of this form control should be made visible.",
35
+ "type": "boolean",
36
+ "default": "\"false\""
37
+ },
38
+ {
39
+ "name": "required",
40
+ "description": "Apply validation rule for requiring a value of this form control.",
41
+ "type": "boolean",
42
+ "default": "\"false\""
43
+ },
44
+ {
45
+ "name": "required-message",
46
+ "description": "Required validation message.",
47
+ "type": "string",
48
+ "default": "\"This field is required\""
49
+ },
50
+ {
51
+ "name": "error",
52
+ "description": "Apply custom error on this input.",
53
+ "type": "boolean",
54
+ "default": "false"
55
+ },
56
+ {
57
+ "name": "error-message",
58
+ "description": "Custom error message.",
59
+ "type": "string",
60
+ "default": "\"This field is invalid\""
61
+ }
62
+ ],
63
+ "properties": [
64
+ {
65
+ "name": "styles",
66
+ "type": "CSSResult[]",
67
+ "default": "[null]"
68
+ },
69
+ {
70
+ "name": "accept",
71
+ "attribute": "accept",
72
+ "description": "Accepted filetypes. Will allow all types if empty.",
73
+ "type": "string",
74
+ "default": "\"false\""
75
+ },
76
+ {
77
+ "name": "multiple",
78
+ "attribute": "multiple",
79
+ "description": "Allows for multiple files to be selected.",
80
+ "type": "boolean",
81
+ "default": "\"false\""
82
+ },
83
+ {
84
+ "name": "formAssociated",
85
+ "description": "This is a static class field indicating that the element is can be used inside a native form and participate in its events.\nIt may require a polyfill, check support here https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals.\nRead more about form controls here https://web.dev/more-capable-form-controls/",
86
+ "type": "boolean",
87
+ "default": "true"
88
+ },
89
+ {
90
+ "name": "name",
91
+ "attribute": "name",
92
+ "description": "This is a name property of the component.",
93
+ "type": "string",
94
+ "default": "\"''\""
95
+ },
96
+ {
97
+ "name": "value",
98
+ "attribute": "value",
99
+ "description": "Value of this form control.",
100
+ "type": "string",
101
+ "default": "\"''\""
102
+ },
103
+ {
104
+ "name": "pristine",
105
+ "attribute": "pristine",
106
+ "description": "Determines wether the form control has been touched or interacted with, this determines wether the validation-status of this form control should be made visible.",
107
+ "type": "boolean",
108
+ "default": "\"false\""
109
+ },
110
+ {
111
+ "name": "required",
112
+ "attribute": "required",
113
+ "description": "Apply validation rule for requiring a value of this form control.",
114
+ "type": "boolean",
115
+ "default": "\"false\""
116
+ },
117
+ {
118
+ "name": "requiredMessage",
119
+ "attribute": "required-message",
120
+ "description": "Required validation message.",
121
+ "type": "string",
122
+ "default": "\"This field is required\""
123
+ },
124
+ {
125
+ "name": "error",
126
+ "attribute": "error",
127
+ "description": "Apply custom error on this input.",
128
+ "type": "boolean",
129
+ "default": "false"
130
+ },
131
+ {
132
+ "name": "errorMessage",
133
+ "attribute": "error-message",
134
+ "description": "Custom error message.",
135
+ "type": "string",
136
+ "default": "\"This field is invalid\""
137
+ },
138
+ {
139
+ "name": "validity",
140
+ "type": "ValidityState"
141
+ },
142
+ {
143
+ "name": "validationMessage"
144
+ }
145
+ ]
146
+ }
147
+ ]
148
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './uui-input-file.element';
package/lib/index.js ADDED
@@ -0,0 +1,236 @@
1
+ import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
2
+ import { query, property, state } from 'lit/decorators.js';
3
+ import { LitElement, html, css } from 'lit';
4
+ import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
5
+ import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils';
6
+ import { iconDelete } from '@umbraco-ui/uui-icon-registry-essential/lib/svgs';
7
+ import { repeat } from 'lit/directives/repeat.js';
8
+
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __decorateClass = (decorators, target, key, kind) => {
12
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
13
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
14
+ if (decorator = decorators[i])
15
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
16
+ if (kind && result)
17
+ __defProp(target, key, result);
18
+ return result;
19
+ };
20
+ let UUIInputFileElement = class extends FormControlMixin(LitElement) {
21
+ constructor() {
22
+ super();
23
+ this.accept = "";
24
+ this.multiple = false;
25
+ this._files = [];
26
+ this._updateFileWrappers = (data) => {
27
+ let newFileWrappers = [];
28
+ for (const file of data) {
29
+ if (this.multiple) {
30
+ newFileWrappers.push(file);
31
+ } else {
32
+ newFileWrappers = [file];
33
+ }
34
+ }
35
+ this._files = newFileWrappers;
36
+ };
37
+ this.addEventListener("dragenter", () => this._setShowDropzone(true));
38
+ this.addEventListener("dragleave", () => this._setShowDropzone(false));
39
+ this.addEventListener("drop", () => this._setShowDropzone(false));
40
+ }
41
+ get value() {
42
+ return this._value;
43
+ }
44
+ set value(newValue) {
45
+ super.value = newValue;
46
+ if (newValue instanceof FormData) {
47
+ const data = this.multiple ? newValue.getAll(this.name) : [newValue.get(this.name)];
48
+ this._updateFileWrappers(data);
49
+ return;
50
+ }
51
+ if (newValue instanceof File) {
52
+ this._updateFileWrappers([newValue]);
53
+ return;
54
+ }
55
+ }
56
+ connectedCallback() {
57
+ super.connectedCallback();
58
+ demandCustomElement(this, "uui-icon");
59
+ demandCustomElement(this, "uui-file-dropzone");
60
+ demandCustomElement(this, "uui-button");
61
+ demandCustomElement(this, "uui-action-bar");
62
+ demandCustomElement(this, "uui-file-preview");
63
+ }
64
+ getFormElement() {
65
+ return this._dropZone;
66
+ }
67
+ _handleClick(e) {
68
+ e.stopImmediatePropagation();
69
+ this._dropzone.browse();
70
+ }
71
+ async _handleFilesChange(event) {
72
+ const entries = event.detail.files;
73
+ const files = entries.filter((entry) => entry instanceof File || entry.isFile);
74
+ if (!this.multiple) {
75
+ const entry = files[0];
76
+ const isFile = entry instanceof File;
77
+ const file = isFile ? entry : await this._getFile(entry);
78
+ if (this.value instanceof File) {
79
+ this.value = file;
80
+ return;
81
+ }
82
+ if (this.value instanceof FormData) {
83
+ this.value.delete(this.name);
84
+ this.value.append(this.name, file);
85
+ this._updateFileWrappers([file]);
86
+ return;
87
+ }
88
+ }
89
+ let newValue = this.value;
90
+ if (files.length > 0 && !(this.value instanceof FormData)) {
91
+ newValue = new FormData();
92
+ }
93
+ if (newValue instanceof FormData) {
94
+ for (const entry of files) {
95
+ const isFile = entry instanceof File;
96
+ newValue.append(this.name, isFile ? entry : await this._getFile(entry));
97
+ }
98
+ }
99
+ this.value = newValue;
100
+ }
101
+ async _getFile(fileEntry) {
102
+ return await new Promise((resolve, reject) => fileEntry.file(resolve, reject));
103
+ }
104
+ _removeFile(index) {
105
+ const fileToRemove = this._files[index];
106
+ if (this.value instanceof FormData) {
107
+ const files = this.value.getAll(this.name);
108
+ const filteredFiles = files.filter((file) => file !== fileToRemove);
109
+ if (filteredFiles.length === 0) {
110
+ this.value = "";
111
+ } else {
112
+ this.value.delete(this.name);
113
+ for (const file of filteredFiles) {
114
+ this.value.append(this.name, file);
115
+ }
116
+ }
117
+ this._updateFileWrappers(filteredFiles);
118
+ }
119
+ if (this.value instanceof File) {
120
+ this.value = "";
121
+ this._updateFileWrappers([]);
122
+ }
123
+ }
124
+ _setShowDropzone(show) {
125
+ if (show) {
126
+ this._dropZone.style.display = "flex";
127
+ } else {
128
+ this._dropZone.style.display = "none";
129
+ }
130
+ }
131
+ _renderFileItem(file, index) {
132
+ return html`<uui-file-preview .file="${file}">
133
+ <uui-action-bar slot="actions">
134
+ <uui-button
135
+ @click=${() => this._removeFile(index)}
136
+ look="danger"
137
+ label=${`Delete ${file.name}`}>
138
+ <uui-icon name="delete" .fallback=${iconDelete.strings[0]}></uui-icon>
139
+ </uui-button>
140
+ </uui-action-bar>
141
+ </uui-file-preview>`;
142
+ }
143
+ _renderFiles() {
144
+ return html`${repeat(this._files, (file) => file.name + file.size, (file, index) => this._renderFileItem(file, index))}`;
145
+ }
146
+ render() {
147
+ return html`
148
+ <uui-file-dropzone
149
+ id="dropzone"
150
+ ?multiple=${this.multiple}
151
+ .accept=${this.accept}
152
+ @file-change=${this._handleFilesChange}
153
+ label="Drop files here"></uui-file-dropzone>
154
+ <div id="files">
155
+ ${this._renderFiles()}
156
+ <uui-button
157
+ @click=${this._handleClick}
158
+ id="add-button"
159
+ look="placeholder"
160
+ label="Add"></uui-button>
161
+ </div>
162
+ `;
163
+ }
164
+ };
165
+ UUIInputFileElement.styles = [
166
+ css`
167
+ :host {
168
+ width: 100%;
169
+ height: 100%;
170
+ position: relative;
171
+ display: flex;
172
+ box-sizing: border-box;
173
+ border: 1px solid var(--uui-interface-border,#c4c4c4);
174
+ }
175
+
176
+ #input {
177
+ position: absolute;
178
+ width: 0px;
179
+ height: 0px;
180
+ opacity: 0;
181
+ display: none;
182
+ }
183
+
184
+ #files {
185
+ display: grid;
186
+ box-sizing: border-box;
187
+ justify-items: center;
188
+ width: 100%;
189
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
190
+ grid-auto-rows: min-content;
191
+ gap: 16px;
192
+ padding: 16px;
193
+ overflow: auto;
194
+ max-height: 100%;
195
+ }
196
+
197
+ #dropzone {
198
+ display: none;
199
+ position: absolute;
200
+ inset: 0px;
201
+ z-index: 10;
202
+ justify-content: center;
203
+ align-items: center;
204
+ }
205
+
206
+ #add-button {
207
+ width: 150px;
208
+ height: 150px;
209
+ display: flex;
210
+ padding: 16px;
211
+ box-sizing: border-box;
212
+ justify-content: center;
213
+ align-items: center;
214
+ }
215
+ `
216
+ ];
217
+ __decorateClass([
218
+ query("#dropzone")
219
+ ], UUIInputFileElement.prototype, "_dropzone", 2);
220
+ __decorateClass([
221
+ query("#dropzone")
222
+ ], UUIInputFileElement.prototype, "_dropZone", 2);
223
+ __decorateClass([
224
+ property({ type: String })
225
+ ], UUIInputFileElement.prototype, "accept", 2);
226
+ __decorateClass([
227
+ property({ type: Boolean })
228
+ ], UUIInputFileElement.prototype, "multiple", 2);
229
+ __decorateClass([
230
+ state()
231
+ ], UUIInputFileElement.prototype, "_files", 2);
232
+ UUIInputFileElement = __decorateClass([
233
+ defineElement("uui-input-file")
234
+ ], UUIInputFileElement);
235
+
236
+ export { UUIInputFileElement };
@@ -0,0 +1,47 @@
1
+ import { LitElement } from 'lit';
2
+ declare const UUIInputFileElement_base: (new (...args: any[]) => import("@umbraco-ui/uui-base/lib/mixins").FormControlMixinInterface) & typeof LitElement;
3
+ /**
4
+ * @element uui-input-file
5
+ * @description - A form associated file input that supports multiple files.
6
+ * @extends FormControlMixin
7
+ */
8
+ export declare class UUIInputFileElement extends UUIInputFileElement_base {
9
+ static styles: import("lit").CSSResult[];
10
+ private _dropzone;
11
+ private _dropZone;
12
+ /**
13
+ * Accepted filetypes. Will allow all types if empty.
14
+ * @type {string}
15
+ * @attr
16
+ * @default false
17
+ */
18
+ accept: string;
19
+ /**
20
+ * Allows for multiple files to be selected.
21
+ * @type {boolean}
22
+ * @attr
23
+ * @default false
24
+ */
25
+ multiple: boolean;
26
+ get value(): FormDataEntryValue | FormData;
27
+ set value(newValue: FormDataEntryValue | FormData);
28
+ private _files;
29
+ constructor();
30
+ connectedCallback(): void;
31
+ protected getFormElement(): HTMLElement;
32
+ private _handleClick;
33
+ private _updateFileWrappers;
34
+ private _handleFilesChange;
35
+ private _getFile;
36
+ private _removeFile;
37
+ private _setShowDropzone;
38
+ private _renderFileItem;
39
+ private _renderFiles;
40
+ render(): import("lit-html").TemplateResult<1>;
41
+ }
42
+ declare global {
43
+ interface HTMLElementTagNameMap {
44
+ 'uui-input-file': UUIInputFileElement;
45
+ }
46
+ }
47
+ export {};
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@umbraco-ui/uui-input-file",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "keywords": [
6
+ "Umbraco",
7
+ "Custom elements",
8
+ "Web components",
9
+ "UI",
10
+ "Lit",
11
+ "Input File"
12
+ ],
13
+ "description": "Umbraco UI input-file component.",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/umbraco/Umbraco.UI.git",
17
+ "directory": "packages/uui-input-file"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/umbraco/Umbraco.UI/issues"
21
+ },
22
+ "main": "./lib/index.js",
23
+ "module": "./lib/index.js",
24
+ "types": "./lib/index.d.ts",
25
+ "type": "module",
26
+ "customElements": "custom-elements.json",
27
+ "files": [
28
+ "lib/**/*.d.ts",
29
+ "lib/**/*.js",
30
+ "custom-elements.json"
31
+ ],
32
+ "dependencies": {
33
+ "@umbraco-ui/uui-action-bar": "0.2.0",
34
+ "@umbraco-ui/uui-base": "0.2.0",
35
+ "@umbraco-ui/uui-button": "0.3.0",
36
+ "@umbraco-ui/uui-file-dropzone": "0.1.0",
37
+ "@umbraco-ui/uui-icon": "0.2.0",
38
+ "@umbraco-ui/uui-icon-registry-essential": "0.2.0"
39
+ },
40
+ "scripts": {
41
+ "build": "npm run analyze && tsc --build --force && rollup -c rollup.config.js",
42
+ "clean": "tsc --build --clean && rimraf dist lib/*.js lib/**/*.js custom-elements.json",
43
+ "analyze": "web-component-analyzer **/*.element.ts --outFile custom-elements.json"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "homepage": "https://uui.umbraco.com/?path=/story/uui-input-file",
49
+ "gitHead": "5494b55e03c9fb3ba8f160e693d3ce59c02d21cd"
50
+ }