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.
Files changed (33) hide show
  1. package/bin/snice.js +8 -0
  2. package/dist/components/file-gallery/snice-file-gallery.d.ts +87 -0
  3. package/dist/components/file-gallery/snice-file-gallery.js +892 -0
  4. package/dist/components/file-gallery/snice-file-gallery.js.map +1 -0
  5. package/dist/components/file-gallery/snice-file-gallery.types.d.ts +72 -0
  6. package/dist/components/qr-reader/qr-decoder.d.ts +20 -0
  7. package/dist/components/qr-reader/qr-decoder.js +49 -0
  8. package/dist/components/qr-reader/qr-decoder.js.map +1 -0
  9. package/dist/components/qr-reader/qr-worker.d.ts +6 -0
  10. package/dist/components/qr-reader/qr-worker.js +64 -0
  11. package/dist/components/qr-reader/qr-worker.js.map +1 -0
  12. package/dist/components/qr-reader/snice-qr-reader.d.ts +39 -0
  13. package/dist/components/qr-reader/snice-qr-reader.js +436 -0
  14. package/dist/components/qr-reader/snice-qr-reader.js.map +1 -0
  15. package/dist/components/qr-reader/snice-qr-reader.types.d.ts +17 -0
  16. package/dist/components/qr-reader/zxing-reader.mjs +1582 -0
  17. package/dist/components/qr-reader/zxing-share.mjs +305 -0
  18. package/dist/components/qr-reader/zxing_reader.wasm +0 -0
  19. package/dist/components/zxing-reader-B3Rfebg9.js +1771 -0
  20. package/dist/components/zxing-reader-B3Rfebg9.js.map +1 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.esm.js +1 -1
  23. package/dist/index.iife.js +1 -1
  24. package/dist/symbols.cjs +1 -1
  25. package/dist/symbols.esm.js +1 -1
  26. package/dist/transitions.cjs +1 -1
  27. package/dist/transitions.esm.js +1 -1
  28. package/docs/ai/README.md +1 -1
  29. package/docs/ai/components/file-gallery.md +206 -0
  30. package/docs/ai/components/qr-reader.md +80 -0
  31. package/docs/components/file-gallery.md +692 -0
  32. package/docs/components/qr-reader.md +327 -0
  33. 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