@devvit/ui-renderer 0.10.20-next-2024-04-26-62f55e1ef.0 → 0.10.20-next-2024-04-29-c515e730a.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 (37) hide show
  1. package/client/formbuilder/components/image-field/devvit-image-field.d.ts +24 -0
  2. package/client/formbuilder/components/image-field/devvit-image-field.d.ts.map +1 -0
  3. package/client/formbuilder/components/image-field/devvit-image-field.js +158 -0
  4. package/client/formbuilder/components/image-field/drag-and-drop-controller.d.ts +19 -0
  5. package/client/formbuilder/components/image-field/drag-and-drop-controller.d.ts.map +1 -0
  6. package/client/formbuilder/components/image-field/drag-and-drop-controller.js +113 -0
  7. package/client/formbuilder/components/image-field/media-controller.d.ts +59 -0
  8. package/client/formbuilder/components/image-field/media-controller.d.ts.map +1 -0
  9. package/client/formbuilder/components/image-field/media-controller.js +207 -0
  10. package/client/formbuilder/components/image-field/render-empty-state.d.ts +15 -0
  11. package/client/formbuilder/components/image-field/render-empty-state.d.ts.map +1 -0
  12. package/client/formbuilder/components/image-field/render-empty-state.js +46 -0
  13. package/client/formbuilder/components/image-field/render-loading-state.d.ts +9 -0
  14. package/client/formbuilder/components/image-field/render-loading-state.d.ts.map +1 -0
  15. package/client/formbuilder/components/image-field/render-loading-state.js +27 -0
  16. package/client/formbuilder/components/image-field/render-media-carousel.d.ts +5 -0
  17. package/client/formbuilder/components/image-field/render-media-carousel.d.ts.map +1 -0
  18. package/client/formbuilder/components/image-field/render-media-carousel.js +92 -0
  19. package/client/formbuilder/components/image-field/render-media-preview.d.ts +15 -0
  20. package/client/formbuilder/components/image-field/render-media-preview.d.ts.map +1 -0
  21. package/client/formbuilder/components/image-field/render-media-preview.js +39 -0
  22. package/client/formbuilder/components/image-field/validate-image-size.d.ts +14 -0
  23. package/client/formbuilder/components/image-field/validate-image-size.d.ts.map +1 -0
  24. package/client/formbuilder/components/image-field/validate-image-size.js +47 -0
  25. package/client/formbuilder/components/image-field/validateMedia.d.ts +13 -0
  26. package/client/formbuilder/components/image-field/validateMedia.d.ts.map +1 -0
  27. package/client/formbuilder/components/image-field/validateMedia.js +29 -0
  28. package/client/formbuilder/fields/renderFieldLabel.d.ts +1 -1
  29. package/client/formbuilder/fields/renderFieldLabel.d.ts.map +1 -1
  30. package/client/formbuilder/fields/renderFieldLabel.js +3 -1
  31. package/client/formbuilder/fields/renderFormFields.d.ts.map +1 -1
  32. package/client/formbuilder/fields/renderFormFields.js +2 -1
  33. package/client/formbuilder/fields/renderImageField.d.ts +5 -0
  34. package/client/formbuilder/fields/renderImageField.d.ts.map +1 -0
  35. package/client/formbuilder/fields/renderImageField.js +26 -0
  36. package/package.json +8 -8
  37. package/styles.js +1 -1
@@ -0,0 +1,24 @@
1
+ import { InputElement } from '@reddit/faceplate/base-classes/input-element.js';
2
+ import { MediaController } from './media-controller.js';
3
+ import type { PropertyValueMap } from 'lit';
4
+ /**
5
+ * This entire file was copied from shreddit
6
+ * I removed some parts of it that were too "shreddit post creation" focused, but kept the original code style
7
+ * Eventually we will remove it in favour of faceplate-ui image upload component, but for now we have this
8
+ */
9
+ export type UIType = 'mobile' | 'desktop';
10
+ export declare class DevvitFormImageField extends InputElement {
11
+ #private;
12
+ /** @ignore */
13
+ static get styles(): import("lit").CSSResultGroup[];
14
+ protected firstUpdated(_changedProperties: PropertyValueMap<unknown>): void;
15
+ fileInputInnerWrapper: HTMLDivElement;
16
+ mediaController: MediaController;
17
+ get value(): string;
18
+ set value(newValue: string);
19
+ _input?: HTMLInputElement;
20
+ handleRemoveClick: () => void;
21
+ formDisabledCallback(disabled: boolean): void;
22
+ render(): import("lit-html").TemplateResult<1>;
23
+ }
24
+ //# sourceMappingURL=devvit-image-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devvit-image-field.d.ts","sourceRoot":"","sources":["../../../../../library/src/client/formbuilder/components/image-field/devvit-image-field.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,iDAAiD,CAAC;AAG/E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAMxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAC;AAE5C;;;;GAIG;AAEH,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE1C,qBAAa,oBAAqB,SAAQ,YAAY;;IAMpD,cAAc;IACd,WAAoB,MAAM,mCAEzB;IAED,SAAS,CAAC,YAAY,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,IAAI;IAO1C,qBAAqB,EAAG,cAAc,CAAC;IAIxE,eAAe,EAAE,eAAe,CAE7B;IAEH,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,QAAQ,EAJL,MAIK,EAEjB;IAID,MAAM,CAAC,EAAE,gBAAgB,CAAC;IA2B1B,iBAAiB,QAAO,IAAI,CAQ1B;IAeO,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAW7C,MAAM;CA0ChB"}
@@ -0,0 +1,158 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
11
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
12
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
13
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
14
+ };
15
+ var _DevvitFormImageField_instances, _DevvitFormImageField_dragabble, _DevvitFormImageField_handleInput, _DevvitFormImageField_handleError, _DevvitFormImageField_setRequiredFieldError, _DevvitFormImageField_handleInputWrapperClick;
16
+ import { html } from 'lit';
17
+ import { property, query } from 'lit/decorators.js';
18
+ import { msg } from '@lit/localize';
19
+ import { InputElement } from '@reddit/faceplate/base-classes/input-element.js';
20
+ import { DragAndDropController, DragState } from './drag-and-drop-controller.js';
21
+ import { MediaController } from './media-controller.js';
22
+ import { renderEmptyState } from './render-empty-state.js';
23
+ import { renderCarousel } from './render-media-carousel.js';
24
+ import { ALLOWED_IMAGE_MIMETYPES } from './validateMedia.js';
25
+ import { styles } from '../../../../styles.js';
26
+ export class DevvitFormImageField extends InputElement {
27
+ constructor() {
28
+ super(...arguments);
29
+ _DevvitFormImageField_instances.add(this);
30
+ // @ts-ignore
31
+ _DevvitFormImageField_dragabble.set(this, new DragAndDropController(this, (files) => {
32
+ void this.mediaController.handleMedia(files);
33
+ }));
34
+ this.mediaController = new MediaController(this, {
35
+ setValidation: (message) => __classPrivateFieldGet(this, _DevvitFormImageField_instances, "m", _DevvitFormImageField_handleError).call(this, message),
36
+ });
37
+ this.handleRemoveClick = () => {
38
+ this.mediaController.media = [];
39
+ if (!this.mediaController.media.length) {
40
+ __classPrivateFieldGet(this, _DevvitFormImageField_instances, "m", _DevvitFormImageField_setRequiredFieldError).call(this);
41
+ }
42
+ // @ts-ignore
43
+ this.requestUpdate();
44
+ };
45
+ _DevvitFormImageField_handleInputWrapperClick.set(this, async (event) => {
46
+ const target = event.target;
47
+ const button = target.closest('button.action');
48
+ if (!button) {
49
+ return;
50
+ }
51
+ if (button.classList.contains('remove')) {
52
+ this.handleRemoveClick();
53
+ }
54
+ else if (button.classList.contains('upload-media')) {
55
+ this._input?.click();
56
+ }
57
+ });
58
+ }
59
+ /** @ignore */
60
+ static get styles() {
61
+ return [styles, super.styles];
62
+ }
63
+ firstUpdated(_changedProperties) {
64
+ // @ts-ignore
65
+ super.firstUpdated(_changedProperties);
66
+ __classPrivateFieldGet(this, _DevvitFormImageField_dragabble, "f").initEventListeners(this.fileInputInnerWrapper);
67
+ }
68
+ get value() {
69
+ return this.mediaController.getValue();
70
+ }
71
+ set value(newValue) {
72
+ this.mediaController.setValue(newValue);
73
+ }
74
+ formDisabledCallback(disabled) {
75
+ if (disabled) {
76
+ // @ts-ignore
77
+ this.setCustomValidity('');
78
+ }
79
+ else if (!disabled && this.value?.length === 0) {
80
+ __classPrivateFieldGet(this, _DevvitFormImageField_instances, "m", _DevvitFormImageField_setRequiredFieldError).call(this);
81
+ // @ts-ignore
82
+ this.reportValidity();
83
+ }
84
+ }
85
+ render() {
86
+ const mimetypes = ALLOWED_IMAGE_MIMETYPES;
87
+ /* eslint-disable @reddit/i18n-shreddit/no-unwrapped-strings */
88
+ let containerClasses = `border-neutral-border ${this.mediaController.media.length ? 'border-solid' : 'border-dashed'}`;
89
+ if (__classPrivateFieldGet(this, _DevvitFormImageField_dragabble, "f").dragState === DragState.IN_WINDOW) {
90
+ containerClasses = 'bg-neutral-background-weak border-alienblue-600 border-dashed';
91
+ }
92
+ else if (__classPrivateFieldGet(this, _DevvitFormImageField_dragabble, "f").dragState === DragState.IN_FRAME) {
93
+ containerClasses = 'bg-neutral-background-weak border-alienblue-600 border-solid';
94
+ }
95
+ /* eslint-enable @reddit/i18n-shreddit/no-unwrapped-strings */
96
+ const HiddenInput = html `
97
+ <input
98
+ class="file-input"
99
+ hidden
100
+ type="file"
101
+ @input="${__classPrivateFieldGet(this, _DevvitFormImageField_instances, "m", _DevvitFormImageField_handleInput)}"
102
+ accept="${mimetypes.join(',')}"
103
+ />
104
+ `;
105
+ return html `
106
+ ${HiddenInput}
107
+ <div
108
+ id="fileInputInnerWrapper"
109
+ class="flex w-full border-sm rounded-dx-md overflow-hidden group min-h-[min(20vw,150px)] ${containerClasses}"
110
+ @click="${__classPrivateFieldGet(this, _DevvitFormImageField_handleInputWrapperClick, "f")}"
111
+ @keypress="${__classPrivateFieldGet(this, _DevvitFormImageField_handleInputWrapperClick, "f")}"
112
+ >
113
+ ${this.mediaController.media.length
114
+ ? renderCarousel(this.mediaController)
115
+ : renderEmptyState({
116
+ allowImages: true,
117
+ allowVideos: false,
118
+ inputDragState: __classPrivateFieldGet(this, _DevvitFormImageField_dragabble, "f").dragState,
119
+ })}
120
+ </div>
121
+ `;
122
+ }
123
+ }
124
+ _DevvitFormImageField_dragabble = new WeakMap(), _DevvitFormImageField_handleInputWrapperClick = new WeakMap(), _DevvitFormImageField_instances = new WeakSet(), _DevvitFormImageField_handleInput = function _DevvitFormImageField_handleInput(e) {
125
+ if (!(e.target instanceof HTMLInputElement))
126
+ return;
127
+ const files = e.target.files;
128
+ if (files?.length) {
129
+ void this.mediaController.handleMedia(Array.from(files));
130
+ e.target.value = '';
131
+ }
132
+ }, _DevvitFormImageField_handleError = function _DevvitFormImageField_handleError(error) {
133
+ // @ts-ignore
134
+ this.setCustomValidity(error);
135
+ // @ts-ignore
136
+ this.reportValidity();
137
+ }, _DevvitFormImageField_setRequiredFieldError = function _DevvitFormImageField_setRequiredFieldError() {
138
+ __classPrivateFieldGet(this, _DevvitFormImageField_instances, "m", _DevvitFormImageField_handleError).call(this, msg('Please add an image', {
139
+ desc: 'Message to user to please add an image file',
140
+ }));
141
+ };
142
+ __decorate([
143
+ query('#fileInputInnerWrapper'),
144
+ __metadata("design:type", HTMLDivElement)
145
+ ], DevvitFormImageField.prototype, "fileInputInnerWrapper", void 0);
146
+ __decorate([
147
+ property({ type: MediaController })
148
+ // @ts-ignore
149
+ ,
150
+ __metadata("design:type", MediaController)
151
+ ], DevvitFormImageField.prototype, "mediaController", void 0);
152
+ __decorate([
153
+ query('input.file-input'),
154
+ __metadata("design:type", HTMLInputElement)
155
+ ], DevvitFormImageField.prototype, "_input", void 0);
156
+ // Faceplate's base classes and mixins aren't playing nice with type checking for some reason
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ customElements.define('devvit-form-image-field', DevvitFormImageField);
@@ -0,0 +1,19 @@
1
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ /**
3
+ * This entire file was copied from shreddit
4
+ * I removed some parts of it that were too "shreddit post creation" focused, but kept the original code style
5
+ * Eventually we will remove it in favour of faceplate-ui image upload component, but for now we have this
6
+ */
7
+ export declare enum DragState {
8
+ IDLE = "IDLE",
9
+ IN_WINDOW = "IN_WINDOW",
10
+ IN_FRAME = "IN_FRAME"
11
+ }
12
+ export declare class DragAndDropController implements ReactiveController {
13
+ #private;
14
+ dragState: DragState;
15
+ constructor(host: ReactiveControllerHost, handleDrop: (files: File[]) => void);
16
+ hostDisconnected(): void;
17
+ initEventListeners(component: HTMLElement): void;
18
+ }
19
+ //# sourceMappingURL=drag-and-drop-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drag-and-drop-controller.d.ts","sourceRoot":"","sources":["../../../../../library/src/client/formbuilder/components/image-field/drag-and-drop-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,KAAK,CAAC;AAEtE;;;;GAIG;AAEH,oBAAY,SAAS;IACnB,IAAI,SAAS;IACb,SAAS,cAAc;IACvB,QAAQ,aAAa;CACtB;AAED,qBAAa,qBAAsB,YAAW,kBAAkB;;IAK9D,SAAS,EAAE,SAAS,CAAkB;gBAE1B,IAAI,EAAE,sBAAsB,EAAE,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI;IAa7E,gBAAgB,IAAI,IAAI;IAIjB,kBAAkB,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI;CAkExD"}
@@ -0,0 +1,113 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _DragAndDropController_instances, _DragAndDropController_component, _DragAndDropController_handleDrop, _DragAndDropController_host, _DragAndDropController_disposeEventListeners, _DragAndDropController_onComponentDragEnter, _DragAndDropController_onComponentDrop, _DragAndDropController_onWindowDragEnter, _DragAndDropController_onWindowDragLeave, _DragAndDropController_onWindowDragOver;
13
+ /**
14
+ * This entire file was copied from shreddit
15
+ * I removed some parts of it that were too "shreddit post creation" focused, but kept the original code style
16
+ * Eventually we will remove it in favour of faceplate-ui image upload component, but for now we have this
17
+ */
18
+ export var DragState;
19
+ (function (DragState) {
20
+ DragState["IDLE"] = "IDLE";
21
+ DragState["IN_WINDOW"] = "IN_WINDOW";
22
+ DragState["IN_FRAME"] = "IN_FRAME";
23
+ })(DragState || (DragState = {}));
24
+ export class DragAndDropController {
25
+ constructor(host, handleDrop) {
26
+ _DragAndDropController_instances.add(this);
27
+ _DragAndDropController_component.set(this, null);
28
+ _DragAndDropController_handleDrop.set(this, void 0);
29
+ _DragAndDropController_host.set(this, void 0);
30
+ this.dragState = DragState.IDLE;
31
+ _DragAndDropController_onComponentDragEnter.set(this, (e) => {
32
+ e.preventDefault();
33
+ e.stopPropagation();
34
+ this.dragState = DragState.IN_FRAME;
35
+ __classPrivateFieldGet(this, _DragAndDropController_host, "f").requestUpdate();
36
+ });
37
+ _DragAndDropController_onComponentDrop.set(this, (e) => {
38
+ e.preventDefault();
39
+ if (e.dataTransfer) {
40
+ const files = getFilesFromDataTransfer(e.dataTransfer);
41
+ __classPrivateFieldGet(this, _DragAndDropController_handleDrop, "f").call(this, files);
42
+ }
43
+ this.dragState = DragState.IDLE;
44
+ __classPrivateFieldGet(this, _DragAndDropController_host, "f").requestUpdate();
45
+ });
46
+ _DragAndDropController_onWindowDragEnter.set(this, (e) => {
47
+ e.preventDefault();
48
+ this.dragState = DragState.IN_WINDOW;
49
+ __classPrivateFieldGet(this, _DragAndDropController_host, "f").requestUpdate();
50
+ });
51
+ _DragAndDropController_onWindowDragLeave.set(this, (e) => {
52
+ e.preventDefault();
53
+ if (!e.relatedTarget || e.relatedTarget === document.body) {
54
+ this.dragState = DragState.IDLE;
55
+ __classPrivateFieldGet(this, _DragAndDropController_host, "f").requestUpdate();
56
+ }
57
+ });
58
+ _DragAndDropController_onWindowDragOver.set(this, (e) => {
59
+ e.preventDefault();
60
+ });
61
+ __classPrivateFieldSet(this, _DragAndDropController_handleDrop, handleDrop, "f");
62
+ __classPrivateFieldSet(this, _DragAndDropController_host, host, "f");
63
+ host.addController(this);
64
+ // Bind event handlers to ensure the correct 'this' context
65
+ __classPrivateFieldSet(this, _DragAndDropController_onComponentDragEnter, __classPrivateFieldGet(this, _DragAndDropController_onComponentDragEnter, "f").bind(this), "f");
66
+ __classPrivateFieldSet(this, _DragAndDropController_onComponentDrop, __classPrivateFieldGet(this, _DragAndDropController_onComponentDrop, "f").bind(this), "f");
67
+ __classPrivateFieldSet(this, _DragAndDropController_onWindowDragEnter, __classPrivateFieldGet(this, _DragAndDropController_onWindowDragEnter, "f").bind(this), "f");
68
+ __classPrivateFieldSet(this, _DragAndDropController_onWindowDragLeave, __classPrivateFieldGet(this, _DragAndDropController_onWindowDragLeave, "f").bind(this), "f");
69
+ __classPrivateFieldSet(this, _DragAndDropController_onWindowDragOver, __classPrivateFieldGet(this, _DragAndDropController_onWindowDragOver, "f").bind(this), "f");
70
+ }
71
+ hostDisconnected() {
72
+ __classPrivateFieldGet(this, _DragAndDropController_instances, "m", _DragAndDropController_disposeEventListeners).call(this);
73
+ }
74
+ initEventListeners(component) {
75
+ __classPrivateFieldSet(this, _DragAndDropController_component, component, "f");
76
+ this.dragState = DragState.IDLE;
77
+ __classPrivateFieldGet(this, _DragAndDropController_host, "f").requestUpdate();
78
+ // Component-specific events
79
+ __classPrivateFieldGet(this, _DragAndDropController_component, "f").addEventListener('dragover', __classPrivateFieldGet(this, _DragAndDropController_onComponentDragEnter, "f"));
80
+ __classPrivateFieldGet(this, _DragAndDropController_component, "f").addEventListener('drop', __classPrivateFieldGet(this, _DragAndDropController_onComponentDrop, "f"));
81
+ // Window-specific events
82
+ window.addEventListener('dragover', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragEnter, "f"));
83
+ window.addEventListener('dragleave', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragLeave, "f"));
84
+ window.addEventListener('drop', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragLeave, "f"));
85
+ window.addEventListener('dragover', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragOver, "f"));
86
+ }
87
+ }
88
+ _DragAndDropController_component = new WeakMap(), _DragAndDropController_handleDrop = new WeakMap(), _DragAndDropController_host = new WeakMap(), _DragAndDropController_onComponentDragEnter = new WeakMap(), _DragAndDropController_onComponentDrop = new WeakMap(), _DragAndDropController_onWindowDragEnter = new WeakMap(), _DragAndDropController_onWindowDragLeave = new WeakMap(), _DragAndDropController_onWindowDragOver = new WeakMap(), _DragAndDropController_instances = new WeakSet(), _DragAndDropController_disposeEventListeners = function _DragAndDropController_disposeEventListeners() {
89
+ if (__classPrivateFieldGet(this, _DragAndDropController_component, "f")) {
90
+ __classPrivateFieldGet(this, _DragAndDropController_component, "f").removeEventListener('dragover', __classPrivateFieldGet(this, _DragAndDropController_onComponentDragEnter, "f"));
91
+ __classPrivateFieldGet(this, _DragAndDropController_component, "f").removeEventListener('drop', __classPrivateFieldGet(this, _DragAndDropController_onComponentDrop, "f"));
92
+ }
93
+ // Remove window-specific event listeners
94
+ window.removeEventListener('dragover', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragEnter, "f"));
95
+ window.removeEventListener('dragleave', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragLeave, "f"));
96
+ window.removeEventListener('drop', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragLeave, "f")); // Match with the correct event
97
+ window.removeEventListener('dragover', __classPrivateFieldGet(this, _DragAndDropController_onWindowDragOver, "f"));
98
+ this.dragState = DragState.IDLE;
99
+ __classPrivateFieldGet(this, _DragAndDropController_host, "f").requestUpdate();
100
+ };
101
+ const getFilesFromDataTransfer = (dataTransfer) => {
102
+ if (dataTransfer.items) {
103
+ return Array.from(dataTransfer.items)
104
+ .map((item) => (item.kind === 'file' ? item.getAsFile() : null))
105
+ .filter(Boolean);
106
+ }
107
+ else if (dataTransfer.files) {
108
+ return Array.from(dataTransfer.files);
109
+ }
110
+ else {
111
+ return [];
112
+ }
113
+ };
@@ -0,0 +1,59 @@
1
+ import type { ValidationResult } from './validateMedia.js';
2
+ import type { LitElement } from 'lit';
3
+ /**
4
+ * This entire file was copied from shreddit
5
+ * I removed some parts of it that were too "shreddit post creation" focused, but kept the original code style
6
+ * Eventually we will remove it in favour of faceplate-ui image upload component, but for now we have this
7
+ */
8
+ interface UploadResponse {
9
+ url?: string;
10
+ mediaId?: string;
11
+ error?: string;
12
+ }
13
+ export type MediaValue = {
14
+ mediaId: string;
15
+ url: string;
16
+ caption?: string;
17
+ outboundUrl?: string;
18
+ posterUrl?: string;
19
+ isGif?: boolean;
20
+ };
21
+ export type MediaItem = {
22
+ id: string;
23
+ file?: File;
24
+ type?: 'still' | 'video';
25
+ src?: string;
26
+ posterSrc?: string;
27
+ status?: MediaStatus;
28
+ validationResult?: ValidationResult;
29
+ mediaResponse?: Promise<UploadResponse>;
30
+ posterResponse?: Promise<UploadResponse>;
31
+ value?: MediaValue;
32
+ width?: number;
33
+ height?: number;
34
+ };
35
+ export declare enum MediaStatus {
36
+ loading = "loading",
37
+ success = "success"
38
+ }
39
+ export declare enum PostType {
40
+ image = "image"
41
+ }
42
+ type ValidationCallback = (message: string) => void;
43
+ type MediaControllerOptions = {
44
+ setValidation?: ValidationCallback;
45
+ };
46
+ export declare const MAX_FILE_COUNT = 20;
47
+ export declare class MediaController {
48
+ #private;
49
+ media: MediaItem[];
50
+ constructor(host: LitElement, options?: MediaControllerOptions);
51
+ getValue(): string;
52
+ setValue(newValue: string): void;
53
+ handleMedia(files: File[]): Promise<void>;
54
+ getPostType(): PostType;
55
+ hasLoadingMedia(): boolean;
56
+ removeMedia(index: number): void;
57
+ }
58
+ export {};
59
+ //# sourceMappingURL=media-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-controller.d.ts","sourceRoot":"","sources":["../../../../../library/src/client/formbuilder/components/image-field/media-controller.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEtC;;;;GAIG;AAGH,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,oBAAY,WAAW;IACrB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED,oBAAY,QAAQ;IAClB,KAAK,UAAU;CAChB;AAED,KAAK,kBAAkB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEpD,KAAK,sBAAsB,GAAG;IAC5B,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC,CAAC;AAEF,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,qBAAa,eAAe;;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAM;gBAKZ,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,sBAAsB;IAK9D,QAAQ,IAAI,MAAM;IAQlB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAmB1B,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4H/C,WAAW,IAAI,QAAQ;IAIvB,eAAe,IAAI,OAAO;IA0D1B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAGjC"}
@@ -0,0 +1,207 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _MediaController_instances, _MediaController_host, _MediaController_setValidation, _MediaController_getRandomId, _MediaController_fireChangeEvent, _MediaController_handleImageResponse, _MediaController_handleImage, _MediaController_upload, _MediaController_createMediaItem, _MediaController_createImage, _MediaController_isVideo, _MediaController_dispatchAlert, _MediaController_filterValidFiles, _MediaController_getDefaultErrorMessage, _MediaController_handleError;
13
+ import { customEvent } from '@reddit/faceplate/lib/custom-event.js';
14
+ import { msg } from '@lit/localize';
15
+ import { ALLOWED_VIDEO_MIMETYPES, validateImageFile } from './validateMedia.js';
16
+ export var MediaStatus;
17
+ (function (MediaStatus) {
18
+ MediaStatus["loading"] = "loading";
19
+ MediaStatus["success"] = "success";
20
+ })(MediaStatus || (MediaStatus = {}));
21
+ export var PostType;
22
+ (function (PostType) {
23
+ PostType["image"] = "image";
24
+ })(PostType || (PostType = {}));
25
+ export const MAX_FILE_COUNT = 20;
26
+ export class MediaController {
27
+ constructor(host, options) {
28
+ _MediaController_instances.add(this);
29
+ this.media = [];
30
+ _MediaController_host.set(this, void 0);
31
+ _MediaController_setValidation.set(this, void 0);
32
+ __classPrivateFieldSet(this, _MediaController_host, host, "f");
33
+ __classPrivateFieldSet(this, _MediaController_setValidation, options?.setValidation ?? (() => { }), "f");
34
+ }
35
+ getValue() {
36
+ const validFiles = this.media.filter((media) => media.status === MediaStatus.success);
37
+ if (validFiles.length === 0) {
38
+ return '';
39
+ }
40
+ return JSON.stringify(validFiles.map((media) => media.value));
41
+ }
42
+ setValue(newValue) {
43
+ const val = JSON.parse(newValue);
44
+ this.media = val;
45
+ }
46
+ async handleMedia(files) {
47
+ try {
48
+ const seed = this.media.length;
49
+ __classPrivateFieldGet(this, _MediaController_setValidation, "f").call(this, '');
50
+ const validFiles = __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_filterValidFiles).call(this, files);
51
+ if (validFiles.length > 0) {
52
+ const mediaItems = [];
53
+ for (const file of validFiles) {
54
+ mediaItems.push(__classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_handleImage).call(this, file));
55
+ }
56
+ this.media = this.media.concat(mediaItems);
57
+ __classPrivateFieldGet(this, _MediaController_host, "f").requestUpdate('mediaLength', seed);
58
+ const mediaResponse = await Promise.all(mediaItems.map((item) => item.mediaResponse));
59
+ __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_handleImageResponse).call(this, mediaResponse, seed);
60
+ __classPrivateFieldGet(this, _MediaController_host, "f").requestUpdate();
61
+ }
62
+ }
63
+ catch (error) {
64
+ if (!(error instanceof Error)) {
65
+ console.error(error);
66
+ __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_handleError).call(this, msg('Something went wrong. Please try again later', {
67
+ desc: 'Unknown error uploading the file',
68
+ }));
69
+ return;
70
+ }
71
+ __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_handleError).call(this, error.message);
72
+ }
73
+ }
74
+ getPostType() {
75
+ return PostType.image;
76
+ }
77
+ hasLoadingMedia() {
78
+ return this.media.some((media) => media.status === MediaStatus.loading);
79
+ }
80
+ removeMedia(index) {
81
+ this.media.splice(index, 1);
82
+ }
83
+ }
84
+ _MediaController_host = new WeakMap(), _MediaController_setValidation = new WeakMap(), _MediaController_instances = new WeakSet(), _MediaController_getRandomId = function _MediaController_getRandomId(filename) {
85
+ // match all temporary media IDs with that filename
86
+ // eslint-disable-next-line @reddit/i18n-shreddit/no-unwrapped-strings, security/detect-non-literal-regexp
87
+ const regex = new RegExp(`^${filename}-(?<id>\\d+)$`);
88
+ // find the highest suffix and increment it by one
89
+ const suffix = 1 +
90
+ this.media.reduce((currSuffix, media) => {
91
+ const match = media.id.match(regex);
92
+ return match ? Math.max(currSuffix, parseInt(match.groups.id, 10)) : currSuffix;
93
+ }, -1);
94
+ return `${filename}-${suffix}`;
95
+ }, _MediaController_fireChangeEvent = function _MediaController_fireChangeEvent() {
96
+ // eslint-disable-next-line @reddit/i18n-shreddit/no-unwrapped-strings
97
+ const event = new Event('change', { bubbles: true });
98
+ __classPrivateFieldGet(this, _MediaController_host, "f").dispatchEvent(event);
99
+ }, _MediaController_handleImageResponse = function _MediaController_handleImageResponse(mediaResponses, seed) {
100
+ __classPrivateFieldGet(this, _MediaController_host, "f").setAttribute('name', 'image');
101
+ let success = false;
102
+ const failedItemsIndexes = [];
103
+ for (const [i, mediaResponse] of mediaResponses.entries()) {
104
+ const index = seed + i;
105
+ const { url, mediaId, error = null } = mediaResponse ?? {};
106
+ const media = this.media[index];
107
+ if (url && mediaId) {
108
+ const galleryItem = { mediaId, url };
109
+ media.id = mediaId;
110
+ media.value = galleryItem;
111
+ media.status = MediaStatus.success;
112
+ success = true;
113
+ }
114
+ else {
115
+ __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_dispatchAlert).call(this, error);
116
+ failedItemsIndexes.push(index);
117
+ }
118
+ }
119
+ if (failedItemsIndexes.length > 0) {
120
+ this.media = this.media.filter((_, index) => !failedItemsIndexes.includes(index));
121
+ }
122
+ if (success) {
123
+ __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_fireChangeEvent).call(this);
124
+ }
125
+ else {
126
+ __classPrivateFieldGet(this, _MediaController_host, "f").requestUpdate('mediaLength', this.media.length - 1);
127
+ }
128
+ }, _MediaController_handleImage = function _MediaController_handleImage(file) {
129
+ const IMAGE_UPLOAD_ERROR = msg('Unable to upload image', {
130
+ desc: 'Error: unable to upload image file',
131
+ });
132
+ const mediaItem = __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_createImage).call(this, file);
133
+ const validation = validateImageFile(file);
134
+ if (!validation.valid) {
135
+ mediaItem.mediaResponse = Promise.resolve({
136
+ error: validation.error ?? IMAGE_UPLOAD_ERROR,
137
+ });
138
+ }
139
+ else {
140
+ mediaItem.mediaResponse = __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_upload).call(this, file);
141
+ }
142
+ return mediaItem;
143
+ }, _MediaController_upload = async function _MediaController_upload(blob) {
144
+ console.log('UPLOAD HAPPENS');
145
+ const imageUploadEvent = customEvent('devvit-image-upload', {
146
+ blob,
147
+ uploadResponse: null,
148
+ });
149
+ __classPrivateFieldGet(this, _MediaController_host, "f").dispatchEvent(imageUploadEvent);
150
+ const response = await imageUploadEvent.detail.uploadResponse;
151
+ if (!response) {
152
+ return Promise.resolve({
153
+ error: 'Image upload failed',
154
+ });
155
+ }
156
+ return response;
157
+ }, _MediaController_createMediaItem = function _MediaController_createMediaItem(file) {
158
+ const src = URL.createObjectURL(file);
159
+ return {
160
+ id: __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_getRandomId).call(this, file.name),
161
+ file,
162
+ src,
163
+ status: MediaStatus.loading,
164
+ };
165
+ }, _MediaController_createImage = function _MediaController_createImage(file) {
166
+ return {
167
+ ...__classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_createMediaItem).call(this, file),
168
+ type: 'still',
169
+ };
170
+ }, _MediaController_isVideo = function _MediaController_isVideo(file) {
171
+ if (!file)
172
+ return false;
173
+ return ALLOWED_VIDEO_MIMETYPES.includes(file.type);
174
+ }, _MediaController_dispatchAlert = function _MediaController_dispatchAlert(message) {
175
+ __classPrivateFieldGet(this, _MediaController_host, "f").dispatchEvent(customEvent('faceplate-alert', {
176
+ level: 4, // warning
177
+ message: message ?? __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_getDefaultErrorMessage).call(this),
178
+ }));
179
+ }, _MediaController_filterValidFiles = function _MediaController_filterValidFiles(files) {
180
+ let mediaFiles = files;
181
+ const hasVideoInSelectedFiles = files.some((file) => __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_isVideo).call(this, file));
182
+ const isMultipleSelection = files.length > 1;
183
+ const isValidMediaPresent = this.media.length > 0;
184
+ if (isMultipleSelection || isValidMediaPresent) {
185
+ const GALLERY_NOT_ALLOWED_ERROR = msg(`Only one file can be used for the image input`, {
186
+ desc: 'Error: Multiple files cannot be added to image input',
187
+ });
188
+ __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_dispatchAlert).call(this, GALLERY_NOT_ALLOWED_ERROR);
189
+ return [];
190
+ }
191
+ if (hasVideoInSelectedFiles && (isValidMediaPresent || isMultipleSelection)) {
192
+ const GALLERY_CONTAIN_VIDEO_ERROR = msg('Image input can only contain images', {
193
+ desc: 'Error when users upload non-image media files to image input',
194
+ });
195
+ __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_dispatchAlert).call(this, GALLERY_CONTAIN_VIDEO_ERROR);
196
+ mediaFiles = files.filter((file) => !__classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_isVideo).call(this, file));
197
+ }
198
+ return mediaFiles;
199
+ }, _MediaController_getDefaultErrorMessage = function _MediaController_getDefaultErrorMessage() {
200
+ return msg('There was an error uploading the file', {
201
+ desc: 'Generic error message when file upload fails',
202
+ id: 'generic-error-message-for-uploading-file',
203
+ });
204
+ }, _MediaController_handleError = function _MediaController_handleError(err) {
205
+ const error = err || __classPrivateFieldGet(this, _MediaController_instances, "m", _MediaController_getDefaultErrorMessage).call(this);
206
+ __classPrivateFieldGet(this, _MediaController_setValidation, "f").call(this, error);
207
+ };
@@ -0,0 +1,15 @@
1
+ import type { TemplateResult } from 'lit';
2
+ import { DragState } from './drag-and-drop-controller.js';
3
+ /**
4
+ * This entire file was copied from shreddit
5
+ * I removed some parts of it that were too "shreddit post creation" focused, but kept the original code style
6
+ * Eventually we will remove it in favour of faceplate-ui image upload component, but for now we have this
7
+ */
8
+ interface RenderEmptyStateProps {
9
+ allowImages: boolean;
10
+ allowVideos: boolean;
11
+ inputDragState: DragState;
12
+ }
13
+ export declare function renderEmptyState({ allowImages, allowVideos, inputDragState, }: RenderEmptyStateProps): TemplateResult<1>;
14
+ export {};
15
+ //# sourceMappingURL=render-empty-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-empty-state.d.ts","sourceRoot":"","sources":["../../../../../library/src/client/formbuilder/components/image-field/render-empty-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAG1C,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE1D;;;;GAIG;AAEH,UAAU,qBAAqB;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,SAAS,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,WAAW,EACX,WAAW,EACX,cAAc,GACf,EAAE,qBAAqB,GAAG,cAAc,CAAC,CAAC,CAAC,CAuC3C"}