pages_core 3.12.4 → 3.12.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +8 -43
  4. data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
  5. data/app/assets/builds/pages_core/admin.css +264 -133
  6. data/app/assets/stylesheets/pages_core/admin/components/attachments.css +3 -4
  7. data/app/assets/stylesheets/pages_core/admin/components/forms.css +17 -16
  8. data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +8 -4
  9. data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +1 -1
  10. data/app/assets/stylesheets/pages_core/admin/components/list_table.css +11 -3
  11. data/app/assets/stylesheets/pages_core/admin/components/modal.css +9 -5
  12. data/app/assets/stylesheets/pages_core/admin/components/page_tree.css +5 -1
  13. data/app/assets/stylesheets/pages_core/admin/components/toast.css +2 -2
  14. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +4 -2
  15. data/app/assets/stylesheets/pages_core/admin/vars.css +2 -1
  16. data/app/controllers/admin/calendars_controller.rb +2 -2
  17. data/app/controllers/admin/categories_controller.rb +3 -3
  18. data/app/controllers/admin/news_controller.rb +6 -6
  19. data/app/controllers/admin/pages_controller.rb +12 -11
  20. data/app/controllers/admin/users_controller.rb +1 -1
  21. data/app/controllers/concerns/pages_core/preview_pages_controller.rb +15 -17
  22. data/app/controllers/pages_core/admin_controller.rb +2 -2
  23. data/app/controllers/pages_core/base_controller.rb +1 -8
  24. data/app/controllers/pages_core/frontend/pages_controller.rb +13 -5
  25. data/app/controllers/pages_core/frontend_controller.rb +12 -7
  26. data/app/helpers/admin/menu_helper.rb +2 -0
  27. data/app/helpers/admin/pages_helper.rb +1 -4
  28. data/app/helpers/pages_core/admin/admin_helper.rb +0 -1
  29. data/app/helpers/pages_core/admin/content_tabs_helper.rb +9 -2
  30. data/app/helpers/pages_core/application_helper.rb +2 -3
  31. data/app/helpers/pages_core/frontend_helper.rb +1 -1
  32. data/app/helpers/pages_core/head_tags_helper.rb +15 -46
  33. data/app/helpers/pages_core/images_helper.rb +76 -21
  34. data/app/helpers/pages_core/locales_helper.rb +9 -0
  35. data/app/helpers/pages_core/open_graph_tags_helper.rb +3 -5
  36. data/app/helpers/pages_core/page_path_helper.rb +1 -1
  37. data/app/javascript/components/Attachments/Attachment.tsx +55 -52
  38. data/app/javascript/components/Attachments/AttachmentEditor.tsx +45 -50
  39. data/app/javascript/components/Attachments/Placeholder.tsx +1 -2
  40. data/app/javascript/components/Attachments.jsx +69 -57
  41. data/app/javascript/components/DateRangeSelect.jsx +94 -54
  42. data/app/javascript/components/EditableImage.tsx +20 -16
  43. data/app/javascript/components/FileUploadButton.tsx +12 -12
  44. data/app/javascript/components/ImageCropper/FocalPoint.tsx +22 -20
  45. data/app/javascript/components/ImageCropper/Image.tsx +20 -16
  46. data/app/javascript/components/ImageCropper/Toolbar.tsx +35 -27
  47. data/app/javascript/components/ImageCropper/useCrop.ts +105 -91
  48. data/app/javascript/components/ImageCropper.tsx +34 -25
  49. data/app/javascript/components/ImageEditor/Form.tsx +32 -43
  50. data/app/javascript/components/ImageEditor.tsx +29 -21
  51. data/app/javascript/components/ImageGrid/DragElement.tsx +6 -4
  52. data/app/javascript/components/ImageGrid/GridImage.tsx +56 -52
  53. data/app/javascript/components/ImageGrid/Placeholder.tsx +1 -1
  54. data/app/javascript/components/ImageGrid.jsx +132 -101
  55. data/app/javascript/components/ImageUploader.tsx +59 -55
  56. data/app/javascript/components/Modal.tsx +2 -4
  57. data/app/javascript/components/PageDates.jsx +25 -20
  58. data/app/javascript/components/PageFiles.jsx +7 -5
  59. data/app/javascript/components/PageImages.tsx +9 -7
  60. data/app/javascript/components/PageTree/Draggable.tsx +46 -40
  61. data/app/javascript/components/PageTree/Node.tsx +111 -95
  62. data/app/javascript/components/PageTree/types.ts +9 -9
  63. data/app/javascript/components/PageTree.tsx +44 -29
  64. data/app/javascript/components/RichTextArea.jsx +51 -37
  65. data/app/javascript/components/RichTextToolbarButton.tsx +8 -5
  66. data/app/javascript/components/TagEditor/AddTagForm.tsx +11 -10
  67. data/app/javascript/components/TagEditor/Tag.tsx +10 -8
  68. data/app/javascript/components/TagEditor.tsx +15 -10
  69. data/app/javascript/components/Toast.tsx +3 -7
  70. data/app/javascript/components/drag/draggedOrder.ts +16 -15
  71. data/app/javascript/components/drag/types.ts +12 -12
  72. data/app/javascript/components/drag/useDragCollection.ts +36 -42
  73. data/app/javascript/components/drag/useDragUploader.ts +3 -2
  74. data/app/javascript/components/drag.ts +5 -4
  75. data/app/javascript/controllers/LoginController.ts +0 -1
  76. data/app/javascript/controllers/MainController.ts +6 -2
  77. data/app/javascript/controllers/PageOptionsController.js +7 -2
  78. data/app/javascript/features/RichText.tsx +9 -7
  79. data/app/javascript/index.ts +5 -3
  80. data/app/javascript/lib/Tree.ts +27 -24
  81. data/app/javascript/lib/copyToClipboard.ts +5 -4
  82. data/app/javascript/lib/readyHandler.ts +4 -4
  83. data/app/javascript/lib/request.ts +7 -3
  84. data/app/javascript/stores/useModalStore.ts +3 -3
  85. data/app/javascript/stores/useToastStore.ts +14 -12
  86. data/app/javascript/types.ts +22 -22
  87. data/app/models/concerns/pages_core/page_model/templateable.rb +1 -1
  88. data/app/views/admin/calendars/show.html.erb +1 -1
  89. data/app/views/admin/news/index.html.erb +1 -1
  90. data/app/views/admin/pages/_edit_files.html.erb +1 -1
  91. data/app/views/admin/pages/_edit_images.html.erb +1 -1
  92. data/app/views/admin/pages/_list_item.html.erb +1 -1
  93. data/app/views/admin/pages/_search_bar.html.erb +1 -1
  94. data/app/views/admin/pages/deleted.html.erb +2 -2
  95. data/app/views/admin/pages/edit.html.erb +3 -3
  96. data/app/views/admin/pages/index.html.erb +4 -4
  97. data/app/views/admin/pages/new.html.erb +1 -1
  98. data/app/views/admin/pages/search.html.erb +3 -3
  99. data/app/views/feeds/pages.rss.builder +2 -2
  100. data/app/views/layouts/admin/_page_header.html.erb +4 -4
  101. data/app/views/layouts/admin.html.erb +1 -2
  102. data/config/locales/en.yml +1 -0
  103. data/lib/pages_core/pages_plugin.rb +5 -3
  104. data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +15 -13
  105. data/lib/tasks/pages/reports.rake +26 -0
  106. metadata +32 -4
  107. data/app/helpers/pages_core/admin/deprecated_admin_helper.rb +0 -40
  108. data/app/views/pages_core/_google_analytics.html.erb +0 -8
@@ -4,9 +4,9 @@ import { ImageResource } from "../../types";
4
4
  import { DragState } from "../drag";
5
5
 
6
6
  interface DragElementProps {
7
- container: RefObject<HTMLDivElement>,
8
- draggable: string | { record: { image: ImageResource, src?: string } },
9
- dragState: DragState
7
+ container: RefObject<HTMLDivElement>;
8
+ draggable: string | { record: { image: ImageResource; src?: string } };
9
+ dragState: DragState;
10
10
  }
11
11
 
12
12
  export default function DragElement(props: DragElementProps) {
@@ -24,7 +24,9 @@ export default function DragElement(props: DragElementProps) {
24
24
  return (
25
25
  <div className="drag-image" style={translateStyle}>
26
26
  {"record" in draggable && draggable.record.image && (
27
- <img src={draggable.record.src || draggable.record.image.thumbnail_url} />
27
+ <img
28
+ src={draggable.record.src || draggable.record.image.thumbnail_url}
29
+ />
28
30
  )}
29
31
  </div>
30
32
  );
@@ -8,25 +8,25 @@ import Placeholder from "./Placeholder";
8
8
  import { useDraggable } from "../drag";
9
9
 
10
10
  interface Record {
11
- id: number | null,
12
- image: ImageResource,
13
- src: string | null,
14
- file: File | null
11
+ id: number | null;
12
+ image: ImageResource;
13
+ src: string | null;
14
+ file: File | null;
15
15
  }
16
16
 
17
17
  interface GridImageProps {
18
- draggable: { handle: string, record: Record }
19
- attributeName: string,
20
- locale: string,
21
- locales: { [index: string]: Locale },
22
- placeholder: boolean,
23
- enablePrimary: boolean,
24
- showEmbed: boolean,
25
- primary: boolean,
26
- position: number,
27
- deleteImage: () => void,
28
- startDrag: (evt: Event, draggable: Draggable) => void,
29
- onUpdate: (newImage: ImageResource, src: string) => void
18
+ draggable: { handle: string; record: Record };
19
+ attributeName: string;
20
+ locale: string;
21
+ locales: { [index: string]: Locale };
22
+ placeholder: boolean;
23
+ enablePrimary: boolean;
24
+ showEmbed: boolean;
25
+ primary: boolean;
26
+ position: number;
27
+ deleteImage: () => void;
28
+ startDrag: (evt: Event, draggable: Draggable) => void;
29
+ onUpdate: (newImage: ImageResource, src: string) => void;
30
30
  }
31
31
 
32
32
  export default function GridImage(props: GridImageProps) {
@@ -70,44 +70,48 @@ export default function GridImage(props: GridImageProps) {
70
70
  }
71
71
 
72
72
  return (
73
- <div className={classes.join(" ")}
74
- {...dragAttrs}>
75
- <input name={`${attributeName}[id]`}
76
- type="hidden" value={record.id || ""} />
77
- <input name={`${attributeName}[image_id]`}
78
- type="hidden" value={(image && image.id) || ""} />
79
- <input name={`${attributeName}[position]`}
80
- type="hidden" value={props.position} />
73
+ <div className={classes.join(" ")} {...dragAttrs}>
74
+ <input
75
+ name={`${attributeName}[id]`}
76
+ type="hidden"
77
+ value={record.id || ""}
78
+ />
79
+ <input
80
+ name={`${attributeName}[image_id]`}
81
+ type="hidden"
82
+ value={(image && image.id) || ""}
83
+ />
84
+ <input
85
+ name={`${attributeName}[position]`}
86
+ type="hidden"
87
+ value={props.position}
88
+ />
81
89
  {props.enablePrimary && (
82
- <input name={`${attributeName}[primary]`}
83
- type="hidden" value={props.primary} />
90
+ <input
91
+ name={`${attributeName}[primary]`}
92
+ type="hidden"
93
+ value={props.primary}
94
+ />
95
+ )}
96
+ {!image && <Placeholder src={src} />}
97
+ {image && (
98
+ <>
99
+ <EditableImage
100
+ image={image}
101
+ key={props.placeholder ? "placeholder" : draggable.handle}
102
+ src={src || image.thumbnail_url}
103
+ width={250}
104
+ caption={true}
105
+ locale={props.locale}
106
+ locales={props.locales}
107
+ onUpdate={props.onUpdate}
108
+ />
109
+ <div className="actions">
110
+ {props.showEmbed && <button onClick={copyEmbed}>Embed</button>}
111
+ {props.deleteImage && <button onClick={deleteImage}>Remove</button>}
112
+ </div>
113
+ </>
84
114
  )}
85
- {!image &&
86
- <Placeholder src={src} />}
87
- {image &&
88
- <>
89
- <EditableImage
90
- image={image}
91
- key={props.placeholder ? "placeholder" : draggable.handle}
92
- src={src || image.thumbnail_url}
93
- width={250}
94
- caption={true}
95
- locale={props.locale}
96
- locales={props.locales}
97
- onUpdate={props.onUpdate} />
98
- <div className="actions">
99
- {props.showEmbed && (
100
- <button onClick={copyEmbed}>
101
- Embed
102
- </button>
103
- )}
104
- {props.deleteImage && (
105
- <button onClick={deleteImage}>
106
- Remove
107
- </button>
108
- )}
109
- </div>
110
- </>}
111
115
  </div>
112
116
  );
113
117
  }
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
 
3
3
  interface PlaceholderProps {
4
- src: string
4
+ src: string;
5
5
  }
6
6
 
7
7
  export default function Placeholder(props: PlaceholderProps) {
@@ -7,26 +7,35 @@ import GridImage from "./ImageGrid/GridImage";
7
7
  import useToastStore from "../stores/useToastStore";
8
8
  import { post } from "../lib/request";
9
9
 
10
- import { createDraggable,
11
- collectionOrder,
12
- useDragCollection,
13
- useDragUploader } from "./drag";
10
+ import {
11
+ createDraggable,
12
+ collectionOrder,
13
+ useDragCollection,
14
+ useDragUploader
15
+ } from "./drag";
14
16
 
15
17
  function filterFiles(files) {
16
- const validMimeTypes = ["image/gif",
17
- "image/jpeg",
18
- "image/pjpeg",
19
- "image/png",
20
- "image/tiff"];
21
- return files.filter(f => (validMimeTypes.indexOf(f.type) !== -1));
18
+ const validMimeTypes = [
19
+ "image/gif",
20
+ "image/jpeg",
21
+ "image/pjpeg",
22
+ "image/png",
23
+ "image/tiff"
24
+ ];
25
+ return files.filter((f) => validMimeTypes.indexOf(f.type) !== -1);
22
26
  }
23
27
 
24
28
  function draggedImageOrder(primaryCollection, imagesCollection, dragState) {
25
29
  const [primary, ...rest] = collectionOrder(primaryCollection, dragState);
26
30
  let images = [...rest, ...collectionOrder(imagesCollection, dragState)];
27
31
 
28
- if (dragState.dragging && [primary, ...images].indexOf(dragState.dragging) === -1) {
29
- if (dragState.y < imagesCollection.ref.current.getBoundingClientRect().top) {
32
+ if (
33
+ dragState.dragging &&
34
+ [primary, ...images].indexOf(dragState.dragging) === -1
35
+ ) {
36
+ if (
37
+ dragState.y < imagesCollection.ref.current.getBoundingClientRect().top
38
+ ) {
30
39
  images = [dragState.dragging, ...images];
31
40
  } else {
32
41
  images.push(dragState.dragging);
@@ -37,11 +46,11 @@ function draggedImageOrder(primaryCollection, imagesCollection, dragState) {
37
46
  }
38
47
 
39
48
  function initRecords(props) {
40
- const primary = props.enablePrimary ?
41
- props.records.filter(r => r.primary).slice(0, 1) :
42
- [];
49
+ const primary = props.enablePrimary
50
+ ? props.records.filter((r) => r.primary).slice(0, 1)
51
+ : [];
43
52
 
44
- return [primary, props.records.filter(r => primary.indexOf(r) === -1)];
53
+ return [primary, props.records.filter((r) => primary.indexOf(r) === -1)];
45
54
  }
46
55
 
47
56
  export default function ImageGrid(props) {
@@ -59,8 +68,11 @@ export default function ImageGrid(props) {
59
68
  };
60
69
 
61
70
  const dragEnd = (dragState, files) => {
62
- const [draggedPrimary,
63
- draggedImages] = draggedImageOrder(primary, images, dragState);
71
+ const [draggedPrimary, draggedImages] = draggedImageOrder(
72
+ primary,
73
+ images,
74
+ dragState
75
+ );
64
76
 
65
77
  primary.dispatch({
66
78
  type: "reorder",
@@ -69,19 +81,24 @@ export default function ImageGrid(props) {
69
81
  images.dispatch({ type: "reorder", payload: draggedImages });
70
82
 
71
83
  if (files) {
72
- const uploads = filterFiles(files).map(f => uploadImage(f));
84
+ const uploads = filterFiles(files).map((f) => uploadImage(f));
73
85
  dispatchAll({ type: "insertFiles", payload: uploads });
74
86
  }
75
87
  };
76
88
 
77
- const [dragState,
78
- dragStart,
79
- listeners] = useDragUploader([primary, images], dragEnd);
89
+ const [dragState, dragStart, listeners] = useDragUploader(
90
+ [primary, images],
91
+ dragEnd
92
+ );
80
93
 
81
94
  const position = (record) => {
82
- return [...primary.draggables.map(d => d.record),
83
- ...images.draggables.map(d => d.record),
84
- ...deleted].indexOf(record) + 1;
95
+ return (
96
+ [
97
+ ...primary.draggables.map((d) => d.record),
98
+ ...images.draggables.map((d) => d.record),
99
+ ...deleted
100
+ ].indexOf(record) + 1
101
+ );
85
102
  };
86
103
 
87
104
  const attrName = (record) => {
@@ -89,26 +106,23 @@ export default function ImageGrid(props) {
89
106
  };
90
107
 
91
108
  const uploadImage = (file) => {
92
- const draggable = createDraggable(
93
- { image: null, file: file }
94
- );
109
+ const draggable = createDraggable({ image: null, file: file });
95
110
 
96
111
  let data = new FormData();
97
112
 
98
113
  data.append("image[file]", file);
99
114
 
100
- post("/admin/images.json", data)
101
- .then(json => {
102
- if (json.status === "error") {
103
- error("Error uploading image: " + json.error);
104
- dispatchAll({ type: "remove", payload: draggable });
105
- } else {
106
- dispatchAll({
107
- type: "update",
108
- payload: { ...draggable, record: { image: json } }
109
- });
110
- }
111
- });
115
+ post("/admin/images.json", data).then((json) => {
116
+ if (json.status === "error") {
117
+ error("Error uploading image: " + json.error);
118
+ dispatchAll({ type: "remove", payload: draggable });
119
+ } else {
120
+ dispatchAll({
121
+ type: "update",
122
+ payload: { ...draggable, record: { image: json } }
123
+ });
124
+ }
125
+ });
112
126
 
113
127
  return draggable;
114
128
  };
@@ -136,28 +150,30 @@ export default function ImageGrid(props) {
136
150
  const { dragging } = dragState;
137
151
 
138
152
  if (draggable === "Files") {
139
- return (<FilePlaceholder key="placeholder" />);
153
+ return <FilePlaceholder key="placeholder" />;
140
154
  }
141
155
 
142
156
  return (
143
- <GridImage key={draggable.handle}
144
- draggable={draggable}
145
- locale={props.locale}
146
- locales={props.locales}
147
- showEmbed={props.showEmbed}
148
- startDrag={dragStart}
149
- position={position(draggable.record)}
150
- primary={isPrimary}
151
- onUpdate={update(draggable)}
152
- enablePrimary={props.enablePrimary}
153
- deleteImage={remove(draggable)}
154
- attributeName={attrName(draggable.record)}
155
- placeholder={dragging && dragging == draggable} />
157
+ <GridImage
158
+ key={draggable.handle}
159
+ draggable={draggable}
160
+ locale={props.locale}
161
+ locales={props.locales}
162
+ showEmbed={props.showEmbed}
163
+ startDrag={dragStart}
164
+ position={position(draggable.record)}
165
+ primary={isPrimary}
166
+ onUpdate={update(draggable)}
167
+ enablePrimary={props.enablePrimary}
168
+ deleteImage={remove(draggable)}
169
+ attributeName={attrName(draggable.record)}
170
+ placeholder={dragging && dragging == draggable}
171
+ />
156
172
  );
157
173
  };
158
174
 
159
175
  const uploadPrimary = (files) => {
160
- const [first, ...rest] = filterFiles(files).map(f => uploadImage(f));
176
+ const [first, ...rest] = filterFiles(files).map((f) => uploadImage(f));
161
177
  if (first) {
162
178
  images.dispatch({
163
179
  type: "prepend",
@@ -170,7 +186,7 @@ export default function ImageGrid(props) {
170
186
  const uploadAdditional = (files) => {
171
187
  images.dispatch({
172
188
  type: "append",
173
- payload: filterFiles(files).map(f => uploadImage(f))
189
+ payload: filterFiles(files).map((f) => uploadImage(f))
174
190
  });
175
191
  };
176
192
 
@@ -179,67 +195,82 @@ export default function ImageGrid(props) {
179
195
  classNames.push("with-primary-image");
180
196
  }
181
197
 
182
- const [draggedPrimary,
183
- draggedImages] = draggedImageOrder(primary, images, dragState);
198
+ const [draggedPrimary, draggedImages] = draggedImageOrder(
199
+ primary,
200
+ images,
201
+ dragState
202
+ );
184
203
 
185
204
  return (
186
- <div className={classNames.join(" ")}
187
- ref={containerRef}
188
- {...listeners}>
189
- {dragState.dragging &&
190
- <DragElement draggable={dragState.dragging}
191
- dragState={dragState}
192
- container={containerRef} />}
205
+ <div className={classNames.join(" ")} ref={containerRef} {...listeners}>
206
+ {dragState.dragging && (
207
+ <DragElement
208
+ draggable={dragState.dragging}
209
+ dragState={dragState}
210
+ container={containerRef}
211
+ />
212
+ )}
193
213
  {props.enablePrimary && (
194
214
  <div className="primary-image" ref={primary.ref}>
195
- <h3>
196
- Main image
197
- </h3>
198
- {draggedPrimary &&
199
- <>
200
- {renderImage(draggedPrimary, true)}
201
- {props.primaryAttribute && (
202
- <input type="hidden" name={props.primaryAttribute}
203
- value={(draggedPrimary.record &&
204
- draggedPrimary.record.image &&
205
- draggedPrimary.record.image.id) || ""} />
206
- )}
207
- </>}
215
+ <h3>Main image</h3>
216
+ {draggedPrimary && (
217
+ <>
218
+ {renderImage(draggedPrimary, true)}
219
+ {props.primaryAttribute && (
220
+ <input
221
+ type="hidden"
222
+ name={props.primaryAttribute}
223
+ value={
224
+ (draggedPrimary.record &&
225
+ draggedPrimary.record.image &&
226
+ draggedPrimary.record.image.id) ||
227
+ ""
228
+ }
229
+ />
230
+ )}
231
+ </>
232
+ )}
208
233
  {!draggedPrimary && (
209
234
  <div className="drop-target">
210
- <FileUploadButton multiple={true}
211
- type="image"
212
- multiline={true}
213
- callback={uploadPrimary} />
214
- </div>)}
235
+ <FileUploadButton
236
+ multiple={true}
237
+ type="image"
238
+ multiline={true}
239
+ callback={uploadPrimary}
240
+ />
241
+ </div>
242
+ )}
215
243
  </div>
216
244
  )}
217
245
  <div className="grid" ref={images.ref}>
218
- <h3>
219
- {props.enablePrimary ? "More images" : "Images"}
220
- </h3>
246
+ <h3>{props.enablePrimary ? "More images" : "Images"}</h3>
221
247
  <div className="drop-target">
222
- <FileUploadButton multiple={true}
223
- type="image"
224
- callback={uploadAdditional} />
248
+ <FileUploadButton
249
+ multiple={true}
250
+ type="image"
251
+ callback={uploadAdditional}
252
+ />
225
253
  </div>
226
254
  <div className="images">
227
- {draggedImages.map(r => renderImage(r, false))}
255
+ {draggedImages.map((r) => renderImage(r, false))}
228
256
  </div>
229
257
  </div>
230
258
  <div className="deleted">
231
- {deleted.map(r =>
259
+ {deleted.map((r) => (
232
260
  <span className="deleted-image" key={r.id}>
233
- <input name={`${attrName(r)}[id]`}
234
- type="hidden"
235
- value={r.id} />
236
- <input name={`${attrName(r)}[attachment_id]`}
237
- type="hidden"
238
- value={(r.image && r.image.id) || ""} />
239
- <input name={`${attrName(r)}[_destroy]`}
240
- type="hidden"
241
- value={true} />
242
- </span>)}
261
+ <input name={`${attrName(r)}[id]`} type="hidden" value={r.id} />
262
+ <input
263
+ name={`${attrName(r)}[attachment_id]`}
264
+ type="hidden"
265
+ value={(r.image && r.image.id) || ""}
266
+ />
267
+ <input
268
+ name={`${attrName(r)}[_destroy]`}
269
+ type="hidden"
270
+ value={true}
271
+ />
272
+ </span>
273
+ ))}
243
274
  </div>
244
275
  </div>
245
276
  );
@@ -5,17 +5,17 @@ import useToastStore from "../stores/useToastStore";
5
5
  import { ImageResource, Locale } from "../types";
6
6
  import { post } from "../lib/request";
7
7
 
8
- type ImageResponse = ImageResource | { status: "error", error: string }
8
+ type ImageResponse = ImageResource | { status: "error"; error: string };
9
9
 
10
10
  interface ImageUploaderProps {
11
- locale: string,
12
- locales: { [index: string]: Locale },
13
- image: ImageResource,
14
- src: string,
15
- width: number,
16
- caption: boolean,
17
- attr: string,
18
- alternative: string
11
+ locale: string;
12
+ locales: { [index: string]: Locale };
13
+ image: ImageResource;
14
+ src: string;
15
+ width: number;
16
+ caption: boolean;
17
+ attr: string;
18
+ alternative: string;
19
19
  }
20
20
 
21
21
  function getFiles(dt: DataTransfer): File[] {
@@ -87,15 +87,19 @@ export default function ImageUploader(props: ImageUploaderProps) {
87
87
  };
88
88
 
89
89
  const uploadImage = (file: File) => {
90
- const validTypes = ["image/gif",
91
- "image/jpeg",
92
- "image/pjpeg",
93
- "image/png",
94
- "image/tiff"];
90
+ const validTypes = [
91
+ "image/gif",
92
+ "image/jpeg",
93
+ "image/pjpeg",
94
+ "image/png",
95
+ "image/tiff"
96
+ ];
95
97
 
96
98
  if (validTypes.indexOf(file.type) == -1) {
97
- alert("Invalid file type, only images in JPEG, PNG or GIF " +
98
- "formats are supported");
99
+ alert(
100
+ "Invalid file type, only images in JPEG, PNG or GIF " +
101
+ "formats are supported"
102
+ );
99
103
  return;
100
104
  }
101
105
 
@@ -111,19 +115,18 @@ export default function ImageUploader(props: ImageUploaderProps) {
111
115
 
112
116
  data.append("image[file]", file);
113
117
  locales.forEach((l) => {
114
- data.append(`image[alternative][${l}]`, (props.alternative || ""));
118
+ data.append(`image[alternative][${l}]`, props.alternative || "");
115
119
  });
116
120
 
117
- void post("/admin/images.json", data)
118
- .then((response: ImageResponse) => {
119
- setUploading(false);
120
- if ("status" in response && response.status === "error") {
121
- error(`Error uploading image: ${response.error}`);
122
- } else if ("thumbnail_url" in response) {
123
- setSrc(response.thumbnail_url);
124
- setImage(response);
125
- }
126
- });
121
+ void post("/admin/images.json", data).then((response: ImageResponse) => {
122
+ setUploading(false);
123
+ if ("status" in response && response.status === "error") {
124
+ error(`Error uploading image: ${response.error}`);
125
+ } else if ("thumbnail_url" in response) {
126
+ setSrc(response.thumbnail_url);
127
+ setImage(response);
128
+ }
129
+ });
127
130
  };
128
131
 
129
132
  const classes = ["image-uploader"];
@@ -133,38 +136,39 @@ export default function ImageUploader(props: ImageUploaderProps) {
133
136
  classes.push("dragover");
134
137
  }
135
138
  return (
136
- <div className={classes.join(" ")}
137
- onDragOver={handleDragOver}
138
- onDragLeave={handleDragLeave}
139
- onDragEnd={handleDragEnd}
140
- onDrop={handleDrop}>
141
- <input type="hidden"
142
- name={props.attr}
143
- value={image ? image.id : ""} />
144
- {image &&
145
- <div className="image">
146
- <EditableImage image={image}
147
- src={src}
148
- width={props.width}
149
- caption={props.caption}
150
- locale={props.locale}
151
- locales={props.locales} />
152
- </div>}
139
+ <div
140
+ className={classes.join(" ")}
141
+ onDragOver={handleDragOver}
142
+ onDragLeave={handleDragLeave}
143
+ onDragEnd={handleDragEnd}
144
+ onDrop={handleDrop}>
145
+ <input type="hidden" name={props.attr} value={image ? image.id : ""} />
146
+ {image && (
147
+ <div className="image">
148
+ <EditableImage
149
+ image={image}
150
+ src={src}
151
+ width={props.width}
152
+ caption={props.caption}
153
+ locale={props.locale}
154
+ locales={props.locales}
155
+ />
156
+ </div>
157
+ )}
153
158
  <div className="ui-wrapper">
154
- {uploading && (
155
- <div className="ui">
156
- Uploading image...
157
- </div>
158
- )}
159
+ {uploading && <div className="ui">Uploading image...</div>}
159
160
  {!uploading && (
160
161
  <div className="ui">
161
- <FileUploadButton type="image"
162
- multiline={true}
163
- callback={receiveFiles} />
162
+ <FileUploadButton
163
+ type="image"
164
+ multiline={true}
165
+ callback={receiveFiles}
166
+ />
164
167
  {image && (
165
- <a className="delete remove-image"
166
- href="#"
167
- onClick={handleRemove}>
168
+ <a
169
+ className="delete remove-image"
170
+ href="#"
171
+ onClick={handleRemove}>
168
172
  Remove image
169
173
  </a>
170
174
  )}
@@ -37,12 +37,10 @@ export default function Modal() {
37
37
  return (
38
38
  <div className="modal-wrapper open">
39
39
  <div className="background" onClick={handleClose} />
40
- <div className="modal">
41
- {component}
42
- </div>
40
+ <div className="modal">{component}</div>
43
41
  </div>
44
42
  );
45
43
  } else {
46
- return (<div className="modal-wrapper"></div>);
44
+ return <div className="modal-wrapper"></div>;
47
45
  }
48
46
  }