@nuralyui/file-upload 0.0.1 → 0.0.2

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,18 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google Laabidi Aymen
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { LitElement } from 'lit';
7
+ import "../file-upload.component";
8
+ export declare class ElMeenuElement extends LitElement {
9
+ float: string;
10
+ uploadProcesses: Map<string, number>;
11
+ firstUpdated(): void;
12
+ private _handleFileSelect;
13
+ private _handleFileRemove;
14
+ private _handleFileExceed;
15
+ protected render(): import("lit").TemplateResult<1>;
16
+ static styles: import("lit").CSSResult[];
17
+ }
18
+ //# sourceMappingURL=file-demo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-demo.d.ts","sourceRoot":"","sources":["../../../../src/components/file-upload/demo/file-demo.ts"],"names":[],"mappings":"AACA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAE5C,OAAO,0BAA0B,CAAC;AAGlC,qBACa,cAAe,SAAQ,UAAU;IACnC,KAAK,SAAU;IACf,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAEjD,YAAY;IAYrB,OAAO,CAAC,iBAAiB;IAkCzB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,iBAAiB;cAMN,MAAM;IA0BzB,OAAgB,MAAM,4BA6CpB;CACH"}
@@ -0,0 +1,157 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ /**
9
+ * @license
10
+ * Copyright 2023 Google Laabidi Aymen
11
+ * SPDX-License-Identifier: MIT
12
+ */
13
+ import { LitElement, css, html } from 'lit';
14
+ import { customElement, state } from 'lit/decorators.js';
15
+ import "../file-upload.component";
16
+ let ElMeenuElement = class ElMeenuElement extends LitElement {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.float = 'left';
20
+ this.uploadProcesses = new Map();
21
+ }
22
+ firstUpdated() {
23
+ var _a;
24
+ // Get reference to the file upload component
25
+ const fileUpload = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('nr-file-upload');
26
+ if (fileUpload) {
27
+ // Add event listeners to the component
28
+ fileUpload.addEventListener('file-select', this._handleFileSelect.bind(this));
29
+ fileUpload.addEventListener('file-remove', this._handleFileRemove.bind(this));
30
+ fileUpload.addEventListener('file-exceed', this._handleFileExceed.bind(this));
31
+ }
32
+ }
33
+ _handleFileSelect(event) {
34
+ var _a;
35
+ const { files } = event.detail;
36
+ const fileUpload = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('nr-file-upload');
37
+ if (!fileUpload)
38
+ return;
39
+ files.forEach((file) => {
40
+ // Update status to 'uploading'
41
+ fileUpload.updateFileStatus(file.uid, 'uploading', 0);
42
+ // Simulate upload process with interval
43
+ const intervalId = window.setInterval(() => {
44
+ const currentPercentage = file.percentage || 0;
45
+ const percentage = Math.min(100, currentPercentage + 10);
46
+ fileUpload.updateFileStatus(file.uid, 'uploading', percentage);
47
+ if (percentage === 100) {
48
+ window.clearInterval(intervalId);
49
+ // Set file as successfully uploaded after a small delay
50
+ setTimeout(() => {
51
+ fileUpload.updateFileStatus(file.uid, 'success');
52
+ }, 300);
53
+ // Remove from tracking
54
+ this.uploadProcesses.delete(file.uid);
55
+ }
56
+ }, 500);
57
+ // Store the interval ID for potential cancellation
58
+ this.uploadProcesses.set(file.uid, intervalId);
59
+ });
60
+ }
61
+ _handleFileRemove(event) {
62
+ const { file } = event.detail;
63
+ console.log(`File removed: ${file.name}`);
64
+ // Cancel upload process if it's still running
65
+ if (this.uploadProcesses.has(file.uid)) {
66
+ window.clearInterval(this.uploadProcesses.get(file.uid));
67
+ this.uploadProcesses.delete(file.uid);
68
+ }
69
+ }
70
+ _handleFileExceed(event) {
71
+ const { files } = event.detail;
72
+ console.warn(`Upload limit exceeded. Tried to upload ${files.length} additional files.`);
73
+ // Could show a notification or alert here
74
+ }
75
+ render() {
76
+ return html `
77
+ <div class="demo-container">
78
+ <h2>File Upload Demo</h2>
79
+ <nr-file-upload
80
+ accept="image/*"
81
+ multiple
82
+ drag
83
+ limit="5"
84
+ tip="JPG/PNG files up to 500kb">
85
+ </nr-file-upload>
86
+
87
+ <div class="usage-info">
88
+ <h3>Usage:</h3>
89
+ <ul>
90
+ <li>Drag & drop files here</li>
91
+ <li>Or click to select files</li>
92
+ <li>Files will automatically start uploading</li>
93
+ <li>Use the X button to remove files</li>
94
+ <li>Click on eye icon to preview images</li>
95
+ </ul>
96
+ </div>
97
+ </div>
98
+ `;
99
+ }
100
+ };
101
+ ElMeenuElement.styles = [
102
+ css `
103
+ :host {
104
+ width: 800px;
105
+ display: flex;
106
+ flex-direction: column;
107
+ align-items: center;
108
+ font-family: sans-serif;
109
+ }
110
+
111
+ .demo-container {
112
+ width: 100%;
113
+ max-width: 700px;
114
+ padding: 20px;
115
+ border-radius: 8px;
116
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
117
+ background-color: #fff;
118
+ }
119
+
120
+ h2 {
121
+ color: #333;
122
+ margin-top: 0;
123
+ }
124
+
125
+ .usage-info {
126
+ margin-top: 30px;
127
+ padding: 15px;
128
+ background-color: #f5f5f5;
129
+ border-radius: 6px;
130
+ }
131
+
132
+ .usage-info h3 {
133
+ margin-top: 0;
134
+ color: #555;
135
+ }
136
+
137
+ ul {
138
+ padding-left: 20px;
139
+ }
140
+
141
+ li {
142
+ margin-bottom: 8px;
143
+ color: #666;
144
+ }
145
+ `,
146
+ ];
147
+ __decorate([
148
+ state()
149
+ ], ElMeenuElement.prototype, "float", void 0);
150
+ __decorate([
151
+ state()
152
+ ], ElMeenuElement.prototype, "uploadProcesses", void 0);
153
+ ElMeenuElement = __decorate([
154
+ customElement('hy-file-demo')
155
+ ], ElMeenuElement);
156
+ export { ElMeenuElement };
157
+ //# sourceMappingURL=file-demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-demo.js","sourceRoot":"","sources":["../../../../src/components/file-upload/demo/file-demo.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * @license\n * Copyright 2023 Google Laabidi Aymen\n * SPDX-License-Identifier: MIT\n */\nimport { LitElement, css, html } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport \"../file-upload.component\";\nimport { UploadFile } from '../types';\n\n@customElement('hy-file-demo')\nexport class ElMeenuElement extends LitElement {\n @state() float = 'left';\n @state() uploadProcesses: Map<string, number> = new Map();\n\n override firstUpdated() {\n // Get reference to the file upload component\n const fileUpload : any = this.shadowRoot?.querySelector('nr-file-upload');\n \n if (fileUpload) {\n // Add event listeners to the component\n fileUpload.addEventListener('file-select', this._handleFileSelect.bind(this));\n fileUpload.addEventListener('file-remove', this._handleFileRemove.bind(this));\n fileUpload.addEventListener('file-exceed', this._handleFileExceed.bind(this));\n }\n }\n\n private _handleFileSelect(event: CustomEvent) {\n const { files } = event.detail;\n const fileUpload : any= this.shadowRoot?.querySelector('nr-file-upload');\n \n if (!fileUpload) return;\n \n files.forEach((file: UploadFile) => {\n // Update status to 'uploading'\n fileUpload.updateFileStatus(file.uid, 'uploading', 0);\n \n // Simulate upload process with interval\n const intervalId = window.setInterval(() => {\n const currentPercentage = file.percentage || 0;\n const percentage = Math.min(100, currentPercentage + 10);\n \n fileUpload.updateFileStatus(file.uid, 'uploading', percentage);\n \n if (percentage === 100) {\n window.clearInterval(intervalId);\n // Set file as successfully uploaded after a small delay\n setTimeout(() => {\n fileUpload.updateFileStatus(file.uid, 'success');\n }, 300);\n \n // Remove from tracking\n this.uploadProcesses.delete(file.uid);\n }\n }, 500);\n \n // Store the interval ID for potential cancellation\n this.uploadProcesses.set(file.uid, intervalId);\n });\n }\n\n private _handleFileRemove(event: CustomEvent) {\n const { file } = event.detail;\n console.log(`File removed: ${file.name}`);\n \n // Cancel upload process if it's still running\n if (this.uploadProcesses.has(file.uid)) {\n window.clearInterval(this.uploadProcesses.get(file.uid));\n this.uploadProcesses.delete(file.uid);\n }\n }\n\n private _handleFileExceed(event: CustomEvent) {\n const { files } = event.detail;\n console.warn(`Upload limit exceeded. Tried to upload ${files.length} additional files.`);\n // Could show a notification or alert here\n }\n\n protected override render() {\n return html`\n <div class=\"demo-container\">\n <h2>File Upload Demo</h2>\n <nr-file-upload\n accept=\"image/*\"\n multiple\n drag\n limit=\"5\"\n tip=\"JPG/PNG files up to 500kb\">\n </nr-file-upload>\n \n <div class=\"usage-info\">\n <h3>Usage:</h3>\n <ul>\n <li>Drag & drop files here</li>\n <li>Or click to select files</li>\n <li>Files will automatically start uploading</li>\n <li>Use the X button to remove files</li>\n <li>Click on eye icon to preview images</li>\n </ul>\n </div>\n </div>\n `;\n }\n\n static override styles = [\n css`\n :host {\n width: 800px;\n display: flex;\n flex-direction: column;\n align-items: center;\n font-family: sans-serif;\n }\n \n .demo-container {\n width: 100%;\n max-width: 700px;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n background-color: #fff;\n }\n \n h2 {\n color: #333;\n margin-top: 0;\n }\n \n .usage-info {\n margin-top: 30px;\n padding: 15px;\n background-color: #f5f5f5;\n border-radius: 6px;\n }\n \n .usage-info h3 {\n margin-top: 0;\n color: #555;\n }\n \n ul {\n padding-left: 20px;\n }\n \n li {\n margin-bottom: 8px;\n color: #666;\n }\n `,\n ];\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { LitElement, PropertyValues } from 'lit';
2
+ import { UploadFile } from './types';
3
+ export declare class FileUpload extends LitElement {
4
+ static styles: import("lit").CSSResult;
5
+ accept: string;
6
+ multiple: boolean;
7
+ drag: boolean;
8
+ tip: string;
9
+ limit: number;
10
+ preview: boolean;
11
+ fileList: UploadFile[];
12
+ isDragOver: boolean;
13
+ showDragArea: boolean;
14
+ inputElement: HTMLInputElement | null;
15
+ dragCounter: number;
16
+ previewImage: string | null;
17
+ connectedCallback(): void;
18
+ disconnectedCallback(): void;
19
+ firstUpdated(_changedProperties: PropertyValues): void;
20
+ private _onDocumentDragEnter;
21
+ private _onDocumentDragLeave;
22
+ private _onDocumentDrop;
23
+ private _onDocumentDragOver;
24
+ private _onDrop;
25
+ private _onClick;
26
+ private _onChange;
27
+ private _handleFiles;
28
+ updateFileStatus(uid: string, status: 'ready' | 'uploading' | 'success' | 'error', percentage?: number): void;
29
+ private _updateFile;
30
+ private _removeFile;
31
+ private _dispatchEvent;
32
+ private _showPreview;
33
+ private _closePreview;
34
+ render(): import("lit").TemplateResult<1>;
35
+ }
36
+ //# sourceMappingURL=file.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.component.d.ts","sourceRoot":"","sources":["../../../src/components/file-upload/file.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,cAAc,EAAE,MAAM,KAAK,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAIrC,qBACa,UAAW,SAAQ,UAAU;IACxC,OAAgB,MAAM,0BAAU;IAEJ,MAAM,EAAE,MAAM,CAAM;IACnB,QAAQ,EAAE,OAAO,CAAS;IAC1B,IAAI,EAAE,OAAO,CAAQ;IACtB,GAAG,EAAE,MAAM,CAAM;IACjB,KAAK,EAAE,MAAM,CAAK;IACjB,OAAO,EAAE,OAAO,CAAQ;IAE5C,QAAQ,EAAE,UAAU,EAAE,CAAM;IAC5B,UAAU,EAAE,OAAO,CAAS;IAC5B,YAAY,EAAE,OAAO,CAAS;IAC9B,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAC7C,WAAW,EAAE,MAAM,CAAK;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC,iBAAiB;IAUjB,oBAAoB;IAUpB,YAAY,CAAC,kBAAkB,EAAE,cAAc;IAKxD,OAAO,CAAC,oBAAoB,CAO1B;IAEF,OAAO,CAAC,oBAAoB,CAQ1B;IAEF,OAAO,CAAC,eAAe,CAOrB;IAEF,OAAO,CAAC,mBAAmB,CAYzB;IAEF,OAAO,CAAC,OAAO,CAWb;IAEF,OAAO,CAAC,QAAQ,CAEd;IAEF,OAAO,CAAC,SAAS,CAMf;YAEY,YAAY;IAmCnB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM;IAY7G,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;IAIZ,MAAM;CAwGhB"}
@@ -0,0 +1,327 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
16
+ import { LitElement, html } from 'lit';
17
+ import { customElement, property, state } from 'lit/decorators.js';
18
+ import { styles } from './styles';
19
+ import { fileUtils } from './utils';
20
+ let FileUpload = class FileUpload extends LitElement {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.accept = '';
24
+ this.multiple = false;
25
+ this.drag = true;
26
+ this.tip = '';
27
+ this.limit = 0;
28
+ this.preview = true;
29
+ this.fileList = [];
30
+ this.isDragOver = false;
31
+ this.showDragArea = false;
32
+ this.inputElement = null;
33
+ this.dragCounter = 0;
34
+ this.previewImage = null;
35
+ this._onDocumentDragEnter = (e) => {
36
+ e.preventDefault();
37
+ this.dragCounter++;
38
+ if (this.dragCounter === 1) {
39
+ this.showDragArea = true;
40
+ }
41
+ };
42
+ this._onDocumentDragLeave = (e) => {
43
+ e.preventDefault();
44
+ this.dragCounter--;
45
+ if (this.dragCounter === 0) {
46
+ this.showDragArea = false;
47
+ this.isDragOver = false;
48
+ }
49
+ };
50
+ this._onDocumentDrop = (e) => {
51
+ if (e.target !== this && !this.contains(e.target)) {
52
+ e.preventDefault();
53
+ this.dragCounter = 0;
54
+ this.showDragArea = false;
55
+ this.isDragOver = false;
56
+ }
57
+ };
58
+ this._onDocumentDragOver = (e) => {
59
+ e.preventDefault();
60
+ // Determine if the drag is over our component
61
+ const path = e.composedPath();
62
+ const isOverComponent = path.includes(this);
63
+ if (isOverComponent) {
64
+ this.isDragOver = true;
65
+ }
66
+ else {
67
+ this.isDragOver = false;
68
+ }
69
+ };
70
+ this._onDrop = (e) => {
71
+ var _a;
72
+ e.preventDefault();
73
+ e.stopPropagation();
74
+ this.dragCounter = 0;
75
+ this.isDragOver = false;
76
+ this.showDragArea = false;
77
+ if ((_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.files) {
78
+ this._handleFiles(e.dataTransfer.files);
79
+ }
80
+ };
81
+ this._onClick = () => {
82
+ var _a;
83
+ (_a = this.inputElement) === null || _a === void 0 ? void 0 : _a.click();
84
+ };
85
+ this._onChange = (e) => {
86
+ const target = e.target;
87
+ if (target.files) {
88
+ this._handleFiles(target.files);
89
+ target.value = ''; // Reset so same file can be selected again
90
+ }
91
+ };
92
+ }
93
+ connectedCallback() {
94
+ super.connectedCallback();
95
+ if (this.drag) {
96
+ document.addEventListener('dragenter', this._onDocumentDragEnter);
97
+ document.addEventListener('dragleave', this._onDocumentDragLeave);
98
+ document.addEventListener('drop', this._onDocumentDrop);
99
+ document.addEventListener('dragover', this._onDocumentDragOver);
100
+ }
101
+ }
102
+ disconnectedCallback() {
103
+ super.disconnectedCallback();
104
+ if (this.drag) {
105
+ document.removeEventListener('dragenter', this._onDocumentDragEnter);
106
+ document.removeEventListener('dragleave', this._onDocumentDragLeave);
107
+ document.removeEventListener('drop', this._onDocumentDrop);
108
+ document.removeEventListener('dragover', this._onDocumentDragOver);
109
+ }
110
+ }
111
+ firstUpdated(_changedProperties) {
112
+ var _a;
113
+ super.firstUpdated(_changedProperties);
114
+ this.inputElement = ((_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('input[type="file"]')) || null;
115
+ }
116
+ _handleFiles(files) {
117
+ return __awaiter(this, void 0, void 0, function* () {
118
+ if (this.limit > 0 && this.fileList.length + files.length > this.limit) {
119
+ this._dispatchEvent('exceed', { files });
120
+ return;
121
+ }
122
+ const newFiles = [];
123
+ for (const file of Array.from(files)) {
124
+ const isImage = fileUtils.isImageFile(file);
125
+ const fileObj = {
126
+ name: file.name,
127
+ size: fileUtils.formatFileSize(file.size),
128
+ raw: file,
129
+ status: 'ready',
130
+ percentage: 0,
131
+ uid: Date.now() + Math.random().toString(36).substring(2),
132
+ isImage
133
+ };
134
+ // Create preview URL for images if preview is enabled
135
+ if (this.preview && isImage) {
136
+ fileObj.url = yield fileUtils.createFilePreview(file);
137
+ }
138
+ this.fileList = [...this.fileList, fileObj];
139
+ newFiles.push(fileObj);
140
+ }
141
+ this.requestUpdate();
142
+ // Emit file-selected event with the new files
143
+ this._dispatchEvent('select', { files: newFiles, fileList: this.fileList });
144
+ });
145
+ }
146
+ updateFileStatus(uid, status, percentage) {
147
+ const file = this.fileList.find(f => f.uid === uid);
148
+ if (file) {
149
+ file.status = status;
150
+ if (percentage !== undefined) {
151
+ file.percentage = percentage;
152
+ }
153
+ this._updateFile(file);
154
+ }
155
+ }
156
+ _updateFile(updatedFile) {
157
+ this.fileList = this.fileList.map(file => file.uid === updatedFile.uid ? updatedFile : file);
158
+ this.requestUpdate();
159
+ }
160
+ _removeFile(uid) {
161
+ const file = this.fileList.find(file => file.uid === uid);
162
+ this.fileList = this.fileList.filter(file => file.uid !== uid);
163
+ if (file) {
164
+ this._dispatchEvent('remove', { file });
165
+ }
166
+ this.requestUpdate();
167
+ }
168
+ _dispatchEvent(name, data) {
169
+ this.dispatchEvent(new CustomEvent(`file-${name}`, {
170
+ detail: data,
171
+ bubbles: true,
172
+ composed: true
173
+ }));
174
+ }
175
+ _showPreview(url) {
176
+ this.previewImage = url;
177
+ }
178
+ _closePreview() {
179
+ this.previewImage = null;
180
+ }
181
+ render() {
182
+ return html `
183
+ <div class="upload" @drop=${this._onDrop} @dragover=${(e) => e.preventDefault()}>
184
+ <input
185
+ type="file"
186
+ class="hidden"
187
+ accept=${this.accept}
188
+ ?multiple=${this.multiple}
189
+ @change=${this._onChange}
190
+ />
191
+
192
+ ${this.showDragArea ? html `
193
+ <div
194
+ class="upload-dragger ${this.isDragOver ? 'is-dragover' : ''}"
195
+ @click=${this._onClick}
196
+ >
197
+ <div class="upload-icon">
198
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
199
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
200
+ <polyline points="17 8 12 3 7 8"></polyline>
201
+ <line x1="12" y1="3" x2="12" y2="15"></line>
202
+ </svg>
203
+ </div>
204
+ <div class="upload-text">Drop file here or click to upload</div>
205
+ ${this.tip ? html `<div class="upload-tip">${this.tip}</div>` : ''}
206
+ </div>
207
+ ` : html `
208
+ <button class="upload-button" @click=${this._onClick}>Upload File</button>
209
+ ${this.tip ? html `<div class="upload-tip">${this.tip}</div>` : ''}
210
+ `}
211
+
212
+ <div class="file-list">
213
+ ${this.fileList.map(file => html `
214
+ <div class="file-item">
215
+ ${file.isImage ? html `
216
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
217
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
218
+ <circle cx="8.5" cy="8.5" r="1.5"></circle>
219
+ <polyline points="21 15 16 10 5 21"></polyline>
220
+ </svg>
221
+ ` : html `
222
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
223
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
224
+ <polyline points="14 2 14 8 20 8"></polyline>
225
+ </svg>
226
+ `}
227
+ <div class="file-name">${file.name}</div>
228
+ <div class="file-size">${file.size}</div>
229
+ <div class="file-status">
230
+ ${file.status === 'success' ? html `
231
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#67c23a" stroke-width="2">
232
+ <polyline points="20 6 9 17 4 12"></polyline>
233
+ </svg>
234
+ ` : file.status === 'error' ? html `
235
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f56c6c" stroke-width="2">
236
+ <line x1="18" y1="6" x2="6" y2="18"></line>
237
+ <line x1="6" y1="6" x2="18" y2="18"></line>
238
+ </svg>
239
+ ` : file.status === 'uploading' ? html `${file.percentage}%` : ''}
240
+ </div>
241
+ <div class="file-actions">
242
+ ${file.isImage && file.url ? html `
243
+ <button class="preview-icon" @click=${() => this._showPreview(file.url)}>
244
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
245
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
246
+ <circle cx="12" cy="12" r="3"></circle>
247
+ </svg>
248
+ </button>
249
+ ` : ''}
250
+ <button @click=${() => this._removeFile(file.uid)}>
251
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
252
+ <line x1="18" y1="6" x2="6" y2="18"></line>
253
+ <line x1="6" y1="6" x2="18" y2="18"></line>
254
+ </svg>
255
+ </button>
256
+ </div>
257
+ ${file.status === 'uploading' ? html `
258
+ <div class="progress-bar">
259
+ <div class="progress-inner" style="width: ${file.percentage}%"></div>
260
+ </div>
261
+ ` : ''}
262
+ </div>
263
+ ${this.preview && file.isImage && file.url ? html `
264
+ <div class="file-preview">
265
+ <img
266
+ class="image-preview"
267
+ src="${file.url}"
268
+ alt="${file.name}"
269
+ @click=${() => this._showPreview(file.url)}
270
+ />
271
+ </div>
272
+ ` : ''}
273
+ `)}
274
+ </div>
275
+
276
+ ${this.previewImage ? html `
277
+ <div class="preview-modal" @click=${this._closePreview}>
278
+ <button class="preview-close">×</button>
279
+ <img src="${this.previewImage}" alt="Preview" />
280
+ </div>
281
+ ` : ''}
282
+ </div>
283
+ `;
284
+ }
285
+ };
286
+ FileUpload.styles = styles;
287
+ __decorate([
288
+ property({ type: String })
289
+ ], FileUpload.prototype, "accept", void 0);
290
+ __decorate([
291
+ property({ type: Boolean })
292
+ ], FileUpload.prototype, "multiple", void 0);
293
+ __decorate([
294
+ property({ type: Boolean })
295
+ ], FileUpload.prototype, "drag", void 0);
296
+ __decorate([
297
+ property({ type: String })
298
+ ], FileUpload.prototype, "tip", void 0);
299
+ __decorate([
300
+ property({ type: Number })
301
+ ], FileUpload.prototype, "limit", void 0);
302
+ __decorate([
303
+ property({ type: Boolean })
304
+ ], FileUpload.prototype, "preview", void 0);
305
+ __decorate([
306
+ state()
307
+ ], FileUpload.prototype, "fileList", void 0);
308
+ __decorate([
309
+ state()
310
+ ], FileUpload.prototype, "isDragOver", void 0);
311
+ __decorate([
312
+ state()
313
+ ], FileUpload.prototype, "showDragArea", void 0);
314
+ __decorate([
315
+ state()
316
+ ], FileUpload.prototype, "inputElement", void 0);
317
+ __decorate([
318
+ state()
319
+ ], FileUpload.prototype, "dragCounter", void 0);
320
+ __decorate([
321
+ state()
322
+ ], FileUpload.prototype, "previewImage", void 0);
323
+ FileUpload = __decorate([
324
+ customElement('nr-file-upload')
325
+ ], FileUpload);
326
+ export { FileUpload };
327
+ //# sourceMappingURL=file.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.component.js","sourceRoot":"","sources":["../../../src/components/file-upload/file.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, PropertyValues } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { UploadFile } from './types';\nimport { styles } from './styles';\nimport { fileUtils } from './utils';\n\n@customElement('nr-file-upload')\nexport class FileUpload extends LitElement {\n static override styles = styles;\n\n @property({ type: String }) accept: string = '';\n @property({ type: Boolean }) multiple: boolean = false;\n @property({ type: Boolean }) drag: boolean = true;\n @property({ type: String }) tip: string = '';\n @property({ type: Number }) limit: number = 0;\n @property({ type: Boolean }) preview: boolean = true;\n\n @state() fileList: UploadFile[] = [];\n @state() isDragOver: boolean = false;\n @state() showDragArea: boolean = false;\n @state() inputElement: HTMLInputElement | null = null;\n @state() dragCounter: number = 0;\n @state() previewImage: string | null = null;\n\n override connectedCallback() {\n super.connectedCallback();\n if (this.drag) {\n document.addEventListener('dragenter', this._onDocumentDragEnter);\n document.addEventListener('dragleave', this._onDocumentDragLeave);\n document.addEventListener('drop', this._onDocumentDrop);\n document.addEventListener('dragover', this._onDocumentDragOver);\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n if (this.drag) {\n document.removeEventListener('dragenter', this._onDocumentDragEnter);\n document.removeEventListener('dragleave', this._onDocumentDragLeave);\n document.removeEventListener('drop', this._onDocumentDrop);\n document.removeEventListener('dragover', this._onDocumentDragOver);\n }\n }\n\n override firstUpdated(_changedProperties: PropertyValues) {\n super.firstUpdated(_changedProperties);\n this.inputElement = this.shadowRoot?.querySelector('input[type=\"file\"]') || null;\n }\n\n private _onDocumentDragEnter = (e: DragEvent) => {\n e.preventDefault();\n this.dragCounter++;\n \n if (this.dragCounter === 1) {\n this.showDragArea = true;\n }\n };\n\n private _onDocumentDragLeave = (e: DragEvent) => {\n e.preventDefault();\n this.dragCounter--;\n \n if (this.dragCounter === 0) {\n this.showDragArea = false;\n this.isDragOver = false;\n }\n };\n\n private _onDocumentDrop = (e: DragEvent) => {\n if (e.target !== this && !this.contains(e.target as Node)) {\n e.preventDefault();\n this.dragCounter = 0;\n this.showDragArea = false;\n this.isDragOver = false;\n }\n };\n\n private _onDocumentDragOver = (e: DragEvent) => {\n e.preventDefault();\n \n // Determine if the drag is over our component\n const path = e.composedPath();\n const isOverComponent = path.includes(this);\n \n if (isOverComponent) {\n this.isDragOver = true;\n } else {\n this.isDragOver = false;\n }\n };\n\n private _onDrop = (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n \n this.dragCounter = 0;\n this.isDragOver = false;\n this.showDragArea = false;\n\n if (e.dataTransfer?.files) {\n this._handleFiles(e.dataTransfer.files);\n }\n };\n\n private _onClick = () => {\n this.inputElement?.click();\n };\n\n private _onChange = (e: Event) => {\n const target = e.target as HTMLInputElement;\n if (target.files) {\n this._handleFiles(target.files);\n target.value = ''; // Reset so same file can be selected again\n }\n };\n\n private async _handleFiles(files: FileList) {\n if (this.limit > 0 && this.fileList.length + files.length > this.limit) {\n this._dispatchEvent('exceed', { files });\n return;\n }\n\n const newFiles: UploadFile[] = [];\n\n for (const file of Array.from(files)) {\n const isImage = fileUtils.isImageFile(file);\n const fileObj: UploadFile = {\n name: file.name,\n size: fileUtils.formatFileSize(file.size),\n raw: file,\n status: 'ready',\n percentage: 0,\n uid: Date.now() + Math.random().toString(36).substring(2),\n isImage\n };\n\n // Create preview URL for images if preview is enabled\n if (this.preview && isImage) {\n fileObj.url = await fileUtils.createFilePreview(file);\n }\n\n this.fileList = [...this.fileList, fileObj];\n newFiles.push(fileObj);\n }\n\n this.requestUpdate();\n\n // Emit file-selected event with the new files\n this._dispatchEvent('select', { files: newFiles, fileList: this.fileList });\n }\n\n public updateFileStatus(uid: string, status: 'ready' | 'uploading' | 'success' | 'error', percentage?: number) {\n const file = this.fileList.find(f => f.uid === uid);\n \n if (file) {\n file.status = status;\n if (percentage !== undefined) {\n file.percentage = percentage;\n }\n this._updateFile(file);\n }\n }\n\n private _updateFile(updatedFile: UploadFile) {\n this.fileList = this.fileList.map(file =>\n file.uid === updatedFile.uid ? updatedFile : file\n );\n this.requestUpdate();\n }\n\n private _removeFile(uid: string) {\n const file = this.fileList.find(file => file.uid === uid);\n this.fileList = this.fileList.filter(file => file.uid !== uid);\n if (file) {\n this._dispatchEvent('remove', { file });\n }\n this.requestUpdate();\n }\n\n private _dispatchEvent(name: string, data: any) {\n this.dispatchEvent(new CustomEvent(`file-${name}`, {\n detail: data,\n bubbles: true,\n composed: true\n }));\n }\n\n private _showPreview(url: string) {\n this.previewImage = url;\n }\n\n private _closePreview() {\n this.previewImage = null;\n }\n\n override render() {\n return html`\n <div class=\"upload\" @drop=${this._onDrop} @dragover=${(e: DragEvent) => e.preventDefault()}>\n <input \n type=\"file\" \n class=\"hidden\" \n accept=${this.accept}\n ?multiple=${this.multiple}\n @change=${this._onChange}\n />\n\n ${this.showDragArea ? html`\n <div \n class=\"upload-dragger ${this.isDragOver ? 'is-dragover' : ''}\" \n @click=${this._onClick}\n >\n <div class=\"upload-icon\">\n <svg width=\"40\" height=\"40\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"></path>\n <polyline points=\"17 8 12 3 7 8\"></polyline>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"></line>\n </svg>\n </div>\n <div class=\"upload-text\">Drop file here or click to upload</div>\n ${this.tip ? html`<div class=\"upload-tip\">${this.tip}</div>` : ''}\n </div>\n ` : html`\n <button class=\"upload-button\" @click=${this._onClick}>Upload File</button>\n ${this.tip ? html`<div class=\"upload-tip\">${this.tip}</div>` : ''}\n `}\n\n <div class=\"file-list\">\n ${this.fileList.map(file => html`\n <div class=\"file-item\">\n ${file.isImage ? html`\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect>\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"></circle>\n <polyline points=\"21 15 16 10 5 21\"></polyline>\n </svg>\n ` : html`\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"></path>\n <polyline points=\"14 2 14 8 20 8\"></polyline>\n </svg>\n `}\n <div class=\"file-name\">${file.name}</div>\n <div class=\"file-size\">${file.size}</div>\n <div class=\"file-status\">\n ${file.status === 'success' ? html`\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#67c23a\" stroke-width=\"2\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n ` : file.status === 'error' ? html`\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#f56c6c\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n ` : file.status === 'uploading' ? html`${file.percentage}%` : ''}\n </div>\n <div class=\"file-actions\">\n ${file.isImage && file.url ? html`\n <button class=\"preview-icon\" @click=${() => this._showPreview(file.url!)}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"></path>\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\n </svg>\n </button>\n ` : ''}\n <button @click=${() => this._removeFile(file.uid)}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n </div>\n ${file.status === 'uploading' ? html`\n <div class=\"progress-bar\">\n <div class=\"progress-inner\" style=\"width: ${file.percentage}%\"></div>\n </div>\n ` : ''}\n </div>\n ${this.preview && file.isImage && file.url ? html`\n <div class=\"file-preview\">\n <img \n class=\"image-preview\" \n src=\"${file.url}\" \n alt=\"${file.name}\" \n @click=${() => this._showPreview(file.url!)}\n />\n </div>\n ` : ''}\n `)}\n </div>\n\n ${this.previewImage ? html`\n <div class=\"preview-modal\" @click=${this._closePreview}>\n <button class=\"preview-close\">×</button>\n <img src=\"${this.previewImage}\" alt=\"Preview\" />\n </div>\n ` : ''}\n </div>\n `;\n }\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuralyui/file-upload",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",