snice 3.8.0 → 3.9.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/bin/snice.js +8 -0
- package/dist/components/file-gallery/snice-file-gallery.d.ts +87 -0
- package/dist/components/file-gallery/snice-file-gallery.js +892 -0
- package/dist/components/file-gallery/snice-file-gallery.js.map +1 -0
- package/dist/components/file-gallery/snice-file-gallery.types.d.ts +72 -0
- package/dist/components/qr-reader/qr-decoder.d.ts +20 -0
- package/dist/components/qr-reader/qr-decoder.js +49 -0
- package/dist/components/qr-reader/qr-decoder.js.map +1 -0
- package/dist/components/qr-reader/qr-worker.d.ts +6 -0
- package/dist/components/qr-reader/qr-worker.js +64 -0
- package/dist/components/qr-reader/qr-worker.js.map +1 -0
- package/dist/components/qr-reader/snice-qr-reader.d.ts +39 -0
- package/dist/components/qr-reader/snice-qr-reader.js +436 -0
- package/dist/components/qr-reader/snice-qr-reader.js.map +1 -0
- package/dist/components/qr-reader/snice-qr-reader.types.d.ts +17 -0
- package/dist/components/qr-reader/zxing-reader.mjs +1582 -0
- package/dist/components/qr-reader/zxing-share.mjs +305 -0
- package/dist/components/qr-reader/zxing_reader.wasm +0 -0
- package/dist/components/zxing-reader-B3Rfebg9.js +1771 -0
- package/dist/components/zxing-reader-B3Rfebg9.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.iife.js +1 -1
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/docs/ai/README.md +1 -1
- package/docs/ai/components/file-gallery.md +206 -0
- package/docs/ai/components/qr-reader.md +80 -0
- package/docs/components/file-gallery.md +692 -0
- package/docs/components/qr-reader.md +327 -0
- package/package.json +1 -1
|
@@ -0,0 +1,892 @@
|
|
|
1
|
+
import { __runInitializers, __esDecorate } from 'tslib';
|
|
2
|
+
import { element, property, query, request, ready, render, styles, dispatch, dispose, html, css } from 'snice';
|
|
3
|
+
|
|
4
|
+
var cssContent = ":host{display:block;font-family:var(--snice-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif)}.file-gallery{display:flex;flex-direction:column;gap:var(--snice-spacing-md,1rem)}.drop-zone{border:2px dashed var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-lg,.5rem);padding:var(--snice-spacing-xl,2rem);background:var(--snice-color-background-element,rgb(252 251 249));cursor:pointer;transition:all var(--snice-transition-normal, 200ms) ease;position:relative}.drop-zone:hover:not(.drop-zone--disabled){border-color:var(--snice-color-primary,rgb(37 99 235));background:var(--snice-color-background-element-hover,rgb(249 247 245))}.drop-zone--drag-over{border-color:var(--snice-color-primary,rgb(37 99 235));background:var(--snice-color-primary-subtle,rgb(219 234 254))}.drop-zone--disabled{opacity:.5;cursor:not-allowed}.drop-zone--has-files{padding:var(--snice-spacing-md,1rem)}.file-input{display:none}.drop-zone-content{display:flex;flex-direction:column;align-items:center;gap:var(--snice-spacing-sm,.5rem);text-align:center}.drop-zone-icon{width:3rem;height:3rem;color:var(--snice-color-text-muted,rgb(115 115 115))}.drop-zone-text{display:flex;flex-direction:column;gap:var(--snice-spacing-xs,.25rem)}.drop-zone-primary{font-size:var(--snice-font-size-base, 1rem);font-weight:var(--snice-font-weight-medium,500);color:var(--snice-color-text,rgb(23 23 23))}.drop-zone-secondary{font-size:var(--snice-font-size-sm, .875rem);color:var(--snice-color-text-muted,rgb(115 115 115))}.drop-zone-hint{font-size:var(--snice-font-size-xs, .75rem);color:var(--snice-color-text-muted,rgb(115 115 115))}.gallery-header{display:flex;justify-content:space-between;align-items:center;padding:var(--snice-spacing-sm,.5rem) 0}.gallery-title{font-size:var(--snice-font-size-base, 1rem);font-weight:var(--snice-font-weight-medium,500);color:var(--snice-color-text,rgb(23 23 23))}.gallery-actions{display:flex;gap:var(--snice-spacing-xs,.25rem)}.gallery-action-button{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.375rem);background:var(--snice-color-background-element,rgb(252 251 249));color:var(--snice-color-text,rgb(23 23 23));cursor:pointer;transition:all var(--snice-transition-fast, 150ms) ease}.gallery-action-button:hover{background:var(--snice-color-background-element-hover,rgb(249 247 245));border-color:var(--snice-color-border-hover,rgb(163 163 163))}.gallery-action-button svg{width:1rem;height:1rem;stroke-width:2}.gallery{display:grid;gap:var(--snice-spacing-md,1rem)}.gallery--grid{grid-template-columns:repeat(auto-fill,minmax(10rem,1fr))}.gallery--list{grid-template-columns:1fr}.gallery-item{display:flex;flex-direction:column;border:1px solid var(--snice-color-border,rgb(226 226 226));border-radius:var(--snice-border-radius-md,.375rem);background:var(--snice-color-background-element,rgb(252 251 249));overflow:hidden;transition:all var(--snice-transition-fast, 150ms) ease}.gallery--list .gallery-item{flex-direction:row}.gallery-item:hover{border-color:var(--snice-color-border-hover,rgb(163 163 163));box-shadow:var(--snice-shadow-sm,0 1px 2px 0 rgb(0 0 0 / .05))}.gallery-item--uploading{border-color:var(--snice-color-primary,rgb(37 99 235))}.gallery-item--error{border-color:var(--snice-color-danger,rgb(239 68 68))}.gallery-item--completed{border-color:var(--snice-color-success,rgb(34 197 94))}.gallery-item-preview{position:relative;aspect-ratio:1;background:var(--snice-color-background,rgb(255 255 255));overflow:hidden}.gallery--list .gallery-item-preview{width:6rem;aspect-ratio:1;flex-shrink:0}.gallery-item-image{width:100%;height:100%;object-fit:cover}.gallery-item-placeholder{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:var(--snice-color-text-muted,rgb(115 115 115))}.gallery-item-placeholder svg{width:3rem;height:3rem;stroke-width:1.5}.gallery-item-progress{position:absolute;bottom:0;left:0;right:0;height:.25rem;background:var(--snice-color-background-subtle,rgb(245 245 245))}.gallery-item-progress-bar{height:100%;background:var(--snice-color-primary,rgb(37 99 235));transition:width var(--snice-transition-fast, 150ms) ease}.gallery-item-status{position:absolute;top:.5rem;right:.5rem;width:1.5rem;height:1.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.gallery-item-status svg{width:1rem;height:1rem;stroke-width:2}.gallery-item-status--success{background:var(--snice-color-success,rgb(34 197 94));color:var(--snice-color-text-inverse,rgb(250 250 250))}.gallery-item-status--error{background:var(--snice-color-danger,rgb(239 68 68));color:var(--snice-color-text-inverse,rgb(250 250 250))}.gallery-item-info{display:flex;flex-direction:column;gap:var(--snice-spacing-xs,.25rem);padding:var(--snice-spacing-sm,.5rem);flex:1;min-width:0}.gallery-item-name{font-size:var(--snice-font-size-sm, .875rem);font-weight:var(--snice-font-weight-medium,500);color:var(--snice-color-text,rgb(23 23 23));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gallery-item-badge{position:absolute;z-index:2;pointer-events:none}.gallery-item-badge--top-left{top:var(--snice-spacing-xs,.25rem);left:var(--snice-spacing-xs,.25rem)}.gallery-item-badge--top-right{top:var(--snice-spacing-xs,.25rem);right:var(--snice-spacing-xs,.25rem)}.gallery-item-badge--bottom-left{bottom:var(--snice-spacing-xs,.25rem);left:var(--snice-spacing-xs,.25rem)}.gallery-item-badge--bottom-right{bottom:var(--snice-spacing-xs,.25rem);right:var(--snice-spacing-xs,.25rem)}.gallery-item-meta{display:flex;gap:var(--snice-spacing-sm,.5rem);font-size:var(--snice-font-size-xs, .75rem);color:var(--snice-color-text-muted,rgb(115 115 115))}.gallery-item-error{color:var(--snice-color-danger,rgb(239 68 68))}.gallery-item-progress-text{color:var(--snice-color-primary,rgb(37 99 235));font-weight:var(--snice-font-weight-medium,500)}.gallery-item-actions{display:flex;gap:var(--snice-spacing-xs,.25rem);padding:var(--snice-spacing-sm,.5rem);border-top:1px solid var(--snice-color-border,rgb(226 226 226))}.gallery--list .gallery-item-actions{border-top:none;border-left:1px solid var(--snice-color-border,rgb(226 226 226));flex-direction:column;justify-content:center;align-items:center}.gallery-item-action{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:none;border-radius:var(--snice-border-radius-sm,.25rem);background:0 0;color:var(--snice-color-text,rgb(23 23 23));cursor:pointer;transition:all var(--snice-transition-fast, 150ms) ease}.gallery-item-action:hover{background:var(--snice-color-background-subtle,rgb(245 245 245))}.gallery-item-action--delete{color:var(--snice-color-danger,rgb(239 68 68))}.gallery-item-action--delete:hover{background:var(--snice-color-danger-subtle,rgb(254 226 226))}.gallery-item-action svg{width:1rem;height:1rem;stroke-width:2}.gallery-item--add-button{cursor:pointer;border-style:dashed;transition:all var(--snice-transition-fast, 150ms) ease}.gallery-item--add-button:hover:not(.gallery-item--disabled){border-color:var(--snice-color-primary,rgb(37 99 235));background:var(--snice-color-primary-subtle,rgb(219 234 254))}.gallery-item--add-button.gallery-item--disabled{opacity:.5;cursor:not-allowed}.gallery-item-add-icon{color:var(--snice-color-primary,rgb(37 99 235))}.gallery-item--add-button .gallery-item-name{color:var(--snice-color-primary,rgb(37 99 235));text-align:center}";
|
|
5
|
+
|
|
6
|
+
let SniceFileGallery = (() => {
|
|
7
|
+
let _classDecorators = [element('snice-file-gallery')];
|
|
8
|
+
let _classDescriptor;
|
|
9
|
+
let _classExtraInitializers = [];
|
|
10
|
+
let _classThis;
|
|
11
|
+
let _classSuper = HTMLElement;
|
|
12
|
+
let _instanceExtraInitializers = [];
|
|
13
|
+
let _accept_decorators;
|
|
14
|
+
let _accept_initializers = [];
|
|
15
|
+
let _accept_extraInitializers = [];
|
|
16
|
+
let _multiple_decorators;
|
|
17
|
+
let _multiple_initializers = [];
|
|
18
|
+
let _multiple_extraInitializers = [];
|
|
19
|
+
let _disabled_decorators;
|
|
20
|
+
let _disabled_initializers = [];
|
|
21
|
+
let _disabled_extraInitializers = [];
|
|
22
|
+
let _maxSize_decorators;
|
|
23
|
+
let _maxSize_initializers = [];
|
|
24
|
+
let _maxSize_extraInitializers = [];
|
|
25
|
+
let _maxFiles_decorators;
|
|
26
|
+
let _maxFiles_initializers = [];
|
|
27
|
+
let _maxFiles_extraInitializers = [];
|
|
28
|
+
let _view_decorators;
|
|
29
|
+
let _view_initializers = [];
|
|
30
|
+
let _view_extraInitializers = [];
|
|
31
|
+
let _showProgress_decorators;
|
|
32
|
+
let _showProgress_initializers = [];
|
|
33
|
+
let _showProgress_extraInitializers = [];
|
|
34
|
+
let _allowPause_decorators;
|
|
35
|
+
let _allowPause_initializers = [];
|
|
36
|
+
let _allowPause_extraInitializers = [];
|
|
37
|
+
let _allowDelete_decorators;
|
|
38
|
+
let _allowDelete_initializers = [];
|
|
39
|
+
let _allowDelete_extraInitializers = [];
|
|
40
|
+
let _autoUpload_decorators;
|
|
41
|
+
let _autoUpload_initializers = [];
|
|
42
|
+
let _autoUpload_extraInitializers = [];
|
|
43
|
+
let _showAddButton_decorators;
|
|
44
|
+
let _showAddButton_initializers = [];
|
|
45
|
+
let _showAddButton_extraInitializers = [];
|
|
46
|
+
let _hideAddButton_decorators;
|
|
47
|
+
let _hideAddButton_initializers = [];
|
|
48
|
+
let _hideAddButton_extraInitializers = [];
|
|
49
|
+
let _input_decorators;
|
|
50
|
+
let _input_initializers = [];
|
|
51
|
+
let _input_extraInitializers = [];
|
|
52
|
+
let _dropZone_decorators;
|
|
53
|
+
let _dropZone_initializers = [];
|
|
54
|
+
let _dropZone_extraInitializers = [];
|
|
55
|
+
let _uploadFile_decorators;
|
|
56
|
+
let _setupComponent_decorators;
|
|
57
|
+
let _renderContent_decorators;
|
|
58
|
+
let _componentStyles_decorators;
|
|
59
|
+
let _emitFilesChange_decorators;
|
|
60
|
+
let _emitFileRemove_decorators;
|
|
61
|
+
let _emitUploadProgress_decorators;
|
|
62
|
+
let _emitUploadComplete_decorators;
|
|
63
|
+
let _emitUploadError_decorators;
|
|
64
|
+
let _emitUploadPause_decorators;
|
|
65
|
+
let _emitError_decorators;
|
|
66
|
+
let _cleanup_decorators;
|
|
67
|
+
(class extends _classSuper {
|
|
68
|
+
static { _classThis = this; }
|
|
69
|
+
constructor() {
|
|
70
|
+
super(...arguments);
|
|
71
|
+
this.accept = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _accept_initializers, ''));
|
|
72
|
+
this.multiple = (__runInitializers(this, _accept_extraInitializers), __runInitializers(this, _multiple_initializers, true));
|
|
73
|
+
this.disabled = (__runInitializers(this, _multiple_extraInitializers), __runInitializers(this, _disabled_initializers, false));
|
|
74
|
+
this.maxSize = (__runInitializers(this, _disabled_extraInitializers), __runInitializers(this, _maxSize_initializers, -1)); // in bytes, -1 for unlimited
|
|
75
|
+
this.maxFiles = (__runInitializers(this, _maxSize_extraInitializers), __runInitializers(this, _maxFiles_initializers, -1)); // -1 for unlimited
|
|
76
|
+
this.view = (__runInitializers(this, _maxFiles_extraInitializers), __runInitializers(this, _view_initializers, 'grid'));
|
|
77
|
+
this.showProgress = (__runInitializers(this, _view_extraInitializers), __runInitializers(this, _showProgress_initializers, true));
|
|
78
|
+
this.allowPause = (__runInitializers(this, _showProgress_extraInitializers), __runInitializers(this, _allowPause_initializers, true));
|
|
79
|
+
this.allowDelete = (__runInitializers(this, _allowPause_extraInitializers), __runInitializers(this, _allowDelete_initializers, true));
|
|
80
|
+
this.autoUpload = (__runInitializers(this, _allowDelete_extraInitializers), __runInitializers(this, _autoUpload_initializers, true));
|
|
81
|
+
this.showAddButton = (__runInitializers(this, _autoUpload_extraInitializers), __runInitializers(this, _showAddButton_initializers, false));
|
|
82
|
+
this.hideAddButton = (__runInitializers(this, _showAddButton_extraInitializers), __runInitializers(this, _hideAddButton_initializers, false));
|
|
83
|
+
this.input = (__runInitializers(this, _hideAddButton_extraInitializers), __runInitializers(this, _input_initializers, void 0));
|
|
84
|
+
this.dropZone = (__runInitializers(this, _input_extraInitializers), __runInitializers(this, _dropZone_initializers, void 0));
|
|
85
|
+
this.galleryFiles = (__runInitializers(this, _dropZone_extraInitializers), []);
|
|
86
|
+
this.galleryCustomActions = [];
|
|
87
|
+
this.isDragOver = false;
|
|
88
|
+
this.uploadAbortControllers = new Map();
|
|
89
|
+
}
|
|
90
|
+
static {
|
|
91
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
92
|
+
_accept_decorators = [property()];
|
|
93
|
+
_multiple_decorators = [property({ type: Boolean })];
|
|
94
|
+
_disabled_decorators = [property({ type: Boolean })];
|
|
95
|
+
_maxSize_decorators = [property({ type: Number, attribute: 'max-size' })];
|
|
96
|
+
_maxFiles_decorators = [property({ type: Number, attribute: 'max-files' })];
|
|
97
|
+
_view_decorators = [property()];
|
|
98
|
+
_showProgress_decorators = [property({ type: Boolean, attribute: 'show-progress' })];
|
|
99
|
+
_allowPause_decorators = [property({ type: Boolean, attribute: 'allow-pause' })];
|
|
100
|
+
_allowDelete_decorators = [property({ type: Boolean, attribute: 'allow-delete' })];
|
|
101
|
+
_autoUpload_decorators = [property({ type: Boolean, attribute: 'auto-upload' })];
|
|
102
|
+
_showAddButton_decorators = [property({ type: Boolean, attribute: 'show-add-button' })];
|
|
103
|
+
_hideAddButton_decorators = [property({ type: Boolean, attribute: 'hide-add-button' })];
|
|
104
|
+
_input_decorators = [query('.file-input')];
|
|
105
|
+
_dropZone_decorators = [query('.drop-zone')];
|
|
106
|
+
_uploadFile_decorators = [request('file-gallery-upload')];
|
|
107
|
+
_setupComponent_decorators = [ready()];
|
|
108
|
+
_renderContent_decorators = [render()];
|
|
109
|
+
_componentStyles_decorators = [styles()];
|
|
110
|
+
_emitFilesChange_decorators = [dispatch('@snice/files-change')];
|
|
111
|
+
_emitFileRemove_decorators = [dispatch('@snice/file-remove')];
|
|
112
|
+
_emitUploadProgress_decorators = [dispatch('@snice/upload-progress')];
|
|
113
|
+
_emitUploadComplete_decorators = [dispatch('@snice/upload-complete')];
|
|
114
|
+
_emitUploadError_decorators = [dispatch('@snice/upload-error')];
|
|
115
|
+
_emitUploadPause_decorators = [dispatch('@snice/upload-pause')];
|
|
116
|
+
_emitError_decorators = [dispatch('@snice/error')];
|
|
117
|
+
_cleanup_decorators = [dispose()];
|
|
118
|
+
__esDecorate(this, null, _uploadFile_decorators, { kind: "method", name: "uploadFile", static: false, private: false, access: { has: obj => "uploadFile" in obj, get: obj => obj.uploadFile }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
119
|
+
__esDecorate(this, null, _setupComponent_decorators, { kind: "method", name: "setupComponent", static: false, private: false, access: { has: obj => "setupComponent" in obj, get: obj => obj.setupComponent }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
120
|
+
__esDecorate(this, null, _renderContent_decorators, { kind: "method", name: "renderContent", static: false, private: false, access: { has: obj => "renderContent" in obj, get: obj => obj.renderContent }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
121
|
+
__esDecorate(this, null, _componentStyles_decorators, { kind: "method", name: "componentStyles", static: false, private: false, access: { has: obj => "componentStyles" in obj, get: obj => obj.componentStyles }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
122
|
+
__esDecorate(this, null, _emitFilesChange_decorators, { kind: "method", name: "emitFilesChange", static: false, private: false, access: { has: obj => "emitFilesChange" in obj, get: obj => obj.emitFilesChange }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
123
|
+
__esDecorate(this, null, _emitFileRemove_decorators, { kind: "method", name: "emitFileRemove", static: false, private: false, access: { has: obj => "emitFileRemove" in obj, get: obj => obj.emitFileRemove }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
124
|
+
__esDecorate(this, null, _emitUploadProgress_decorators, { kind: "method", name: "emitUploadProgress", static: false, private: false, access: { has: obj => "emitUploadProgress" in obj, get: obj => obj.emitUploadProgress }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
125
|
+
__esDecorate(this, null, _emitUploadComplete_decorators, { kind: "method", name: "emitUploadComplete", static: false, private: false, access: { has: obj => "emitUploadComplete" in obj, get: obj => obj.emitUploadComplete }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
126
|
+
__esDecorate(this, null, _emitUploadError_decorators, { kind: "method", name: "emitUploadError", static: false, private: false, access: { has: obj => "emitUploadError" in obj, get: obj => obj.emitUploadError }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
127
|
+
__esDecorate(this, null, _emitUploadPause_decorators, { kind: "method", name: "emitUploadPause", static: false, private: false, access: { has: obj => "emitUploadPause" in obj, get: obj => obj.emitUploadPause }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
128
|
+
__esDecorate(this, null, _emitError_decorators, { kind: "method", name: "emitError", static: false, private: false, access: { has: obj => "emitError" in obj, get: obj => obj.emitError }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
129
|
+
__esDecorate(this, null, _cleanup_decorators, { kind: "method", name: "cleanup", static: false, private: false, access: { has: obj => "cleanup" in obj, get: obj => obj.cleanup }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
130
|
+
__esDecorate(null, null, _accept_decorators, { kind: "field", name: "accept", static: false, private: false, access: { has: obj => "accept" in obj, get: obj => obj.accept, set: (obj, value) => { obj.accept = value; } }, metadata: _metadata }, _accept_initializers, _accept_extraInitializers);
|
|
131
|
+
__esDecorate(null, null, _multiple_decorators, { kind: "field", name: "multiple", static: false, private: false, access: { has: obj => "multiple" in obj, get: obj => obj.multiple, set: (obj, value) => { obj.multiple = value; } }, metadata: _metadata }, _multiple_initializers, _multiple_extraInitializers);
|
|
132
|
+
__esDecorate(null, null, _disabled_decorators, { kind: "field", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
|
|
133
|
+
__esDecorate(null, null, _maxSize_decorators, { kind: "field", name: "maxSize", static: false, private: false, access: { has: obj => "maxSize" in obj, get: obj => obj.maxSize, set: (obj, value) => { obj.maxSize = value; } }, metadata: _metadata }, _maxSize_initializers, _maxSize_extraInitializers);
|
|
134
|
+
__esDecorate(null, null, _maxFiles_decorators, { kind: "field", name: "maxFiles", static: false, private: false, access: { has: obj => "maxFiles" in obj, get: obj => obj.maxFiles, set: (obj, value) => { obj.maxFiles = value; } }, metadata: _metadata }, _maxFiles_initializers, _maxFiles_extraInitializers);
|
|
135
|
+
__esDecorate(null, null, _view_decorators, { kind: "field", name: "view", static: false, private: false, access: { has: obj => "view" in obj, get: obj => obj.view, set: (obj, value) => { obj.view = value; } }, metadata: _metadata }, _view_initializers, _view_extraInitializers);
|
|
136
|
+
__esDecorate(null, null, _showProgress_decorators, { kind: "field", name: "showProgress", static: false, private: false, access: { has: obj => "showProgress" in obj, get: obj => obj.showProgress, set: (obj, value) => { obj.showProgress = value; } }, metadata: _metadata }, _showProgress_initializers, _showProgress_extraInitializers);
|
|
137
|
+
__esDecorate(null, null, _allowPause_decorators, { kind: "field", name: "allowPause", static: false, private: false, access: { has: obj => "allowPause" in obj, get: obj => obj.allowPause, set: (obj, value) => { obj.allowPause = value; } }, metadata: _metadata }, _allowPause_initializers, _allowPause_extraInitializers);
|
|
138
|
+
__esDecorate(null, null, _allowDelete_decorators, { kind: "field", name: "allowDelete", static: false, private: false, access: { has: obj => "allowDelete" in obj, get: obj => obj.allowDelete, set: (obj, value) => { obj.allowDelete = value; } }, metadata: _metadata }, _allowDelete_initializers, _allowDelete_extraInitializers);
|
|
139
|
+
__esDecorate(null, null, _autoUpload_decorators, { kind: "field", name: "autoUpload", static: false, private: false, access: { has: obj => "autoUpload" in obj, get: obj => obj.autoUpload, set: (obj, value) => { obj.autoUpload = value; } }, metadata: _metadata }, _autoUpload_initializers, _autoUpload_extraInitializers);
|
|
140
|
+
__esDecorate(null, null, _showAddButton_decorators, { kind: "field", name: "showAddButton", static: false, private: false, access: { has: obj => "showAddButton" in obj, get: obj => obj.showAddButton, set: (obj, value) => { obj.showAddButton = value; } }, metadata: _metadata }, _showAddButton_initializers, _showAddButton_extraInitializers);
|
|
141
|
+
__esDecorate(null, null, _hideAddButton_decorators, { kind: "field", name: "hideAddButton", static: false, private: false, access: { has: obj => "hideAddButton" in obj, get: obj => obj.hideAddButton, set: (obj, value) => { obj.hideAddButton = value; } }, metadata: _metadata }, _hideAddButton_initializers, _hideAddButton_extraInitializers);
|
|
142
|
+
__esDecorate(null, null, _input_decorators, { kind: "field", name: "input", static: false, private: false, access: { has: obj => "input" in obj, get: obj => obj.input, set: (obj, value) => { obj.input = value; } }, metadata: _metadata }, _input_initializers, _input_extraInitializers);
|
|
143
|
+
__esDecorate(null, null, _dropZone_decorators, { kind: "field", name: "dropZone", static: false, private: false, access: { has: obj => "dropZone" in obj, get: obj => obj.dropZone, set: (obj, value) => { obj.dropZone = value; } }, metadata: _metadata }, _dropZone_initializers, _dropZone_extraInitializers);
|
|
144
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
145
|
+
_classThis = _classDescriptor.value;
|
|
146
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
147
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
148
|
+
}
|
|
149
|
+
async *uploadFile(request) {
|
|
150
|
+
const response = await (yield request);
|
|
151
|
+
return response;
|
|
152
|
+
}
|
|
153
|
+
get files() {
|
|
154
|
+
return [...this.galleryFiles];
|
|
155
|
+
}
|
|
156
|
+
get customActions() {
|
|
157
|
+
return [...this.galleryCustomActions];
|
|
158
|
+
}
|
|
159
|
+
getFile(fileId) {
|
|
160
|
+
return this.galleryFiles.find(f => f.id === fileId);
|
|
161
|
+
}
|
|
162
|
+
getCustomAction(actionId) {
|
|
163
|
+
return this.galleryCustomActions.find(a => a.id === actionId);
|
|
164
|
+
}
|
|
165
|
+
isPending(fileId) {
|
|
166
|
+
const file = this.getFile(fileId);
|
|
167
|
+
return file?.uploadStatus === 'pending';
|
|
168
|
+
}
|
|
169
|
+
isUploading(fileId) {
|
|
170
|
+
const file = this.getFile(fileId);
|
|
171
|
+
return file?.uploadStatus === 'uploading';
|
|
172
|
+
}
|
|
173
|
+
isPaused(fileId) {
|
|
174
|
+
const file = this.getFile(fileId);
|
|
175
|
+
return file?.uploadStatus === 'paused';
|
|
176
|
+
}
|
|
177
|
+
isCompleted(fileId) {
|
|
178
|
+
const file = this.getFile(fileId);
|
|
179
|
+
return file?.uploadStatus === 'completed';
|
|
180
|
+
}
|
|
181
|
+
hasError(fileId) {
|
|
182
|
+
const file = this.getFile(fileId);
|
|
183
|
+
return file?.uploadStatus === 'error';
|
|
184
|
+
}
|
|
185
|
+
canAddFiles() {
|
|
186
|
+
return this.maxFiles < 0 || this.galleryFiles.length < this.maxFiles;
|
|
187
|
+
}
|
|
188
|
+
setupComponent() {
|
|
189
|
+
// Use microtask to ensure render has completed
|
|
190
|
+
queueMicrotask(() => {
|
|
191
|
+
this.galleryContainer = this.shadowRoot?.querySelector('.gallery');
|
|
192
|
+
this.galleryHeader = this.shadowRoot?.querySelector('.gallery-header');
|
|
193
|
+
this.dropZone = this.shadowRoot?.querySelector('.drop-zone');
|
|
194
|
+
// Always render initial gallery to show add button or files
|
|
195
|
+
this.updateGalleryDOM();
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
updateGalleryDOM() {
|
|
199
|
+
if (!this.galleryContainer)
|
|
200
|
+
return;
|
|
201
|
+
this.galleryContainer.innerHTML = '';
|
|
202
|
+
// Add file items
|
|
203
|
+
for (const file of this.galleryFiles) {
|
|
204
|
+
const elem = this.createFileItem(file);
|
|
205
|
+
this.galleryContainer.appendChild(elem);
|
|
206
|
+
}
|
|
207
|
+
// Add "add files" button if in that mode (unless explicitly hidden)
|
|
208
|
+
if (this.showAddButton && !this.hideAddButton) {
|
|
209
|
+
const addBtn = this.createAddButton();
|
|
210
|
+
this.galleryContainer.appendChild(addBtn);
|
|
211
|
+
}
|
|
212
|
+
// Add custom action buttons
|
|
213
|
+
for (const action of this.galleryCustomActions) {
|
|
214
|
+
const elem = this.createCustomAction(action);
|
|
215
|
+
this.galleryContainer.appendChild(elem);
|
|
216
|
+
}
|
|
217
|
+
// Update header
|
|
218
|
+
this.updateHeaderDOM();
|
|
219
|
+
}
|
|
220
|
+
updateHeaderDOM() {
|
|
221
|
+
if (!this.galleryHeader)
|
|
222
|
+
return;
|
|
223
|
+
if (this.galleryFiles.length === 0) {
|
|
224
|
+
this.galleryHeader.innerHTML = '';
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
this.galleryHeader.innerHTML = `
|
|
228
|
+
<div class="gallery-title">${this.galleryFiles.length} file${this.galleryFiles.length !== 1 ? 's' : ''}</div>
|
|
229
|
+
<div class="gallery-actions">
|
|
230
|
+
<button class="gallery-action-button" data-action="toggle-view" title="Toggle view">
|
|
231
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
232
|
+
${this.view === 'grid' ? `
|
|
233
|
+
<rect x="3" y="3" width="7" height="7" stroke-width="2"/>
|
|
234
|
+
<rect x="14" y="3" width="7" height="7" stroke-width="2"/>
|
|
235
|
+
<rect x="3" y="14" width="7" height="7" stroke-width="2"/>
|
|
236
|
+
<rect x="14" y="14" width="7" height="7" stroke-width="2"/>
|
|
237
|
+
` : `
|
|
238
|
+
<line x1="8" y1="6" x2="21" y2="6" stroke-width="2" stroke-linecap="round"/>
|
|
239
|
+
<line x1="8" y1="12" x2="21" y2="12" stroke-width="2" stroke-linecap="round"/>
|
|
240
|
+
<line x1="8" y1="18" x2="21" y2="18" stroke-width="2" stroke-linecap="round"/>
|
|
241
|
+
<line x1="3" y1="6" x2="3.01" y2="6" stroke-width="2" stroke-linecap="round"/>
|
|
242
|
+
<line x1="3" y1="12" x2="3.01" y2="12" stroke-width="2" stroke-linecap="round"/>
|
|
243
|
+
<line x1="3" y1="18" x2="3.01" y2="18" stroke-width="2" stroke-linecap="round"/>
|
|
244
|
+
`}
|
|
245
|
+
</svg>
|
|
246
|
+
</button>
|
|
247
|
+
${this.allowDelete ? `
|
|
248
|
+
<button class="gallery-action-button" data-action="clear-all" title="Clear all">
|
|
249
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
250
|
+
<polyline points="3 6 5 6 21 6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
251
|
+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
252
|
+
</svg>
|
|
253
|
+
</button>
|
|
254
|
+
` : ''}
|
|
255
|
+
</div>
|
|
256
|
+
`;
|
|
257
|
+
// Attach event listeners
|
|
258
|
+
const toggleBtn = this.galleryHeader.querySelector('[data-action="toggle-view"]');
|
|
259
|
+
toggleBtn?.addEventListener('click', () => this.handleViewToggle());
|
|
260
|
+
const clearBtn = this.galleryHeader.querySelector('[data-action="clear-all"]');
|
|
261
|
+
clearBtn?.addEventListener('click', () => this.handleClearAll());
|
|
262
|
+
}
|
|
263
|
+
updateFileItemDOM(fileId) {
|
|
264
|
+
if (!this.galleryContainer)
|
|
265
|
+
return;
|
|
266
|
+
const file = this.galleryFiles.find(f => f.id === fileId);
|
|
267
|
+
if (!file)
|
|
268
|
+
return;
|
|
269
|
+
const existingElem = this.galleryContainer.querySelector(`[data-file-id="${fileId}"]`);
|
|
270
|
+
if (!existingElem)
|
|
271
|
+
return;
|
|
272
|
+
const newElem = this.createFileItem(file);
|
|
273
|
+
existingElem.replaceWith(newElem);
|
|
274
|
+
}
|
|
275
|
+
createFileItem(file) {
|
|
276
|
+
const item = document.createElement('div');
|
|
277
|
+
item.className = `gallery-item gallery-item--${file.uploadStatus}`;
|
|
278
|
+
item.setAttribute('data-file-id', file.id);
|
|
279
|
+
const isImage = file.file.type.startsWith('image/');
|
|
280
|
+
const canPause = this.allowPause && file.uploadStatus === 'uploading';
|
|
281
|
+
const canResume = this.allowPause && file.uploadStatus === 'paused';
|
|
282
|
+
const canRetry = file.uploadStatus === 'error';
|
|
283
|
+
item.innerHTML = `
|
|
284
|
+
<div class="gallery-item-preview">
|
|
285
|
+
${isImage && file.preview ? `
|
|
286
|
+
<img src="${file.preview}" alt="${file.file.name}" class="gallery-item-image" />
|
|
287
|
+
` : `
|
|
288
|
+
<div class="gallery-item-placeholder">
|
|
289
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
290
|
+
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
291
|
+
<polyline points="13 2 13 9 20 9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
292
|
+
</svg>
|
|
293
|
+
</div>
|
|
294
|
+
`}
|
|
295
|
+
${file.badge ? `
|
|
296
|
+
<div class="gallery-item-badge gallery-item-badge--${file.badgePosition || 'top-right'}">
|
|
297
|
+
${file.badge}
|
|
298
|
+
</div>
|
|
299
|
+
` : ''}
|
|
300
|
+
${this.showProgress && file.uploadStatus === 'uploading' ? `
|
|
301
|
+
<div class="gallery-item-progress">
|
|
302
|
+
<div class="gallery-item-progress-bar" style="width: ${file.uploadProgress}%"></div>
|
|
303
|
+
</div>
|
|
304
|
+
` : ''}
|
|
305
|
+
${file.uploadStatus === 'completed' ? `
|
|
306
|
+
<div class="gallery-item-status gallery-item-status--success">
|
|
307
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
308
|
+
<polyline points="20 6 9 17 4 12" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
309
|
+
</svg>
|
|
310
|
+
</div>
|
|
311
|
+
` : ''}
|
|
312
|
+
${file.uploadStatus === 'error' ? `
|
|
313
|
+
<div class="gallery-item-status gallery-item-status--error">
|
|
314
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
315
|
+
<circle cx="12" cy="12" r="10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
316
|
+
<line x1="12" y1="8" x2="12" y2="12" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
317
|
+
<line x1="12" y1="16" x2="12.01" y2="16" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
318
|
+
</svg>
|
|
319
|
+
</div>
|
|
320
|
+
` : ''}
|
|
321
|
+
</div>
|
|
322
|
+
<div class="gallery-item-info">
|
|
323
|
+
<div class="gallery-item-name" title="${file.file.name}">
|
|
324
|
+
${file.file.name}
|
|
325
|
+
</div>
|
|
326
|
+
<div class="gallery-item-meta">
|
|
327
|
+
<span class="gallery-item-size">${this.formatFileSize(file.file.size)}</span>
|
|
328
|
+
${file.uploadStatus === 'uploading' && this.showProgress ? `
|
|
329
|
+
<span class="gallery-item-progress-text">${file.uploadProgress}%</span>
|
|
330
|
+
` : ''}
|
|
331
|
+
${file.uploadStatus === 'error' && file.error ? `
|
|
332
|
+
<span class="gallery-item-error" title="${file.error}">Upload failed</span>
|
|
333
|
+
` : ''}
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
<div class="gallery-item-actions">
|
|
337
|
+
${canPause ? `
|
|
338
|
+
<button class="gallery-item-action" data-action="pause" title="Pause upload">
|
|
339
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
340
|
+
<rect x="6" y="4" width="4" height="16" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
341
|
+
<rect x="14" y="4" width="4" height="16" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
342
|
+
</svg>
|
|
343
|
+
</button>
|
|
344
|
+
` : ''}
|
|
345
|
+
${canResume ? `
|
|
346
|
+
<button class="gallery-item-action" data-action="resume" title="Resume upload">
|
|
347
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
348
|
+
<polygon points="5 3 19 12 5 21 5 3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
349
|
+
</svg>
|
|
350
|
+
</button>
|
|
351
|
+
` : ''}
|
|
352
|
+
${canRetry ? `
|
|
353
|
+
<button class="gallery-item-action" data-action="retry" title="Retry upload">
|
|
354
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
355
|
+
<polyline points="23 4 23 10 17 10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
356
|
+
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
357
|
+
</svg>
|
|
358
|
+
</button>
|
|
359
|
+
` : ''}
|
|
360
|
+
${this.allowDelete ? `
|
|
361
|
+
<button class="gallery-item-action gallery-item-action--delete" data-action="delete" title="Remove file">
|
|
362
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
363
|
+
<line x1="18" y1="6" x2="6" y2="18" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
364
|
+
<line x1="6" y1="6" x2="18" y2="18" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
365
|
+
</svg>
|
|
366
|
+
</button>
|
|
367
|
+
` : ''}
|
|
368
|
+
</div>
|
|
369
|
+
`;
|
|
370
|
+
// Attach event listeners
|
|
371
|
+
const pauseBtn = item.querySelector('[data-action="pause"]');
|
|
372
|
+
pauseBtn?.addEventListener('click', () => this.pauseUpload(file.id));
|
|
373
|
+
const resumeBtn = item.querySelector('[data-action="resume"]');
|
|
374
|
+
resumeBtn?.addEventListener('click', () => this.resumeUpload(file.id));
|
|
375
|
+
const retryBtn = item.querySelector('[data-action="retry"]');
|
|
376
|
+
retryBtn?.addEventListener('click', () => this.retryUpload(file.id));
|
|
377
|
+
const deleteBtn = item.querySelector('[data-action="delete"]');
|
|
378
|
+
deleteBtn?.addEventListener('click', () => this.removeFile(file.id));
|
|
379
|
+
return item;
|
|
380
|
+
}
|
|
381
|
+
createAddButton() {
|
|
382
|
+
const canAdd = this.maxFiles < 0 || this.galleryFiles.length < this.maxFiles;
|
|
383
|
+
const item = document.createElement('div');
|
|
384
|
+
item.className = `gallery-item gallery-item--add-button ${canAdd ? '' : 'gallery-item--disabled'}`;
|
|
385
|
+
item.title = canAdd ? 'Add files' : 'Maximum files reached';
|
|
386
|
+
item.innerHTML = `
|
|
387
|
+
<div class="gallery-item-preview">
|
|
388
|
+
<div class="gallery-item-placeholder gallery-item-add-icon">
|
|
389
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
390
|
+
<line x1="12" y1="5" x2="12" y2="19" stroke-width="2" stroke-linecap="round"/>
|
|
391
|
+
<line x1="5" y1="12" x2="19" y2="12" stroke-width="2" stroke-linecap="round"/>
|
|
392
|
+
</svg>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
<div class="gallery-item-info">
|
|
396
|
+
<div class="gallery-item-name">Add files</div>
|
|
397
|
+
</div>
|
|
398
|
+
`;
|
|
399
|
+
if (canAdd) {
|
|
400
|
+
item.addEventListener('click', () => this.handleDropZoneClick());
|
|
401
|
+
}
|
|
402
|
+
return item;
|
|
403
|
+
}
|
|
404
|
+
createCustomAction(action) {
|
|
405
|
+
const item = document.createElement('div');
|
|
406
|
+
item.className = 'gallery-item gallery-item--add-button';
|
|
407
|
+
item.title = action.text;
|
|
408
|
+
const iconContent = action.icon
|
|
409
|
+
.replace(/<svg[^>]*>/, '')
|
|
410
|
+
.replace(/<\/svg>$/, '')
|
|
411
|
+
.trim();
|
|
412
|
+
item.innerHTML = `
|
|
413
|
+
<div class="gallery-item-preview">
|
|
414
|
+
<div class="gallery-item-placeholder gallery-item-add-icon">
|
|
415
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
416
|
+
${iconContent}
|
|
417
|
+
</svg>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
<div class="gallery-item-info">
|
|
421
|
+
<div class="gallery-item-name">${action.text}</div>
|
|
422
|
+
</div>
|
|
423
|
+
`;
|
|
424
|
+
item.addEventListener('click', () => this.handleCustomActionClick(action.id));
|
|
425
|
+
return item;
|
|
426
|
+
}
|
|
427
|
+
renderContent() {
|
|
428
|
+
const dropZoneClasses = [
|
|
429
|
+
'drop-zone',
|
|
430
|
+
this.disabled ? 'drop-zone--disabled' : '',
|
|
431
|
+
this.isDragOver ? 'drop-zone--drag-over' : '',
|
|
432
|
+
this.galleryFiles.length > 0 ? 'drop-zone--has-files' : ''
|
|
433
|
+
].filter(Boolean).join(' ');
|
|
434
|
+
return html `
|
|
435
|
+
<div class="file-gallery">
|
|
436
|
+
<input
|
|
437
|
+
type="file"
|
|
438
|
+
class="file-input"
|
|
439
|
+
accept="${this.accept}"
|
|
440
|
+
?multiple=${this.multiple}
|
|
441
|
+
?disabled=${this.disabled}
|
|
442
|
+
@change=${this.handleFileSelect}
|
|
443
|
+
/>
|
|
444
|
+
|
|
445
|
+
<if ${!this.showAddButton}>
|
|
446
|
+
<div
|
|
447
|
+
class="${dropZoneClasses}"
|
|
448
|
+
@click=${() => this.handleDropZoneClick()}
|
|
449
|
+
@dragenter=${this.handleDragEnter}
|
|
450
|
+
@dragleave=${this.handleDragLeave}
|
|
451
|
+
@dragover=${this.handleDragOver}
|
|
452
|
+
@drop=${this.handleDrop}
|
|
453
|
+
>
|
|
454
|
+
<div class="drop-zone-content">
|
|
455
|
+
<svg class="drop-zone-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
456
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
457
|
+
</svg>
|
|
458
|
+
<div class="drop-zone-text">
|
|
459
|
+
<span class="drop-zone-primary">Click to upload</span>
|
|
460
|
+
<span class="drop-zone-secondary">or drag and drop</span>
|
|
461
|
+
</div>
|
|
462
|
+
<if ${this.accept}>
|
|
463
|
+
<div class="drop-zone-hint">Accepted: ${this.formatAcceptTypes()}</div>
|
|
464
|
+
</if>
|
|
465
|
+
<if ${this.maxSize > 0}>
|
|
466
|
+
<div class="drop-zone-hint">Max size: ${this.formatFileSize(this.maxSize)}</div>
|
|
467
|
+
</if>
|
|
468
|
+
</div>
|
|
469
|
+
</div>
|
|
470
|
+
</if>
|
|
471
|
+
|
|
472
|
+
<div class="gallery-header">
|
|
473
|
+
</div>
|
|
474
|
+
|
|
475
|
+
<div class="gallery gallery--${this.view}">
|
|
476
|
+
</div>
|
|
477
|
+
</div>
|
|
478
|
+
`;
|
|
479
|
+
}
|
|
480
|
+
componentStyles() {
|
|
481
|
+
return css `${cssContent}`;
|
|
482
|
+
}
|
|
483
|
+
handleDropZoneClick() {
|
|
484
|
+
if (this.disabled)
|
|
485
|
+
return;
|
|
486
|
+
// Query input if not already available
|
|
487
|
+
if (!this.input) {
|
|
488
|
+
this.input = this.shadowRoot?.querySelector('.file-input');
|
|
489
|
+
}
|
|
490
|
+
this.input?.click();
|
|
491
|
+
}
|
|
492
|
+
handleFileSelect(e) {
|
|
493
|
+
const input = e.target;
|
|
494
|
+
if (input.files && input.files.length > 0) {
|
|
495
|
+
this.addFiles(input.files);
|
|
496
|
+
input.value = ''; // Reset input
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
handleDragEnter(e) {
|
|
500
|
+
e.preventDefault();
|
|
501
|
+
if (!this.disabled && this.dropZone) {
|
|
502
|
+
this.isDragOver = true;
|
|
503
|
+
this.dropZone.classList.add('drop-zone--drag-over');
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
handleDragLeave(e) {
|
|
507
|
+
e.preventDefault();
|
|
508
|
+
const rect = this.dropZone?.getBoundingClientRect();
|
|
509
|
+
if (rect && (e.clientX <= rect.left ||
|
|
510
|
+
e.clientX >= rect.right ||
|
|
511
|
+
e.clientY <= rect.top ||
|
|
512
|
+
e.clientY >= rect.bottom)) {
|
|
513
|
+
this.isDragOver = false;
|
|
514
|
+
if (this.dropZone) {
|
|
515
|
+
this.dropZone.classList.remove('drop-zone--drag-over');
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
handleDragOver(e) {
|
|
520
|
+
e.preventDefault();
|
|
521
|
+
if (e.dataTransfer) {
|
|
522
|
+
e.dataTransfer.dropEffect = this.disabled ? 'none' : 'copy';
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
handleDrop(e) {
|
|
526
|
+
e.preventDefault();
|
|
527
|
+
this.isDragOver = false;
|
|
528
|
+
if (this.dropZone) {
|
|
529
|
+
this.dropZone.classList.remove('drop-zone--drag-over');
|
|
530
|
+
}
|
|
531
|
+
if (!this.disabled && e.dataTransfer?.files) {
|
|
532
|
+
this.addFiles(e.dataTransfer.files);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
handleViewToggle() {
|
|
536
|
+
this.view = this.view === 'grid' ? 'list' : 'grid';
|
|
537
|
+
if (this.galleryContainer) {
|
|
538
|
+
this.galleryContainer.className = `gallery gallery--${this.view}`;
|
|
539
|
+
}
|
|
540
|
+
this.updateHeaderDOM();
|
|
541
|
+
}
|
|
542
|
+
handleClearAll() {
|
|
543
|
+
if (confirm('Remove all files?')) {
|
|
544
|
+
this.clear();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
addFiles(files) {
|
|
548
|
+
const fileArray = Array.from(files);
|
|
549
|
+
// Check max files limit
|
|
550
|
+
if (this.maxFiles > 0 && this.galleryFiles.length + fileArray.length > this.maxFiles) {
|
|
551
|
+
this.emitError(`Maximum ${this.maxFiles} file${this.maxFiles !== 1 ? 's' : ''} allowed`);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
// Validate and add files
|
|
555
|
+
for (const file of fileArray) {
|
|
556
|
+
// Check file size
|
|
557
|
+
if (this.maxSize > 0 && file.size > this.maxSize) {
|
|
558
|
+
this.emitError(`File "${file.name}" exceeds maximum size of ${this.formatFileSize(this.maxSize)}`);
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
// Check file type
|
|
562
|
+
if (this.accept && !this.isAcceptedType(file)) {
|
|
563
|
+
this.emitError(`File type "${file.type}" not accepted`);
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
const galleryFile = {
|
|
567
|
+
id: this.generateFileId(),
|
|
568
|
+
file,
|
|
569
|
+
uploadProgress: 0,
|
|
570
|
+
uploadStatus: 'pending'
|
|
571
|
+
};
|
|
572
|
+
// Generate preview for images
|
|
573
|
+
if (file.type.startsWith('image/')) {
|
|
574
|
+
this.generatePreview(galleryFile);
|
|
575
|
+
}
|
|
576
|
+
this.galleryFiles.push(galleryFile);
|
|
577
|
+
if (this.autoUpload) {
|
|
578
|
+
this.startUpload(galleryFile);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
this.updateGalleryDOM();
|
|
582
|
+
this.emitFilesChange();
|
|
583
|
+
}
|
|
584
|
+
addFileWithPreview(file, previewDataUrl) {
|
|
585
|
+
// Check max files limit
|
|
586
|
+
if (this.maxFiles > 0 && this.galleryFiles.length >= this.maxFiles) {
|
|
587
|
+
this.emitError(`Maximum ${this.maxFiles} file${this.maxFiles !== 1 ? 's' : ''} allowed`);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
// Check file size
|
|
591
|
+
if (this.maxSize > 0 && file.size > this.maxSize) {
|
|
592
|
+
this.emitError(`File "${file.name}" exceeds maximum size of ${this.formatFileSize(this.maxSize)}`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
// Check file type
|
|
596
|
+
if (this.accept && !this.isAcceptedType(file)) {
|
|
597
|
+
this.emitError(`File type "${file.type}" not accepted`);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const galleryFile = {
|
|
601
|
+
id: this.generateFileId(),
|
|
602
|
+
file,
|
|
603
|
+
preview: previewDataUrl,
|
|
604
|
+
uploadProgress: 0,
|
|
605
|
+
uploadStatus: 'pending'
|
|
606
|
+
};
|
|
607
|
+
this.galleryFiles.push(galleryFile);
|
|
608
|
+
if (this.autoUpload) {
|
|
609
|
+
this.startUpload(galleryFile);
|
|
610
|
+
}
|
|
611
|
+
this.updateGalleryDOM();
|
|
612
|
+
this.emitFilesChange();
|
|
613
|
+
}
|
|
614
|
+
removeFile(fileId) {
|
|
615
|
+
const index = this.galleryFiles.findIndex(f => f.id === fileId);
|
|
616
|
+
if (index === -1)
|
|
617
|
+
return;
|
|
618
|
+
const file = this.galleryFiles[index];
|
|
619
|
+
// Cancel upload if in progress
|
|
620
|
+
if (file.uploadStatus === 'uploading' || file.uploadStatus === 'paused') {
|
|
621
|
+
this.uploadAbortControllers.get(fileId)?.abort();
|
|
622
|
+
this.uploadAbortControllers.delete(fileId);
|
|
623
|
+
}
|
|
624
|
+
// Revoke preview URL
|
|
625
|
+
if (file.preview) {
|
|
626
|
+
URL.revokeObjectURL(file.preview);
|
|
627
|
+
}
|
|
628
|
+
this.galleryFiles.splice(index, 1);
|
|
629
|
+
this.updateGalleryDOM();
|
|
630
|
+
this.emitFilesChange();
|
|
631
|
+
this.emitFileRemove(file);
|
|
632
|
+
}
|
|
633
|
+
pauseUpload(fileId) {
|
|
634
|
+
const file = this.galleryFiles.find(f => f.id === fileId);
|
|
635
|
+
if (!file || file.uploadStatus !== 'uploading')
|
|
636
|
+
return;
|
|
637
|
+
this.uploadAbortControllers.get(fileId)?.abort();
|
|
638
|
+
this.uploadAbortControllers.delete(fileId);
|
|
639
|
+
file.uploadStatus = 'paused';
|
|
640
|
+
this.updateFileItemDOM(fileId);
|
|
641
|
+
this.emitUploadPause(file);
|
|
642
|
+
}
|
|
643
|
+
resumeUpload(fileId) {
|
|
644
|
+
const file = this.galleryFiles.find(f => f.id === fileId);
|
|
645
|
+
if (!file || file.uploadStatus !== 'paused')
|
|
646
|
+
return;
|
|
647
|
+
this.startUpload(file);
|
|
648
|
+
}
|
|
649
|
+
retryUpload(fileId) {
|
|
650
|
+
const file = this.galleryFiles.find(f => f.id === fileId);
|
|
651
|
+
if (!file || file.uploadStatus !== 'error')
|
|
652
|
+
return;
|
|
653
|
+
file.uploadProgress = 0;
|
|
654
|
+
file.error = undefined;
|
|
655
|
+
this.startUpload(file);
|
|
656
|
+
}
|
|
657
|
+
clear() {
|
|
658
|
+
// Cancel all uploads
|
|
659
|
+
for (const controller of this.uploadAbortControllers.values()) {
|
|
660
|
+
controller.abort();
|
|
661
|
+
}
|
|
662
|
+
this.uploadAbortControllers.clear();
|
|
663
|
+
// Revoke all preview URLs
|
|
664
|
+
for (const file of this.galleryFiles) {
|
|
665
|
+
if (file.preview) {
|
|
666
|
+
URL.revokeObjectURL(file.preview);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
this.galleryFiles = [];
|
|
670
|
+
this.updateGalleryDOM();
|
|
671
|
+
this.emitFilesChange();
|
|
672
|
+
}
|
|
673
|
+
addCustomAction(icon, text) {
|
|
674
|
+
const id = `action-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
675
|
+
this.galleryCustomActions.push({ id, icon, text });
|
|
676
|
+
this.updateGalleryDOM();
|
|
677
|
+
return id;
|
|
678
|
+
}
|
|
679
|
+
removeCustomAction(actionId) {
|
|
680
|
+
const index = this.galleryCustomActions.findIndex(a => a.id === actionId);
|
|
681
|
+
if (index !== -1) {
|
|
682
|
+
this.galleryCustomActions.splice(index, 1);
|
|
683
|
+
this.updateGalleryDOM();
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
clearCustomActions() {
|
|
687
|
+
this.galleryCustomActions = [];
|
|
688
|
+
this.updateGalleryDOM();
|
|
689
|
+
}
|
|
690
|
+
clearCompleted() {
|
|
691
|
+
const completed = this.galleryFiles.filter(f => f.uploadStatus === 'completed');
|
|
692
|
+
for (const file of completed) {
|
|
693
|
+
this.removeFile(file.id);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
clearErrors() {
|
|
697
|
+
const errors = this.galleryFiles.filter(f => f.uploadStatus === 'error');
|
|
698
|
+
for (const file of errors) {
|
|
699
|
+
this.removeFile(file.id);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
pauseAll() {
|
|
703
|
+
for (const file of this.galleryFiles) {
|
|
704
|
+
if (file.uploadStatus === 'uploading') {
|
|
705
|
+
this.pauseUpload(file.id);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
resumeAll() {
|
|
710
|
+
for (const file of this.galleryFiles) {
|
|
711
|
+
if (file.uploadStatus === 'paused') {
|
|
712
|
+
this.resumeUpload(file.id);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
retryAll() {
|
|
717
|
+
for (const file of this.galleryFiles) {
|
|
718
|
+
if (file.uploadStatus === 'error') {
|
|
719
|
+
this.retryUpload(file.id);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
cancelUpload(fileId) {
|
|
724
|
+
const controller = this.uploadAbortControllers.get(fileId);
|
|
725
|
+
if (controller) {
|
|
726
|
+
controller.abort();
|
|
727
|
+
this.uploadAbortControllers.delete(fileId);
|
|
728
|
+
}
|
|
729
|
+
this.removeFile(fileId);
|
|
730
|
+
}
|
|
731
|
+
cancelAll() {
|
|
732
|
+
const uploading = this.galleryFiles.filter(f => f.uploadStatus === 'uploading' || f.uploadStatus === 'paused' || f.uploadStatus === 'pending');
|
|
733
|
+
for (const file of uploading) {
|
|
734
|
+
this.cancelUpload(file.id);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
openFilePicker() {
|
|
738
|
+
this.handleDropZoneClick();
|
|
739
|
+
}
|
|
740
|
+
setFileBadge(fileId, badge, position = 'top-right') {
|
|
741
|
+
const file = this.galleryFiles.find(f => f.id === fileId);
|
|
742
|
+
if (file) {
|
|
743
|
+
file.badge = badge;
|
|
744
|
+
file.badgePosition = position;
|
|
745
|
+
this.updateFileItemDOM(fileId);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
removeFileBadge(fileId) {
|
|
749
|
+
const file = this.galleryFiles.find(f => f.id === fileId);
|
|
750
|
+
if (file) {
|
|
751
|
+
file.badge = undefined;
|
|
752
|
+
file.badgePosition = undefined;
|
|
753
|
+
this.updateFileItemDOM(fileId);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
handleCustomActionClick(actionId) {
|
|
757
|
+
this.dispatchEvent(new CustomEvent('@snice/custom-action-click', {
|
|
758
|
+
detail: { actionId, component: this },
|
|
759
|
+
bubbles: true,
|
|
760
|
+
composed: true
|
|
761
|
+
}));
|
|
762
|
+
}
|
|
763
|
+
async startUpload(galleryFile) {
|
|
764
|
+
const controller = new AbortController();
|
|
765
|
+
this.uploadAbortControllers.set(galleryFile.id, controller);
|
|
766
|
+
galleryFile.uploadStatus = 'uploading';
|
|
767
|
+
galleryFile.uploadProgress = 0;
|
|
768
|
+
galleryFile.error = undefined;
|
|
769
|
+
this.updateFileItemDOM(galleryFile.id);
|
|
770
|
+
try {
|
|
771
|
+
const response = await this.uploadFile({
|
|
772
|
+
file: galleryFile.file,
|
|
773
|
+
fileId: galleryFile.id,
|
|
774
|
+
onProgress: (progress) => {
|
|
775
|
+
galleryFile.uploadProgress = Math.round(progress * 100);
|
|
776
|
+
this.updateFileItemDOM(galleryFile.id);
|
|
777
|
+
this.emitUploadProgress(galleryFile);
|
|
778
|
+
},
|
|
779
|
+
signal: controller.signal
|
|
780
|
+
});
|
|
781
|
+
if (response.success) {
|
|
782
|
+
galleryFile.uploadStatus = 'completed';
|
|
783
|
+
galleryFile.uploadProgress = 100;
|
|
784
|
+
this.emitUploadComplete(galleryFile, response);
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
throw new Error(response.error || 'Upload failed');
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
if (error.name === 'AbortError') {
|
|
792
|
+
// Upload was paused, don't treat as error
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
galleryFile.uploadStatus = 'error';
|
|
796
|
+
galleryFile.error = error.message || 'Upload failed';
|
|
797
|
+
this.emitUploadError(galleryFile, error);
|
|
798
|
+
}
|
|
799
|
+
finally {
|
|
800
|
+
this.uploadAbortControllers.delete(galleryFile.id);
|
|
801
|
+
this.updateFileItemDOM(galleryFile.id);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
generatePreview(galleryFile) {
|
|
805
|
+
const reader = new FileReader();
|
|
806
|
+
reader.onload = (e) => {
|
|
807
|
+
galleryFile.preview = e.target?.result;
|
|
808
|
+
this.updateFileItemDOM(galleryFile.id);
|
|
809
|
+
};
|
|
810
|
+
reader.readAsDataURL(galleryFile.file);
|
|
811
|
+
}
|
|
812
|
+
generateFileId() {
|
|
813
|
+
return `file-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
814
|
+
}
|
|
815
|
+
isAcceptedType(file) {
|
|
816
|
+
if (!this.accept)
|
|
817
|
+
return true;
|
|
818
|
+
if (!file || !file.name)
|
|
819
|
+
return false;
|
|
820
|
+
const acceptTypes = this.accept.split(',').map(t => t.trim()).filter(Boolean);
|
|
821
|
+
for (const acceptType of acceptTypes) {
|
|
822
|
+
// Exact MIME type match
|
|
823
|
+
if (acceptType === file.type)
|
|
824
|
+
return true;
|
|
825
|
+
// Wildcard match (e.g., image/*)
|
|
826
|
+
if (acceptType.endsWith('/*')) {
|
|
827
|
+
const baseType = acceptType.split('/')[0];
|
|
828
|
+
if (file.type && file.type.startsWith(baseType + '/'))
|
|
829
|
+
return true;
|
|
830
|
+
}
|
|
831
|
+
// Extension match (e.g., .jpg)
|
|
832
|
+
if (acceptType.startsWith('.')) {
|
|
833
|
+
if (file.name.toLowerCase().endsWith(acceptType.toLowerCase()))
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
formatAcceptTypes() {
|
|
840
|
+
if (!this.accept)
|
|
841
|
+
return '';
|
|
842
|
+
return this.accept.split(',').map(t => t.trim()).join(', ');
|
|
843
|
+
}
|
|
844
|
+
formatFileSize(bytes) {
|
|
845
|
+
if (bytes === 0)
|
|
846
|
+
return '0 B';
|
|
847
|
+
if (bytes < 0)
|
|
848
|
+
return '';
|
|
849
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
850
|
+
const k = 1024;
|
|
851
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
852
|
+
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${units[i]}`;
|
|
853
|
+
}
|
|
854
|
+
emitFilesChange() {
|
|
855
|
+
return { files: this.files, component: this };
|
|
856
|
+
}
|
|
857
|
+
emitFileRemove(file) {
|
|
858
|
+
return { file, component: this };
|
|
859
|
+
}
|
|
860
|
+
emitUploadProgress(file) {
|
|
861
|
+
return { file, progress: file.uploadProgress, component: this };
|
|
862
|
+
}
|
|
863
|
+
emitUploadComplete(file, response) {
|
|
864
|
+
return { file, response, component: this };
|
|
865
|
+
}
|
|
866
|
+
emitUploadError(file, error) {
|
|
867
|
+
return { file, error: error.message, component: this };
|
|
868
|
+
}
|
|
869
|
+
emitUploadPause(file) {
|
|
870
|
+
return { file, component: this };
|
|
871
|
+
}
|
|
872
|
+
emitError(message) {
|
|
873
|
+
return { message, component: this };
|
|
874
|
+
}
|
|
875
|
+
cleanup() {
|
|
876
|
+
// Cancel all uploads
|
|
877
|
+
for (const controller of this.uploadAbortControllers.values()) {
|
|
878
|
+
controller.abort();
|
|
879
|
+
}
|
|
880
|
+
// Revoke all preview URLs
|
|
881
|
+
for (const file of this.galleryFiles) {
|
|
882
|
+
if (file.preview) {
|
|
883
|
+
URL.revokeObjectURL(file.preview);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
return _classThis;
|
|
889
|
+
})();
|
|
890
|
+
|
|
891
|
+
export { SniceFileGallery };
|
|
892
|
+
//# sourceMappingURL=snice-file-gallery.js.map
|