pages_core 3.12.3 → 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 +1 -44
  4. data/app/assets/builds/pages_core/admin-dist.js.map +3 -3
  5. data/app/assets/builds/pages_core/admin.css +79 -46
  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 +17 -16
  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
@@ -6,9 +6,9 @@ export default class DateRangeSelect extends React.Component {
6
6
  super(props);
7
7
  this.state = {
8
8
  startsAt: this.parseDate(props.startsAt) || this.defaultDate(),
9
- endsAt: this.parseDate(props.endsAt) || this.defaultDate(60),
9
+ endsAt: this.parseDate(props.endsAt) || this.defaultDate(60),
10
10
  startTime: "",
11
- endTime: ""
11
+ endTime: ""
12
12
  };
13
13
  this.state.startTime = this.timeToString(this.state.startsAt);
14
14
  this.state.endTime = this.timeToString(this.state.endsAt);
@@ -33,8 +33,9 @@ export default class DateRangeSelect extends React.Component {
33
33
  defaultDate(offset = 0) {
34
34
  let coeff = 1000 * 60 * 60;
35
35
  return new Date(
36
- (Math.round((new Date()).getTime() / coeff) * coeff) + coeff +
37
- (1000 * 60 * offset)
36
+ Math.round(new Date().getTime() / coeff) * coeff +
37
+ coeff +
38
+ 1000 * 60 * offset
38
39
  );
39
40
  }
40
41
 
@@ -49,8 +50,10 @@ export default class DateRangeSelect extends React.Component {
49
50
  if (Object.prototype.hasOwnProperty.call(options, "date")) {
50
51
  newDate.setDate(options.date);
51
52
  }
52
- if (Object.prototype.hasOwnProperty.call(options, "time") &&
53
- options.time.match(/^[\d]{1,2}(:[\d]{1,2})?$/)) {
53
+ if (
54
+ Object.prototype.hasOwnProperty.call(options, "time") &&
55
+ options.time.match(/^[\d]{1,2}(:[\d]{1,2})?$/)
56
+ ) {
54
57
  newDate.setHours(options.time.split(":")[0]);
55
58
  newDate.setMinutes(options.time.split(":")[1] || 0);
56
59
  }
@@ -58,7 +61,9 @@ export default class DateRangeSelect extends React.Component {
58
61
  }
59
62
 
60
63
  parseDate(str) {
61
- if (!str) { return; }
64
+ if (!str) {
65
+ return;
66
+ }
62
67
  return new Date(str);
63
68
  }
64
69
 
@@ -75,40 +80,50 @@ export default class DateRangeSelect extends React.Component {
75
80
  }
76
81
 
77
82
  startsAtToString() {
78
- if (this.props.disabled) { return ""; }
83
+ if (this.props.disabled) {
84
+ return "";
85
+ }
79
86
  return this.state.startsAt.toJSON();
80
87
  }
81
88
 
82
89
  endsAtToString() {
83
- if (this.props.disabled) { return ""; }
90
+ if (this.props.disabled) {
91
+ return "";
92
+ }
84
93
  return this.state.endsAt.toJSON();
85
94
  }
86
95
 
87
96
  renderDateSelect(key, date, handleChange) {
88
97
  return (
89
98
  <div className="date-select">
90
- <select value={date.getMonth()}
91
- onChange={e => handleChange({ month: e.target.value })}
92
- disabled={this.props.disabled}>
99
+ <select
100
+ value={date.getMonth()}
101
+ onChange={(e) => handleChange({ month: e.target.value })}
102
+ disabled={this.props.disabled}>
93
103
  {this.monthOptions().map((m, i) => (
94
- <option key={key + "-month-" + i}
95
- value={i}>{m}</option>
104
+ <option key={key + "-month-" + i} value={i}>
105
+ {m}
106
+ </option>
96
107
  ))}
97
108
  </select>
98
- <select value={date.getDate()}
99
- onChange={e => handleChange({ date: e.target.value })}
100
- disabled={this.props.disabled}>
101
- {this.dayOptions().map(d => (
102
- <option key={key + "-date-" + d}
103
- value={d}>{d}</option>
109
+ <select
110
+ value={date.getDate()}
111
+ onChange={(e) => handleChange({ date: e.target.value })}
112
+ disabled={this.props.disabled}>
113
+ {this.dayOptions().map((d) => (
114
+ <option key={key + "-date-" + d} value={d}>
115
+ {d}
116
+ </option>
104
117
  ))}
105
118
  </select>
106
- <select value={date.getFullYear()}
107
- onChange={e => handleChange({ year: e.target.value })}
108
- disabled={this.props.disabled}>
109
- {this.yearOptions().map(y => (
110
- <option key={key + "-year-" + y}
111
- value={y}>{y}</option>
119
+ <select
120
+ value={date.getFullYear()}
121
+ onChange={(e) => handleChange({ year: e.target.value })}
122
+ disabled={this.props.disabled}>
123
+ {this.yearOptions().map((y) => (
124
+ <option key={key + "-year-" + y} value={y}>
125
+ {y}
126
+ </option>
112
127
  ))}
113
128
  </select>
114
129
  </div>
@@ -118,37 +133,49 @@ export default class DateRangeSelect extends React.Component {
118
133
  render() {
119
134
  return (
120
135
  <div className="date-range-select">
121
- <input type="hidden"
122
- name={this.props.objectName + "[starts_at]"}
123
- value={this.startsAtToString()} />
124
- <input type="hidden"
125
- name={this.props.objectName + "[ends_at]"}
126
- value={this.endsAtToString()} />
136
+ <input
137
+ type="hidden"
138
+ name={this.props.objectName + "[starts_at]"}
139
+ value={this.startsAtToString()}
140
+ />
141
+ <input
142
+ type="hidden"
143
+ name={this.props.objectName + "[ends_at]"}
144
+ value={this.endsAtToString()}
145
+ />
127
146
  <div className="date">
128
- {this.renderDateSelect("starts-at",
129
- this.state.startsAt,
130
- this.changeStartsAt)}
147
+ {this.renderDateSelect(
148
+ "starts-at",
149
+ this.state.startsAt,
150
+ this.changeStartsAt
151
+ )}
131
152
  {!this.props.disableTime && (
132
- <input type="text"
133
- size="5"
134
- value={this.state.startTime}
135
- disabled={this.props.disabled}
136
- onChange={e => this.setState({ startTime: e.target.value })}
137
- onBlur={e => this.changeStartsAt({ time: e.target.value })} />
153
+ <input
154
+ type="text"
155
+ size="5"
156
+ value={this.state.startTime}
157
+ disabled={this.props.disabled}
158
+ onChange={(e) => this.setState({ startTime: e.target.value })}
159
+ onBlur={(e) => this.changeStartsAt({ time: e.target.value })}
160
+ />
138
161
  )}
139
162
  </div>
140
163
  <span className="to">to</span>
141
164
  <div className="date">
142
- {this.renderDateSelect("ends-at",
143
- this.state.endsAt,
144
- this.changeEndsAt)}
165
+ {this.renderDateSelect(
166
+ "ends-at",
167
+ this.state.endsAt,
168
+ this.changeEndsAt
169
+ )}
145
170
  {!this.props.disableTime && (
146
- <input type="text"
147
- size="5"
148
- value={this.state.endTime}
149
- disabled={this.props.disabled}
150
- onChange={e => this.setState({ endTime: e.target.value })}
151
- onBlur={e => this.changeEndsAt({ time: e.target.value })} />
171
+ <input
172
+ type="text"
173
+ size="5"
174
+ value={this.state.endTime}
175
+ disabled={this.props.disabled}
176
+ onChange={(e) => this.setState({ endTime: e.target.value })}
177
+ onBlur={(e) => this.changeEndsAt({ time: e.target.value })}
178
+ />
152
179
  )}
153
180
  </div>
154
181
  </div>
@@ -162,13 +189,26 @@ export default class DateRangeSelect extends React.Component {
162
189
  // Returns an array with years from 2000 to 10 years from now.
163
190
  yearOptions() {
164
191
  let start = 2000;
165
- return Array.apply(null, Array((new Date()).getFullYear() - start + 11))
166
- .map((_, i) => i + start);
192
+ return Array.apply(null, Array(new Date().getFullYear() - start + 11)).map(
193
+ (_, i) => i + start
194
+ );
167
195
  }
168
196
 
169
197
  monthOptions() {
170
- return(["January", "February", "March", "April", "May", "June", "July",
171
- "August", "September", "October", "November", "December"]);
198
+ return [
199
+ "January",
200
+ "February",
201
+ "March",
202
+ "April",
203
+ "May",
204
+ "June",
205
+ "July",
206
+ "August",
207
+ "September",
208
+ "October",
209
+ "November",
210
+ "December"
211
+ ];
172
212
  }
173
213
 
174
214
  dayOptions() {
@@ -5,13 +5,13 @@ import useModalStore from "../stores/useModalStore";
5
5
  import { Locale, ImageResource } from "../types";
6
6
 
7
7
  interface EditableImageProps {
8
- image: ImageResource,
9
- src: string,
10
- caption: boolean,
11
- locale: string,
12
- locales: Record<string, Locale>,
13
- width: number,
14
- onUpdate?: (newImage: ImageResource, src: string) => void
8
+ image: ImageResource;
9
+ src: string;
10
+ caption: boolean;
11
+ locale: string;
12
+ locales: Record<string, Locale>;
13
+ width: number;
14
+ onUpdate?: (newImage: ImageResource, src: string) => void;
15
15
  }
16
16
 
17
17
  export default function EditableImage(props: EditableImageProps) {
@@ -43,7 +43,8 @@ export default function EditableImage(props: EditableImageProps) {
43
43
  caption={props.caption}
44
44
  locale={props.locale}
45
45
  locales={props.locales}
46
- onUpdate={updateImage} />
46
+ onUpdate={updateImage}
47
+ />
47
48
  );
48
49
  };
49
50
 
@@ -51,14 +52,17 @@ export default function EditableImage(props: EditableImageProps) {
51
52
 
52
53
  return (
53
54
  <div className="editable-image">
54
- {altWarning &&
55
- <span className="alt-warning" title="Alternative text is missing">
56
- <i className="fa-solid fa-triangle-exclamation icon" />
57
- </span>}
58
- <img src={src}
59
- width={props.width}
60
- height={height()}
61
- onClick={handleClick} />
55
+ {altWarning && (
56
+ <span className="alt-warning" title="Alternative text is missing">
57
+ <i className="fa-solid fa-triangle-exclamation icon" />
58
+ </span>
59
+ )}
60
+ <img
61
+ src={src}
62
+ width={props.width}
63
+ height={height()}
64
+ onClick={handleClick}
65
+ />
62
66
  </div>
63
67
  );
64
68
  }
@@ -1,10 +1,10 @@
1
1
  import React, { ChangeEvent, useRef } from "react";
2
2
 
3
3
  interface FileUploadButtonProps {
4
- callback: (files: File[]) => void,
5
- type: string,
6
- multiple: boolean,
7
- multiline: boolean
4
+ callback: (files: File[]) => void;
5
+ type: string;
6
+ multiple: boolean;
7
+ multiline: boolean;
8
8
  }
9
9
 
10
10
  export default function FileUploadButton(props: FileUploadButtonProps) {
@@ -32,15 +32,15 @@ export default function FileUploadButton(props: FileUploadButtonProps) {
32
32
  Drag and drop {props.type || "file"}
33
33
  {props.multiple && "s"} here, or
34
34
  {props.multiline && <br />}
35
- <button onClick={triggerDialog}>
36
- choose a file
37
- </button>
35
+ <button onClick={triggerDialog}>choose a file</button>
38
36
  </span>
39
- <input type="file"
40
- onChange={handleChange}
41
- ref={inputRef}
42
- style={{ display: "none" }}
43
- multiple={props.multiple || false} />
37
+ <input
38
+ type="file"
39
+ onChange={handleChange}
40
+ ref={inputRef}
41
+ style={{ display: "none" }}
42
+ multiple={props.multiple || false}
43
+ />
44
44
  </div>
45
45
  );
46
46
  }
@@ -1,16 +1,16 @@
1
1
  import React, { useRef, useState } from "react";
2
2
 
3
3
  interface Position {
4
- x: number,
5
- y: number,
4
+ x: number;
5
+ y: number;
6
6
  }
7
7
 
8
8
  interface FocalPointProps {
9
- x: number,
10
- y: number,
11
- onChange: (pos: Position) => void,
12
- width: number,
13
- height: number
9
+ x: number;
10
+ y: number;
11
+ onChange: (pos: Position) => void;
12
+ width: number;
13
+ height: number;
14
14
  }
15
15
 
16
16
  function clamp(val: number, min: number, max: number): number {
@@ -27,7 +27,10 @@ export default function FocalPoint(props: FocalPointProps) {
27
27
  const { width, height, onChange } = props;
28
28
 
29
29
  const [dragging, setDragging] = useState(false);
30
- const [position, setPosition] = useState<Position>({ x: props.x, y: props.y });
30
+ const [position, setPosition] = useState<Position>({
31
+ x: props.x,
32
+ y: props.y
33
+ });
31
34
 
32
35
  const containerRef = useRef<HTMLDivElement>();
33
36
  const pointRef = useRef<HTMLDivElement>();
@@ -49,7 +52,7 @@ export default function FocalPoint(props: FocalPointProps) {
49
52
 
50
53
  const drag = (evt: TouchEvent | MouseEvent) => {
51
54
  if (dragging) {
52
- let x: number , y: number;
55
+ let x: number, y: number;
53
56
  const containerSize = containerRef.current.getBoundingClientRect();
54
57
  evt.preventDefault();
55
58
 
@@ -78,17 +81,16 @@ export default function FocalPoint(props: FocalPointProps) {
78
81
  };
79
82
 
80
83
  return (
81
- <div className="focal-editor"
82
- ref={containerRef}
83
- onTouchStart={dragStart}
84
- onTouchEnd={dragEnd}
85
- onTouchMove={drag}
86
- onMouseDown={dragStart}
87
- onMouseUp={dragEnd}
88
- onMouseMove={drag}>
89
- <div className="focal-point"
90
- style={pointStyle}
91
- ref={pointRef} />
84
+ <div
85
+ className="focal-editor"
86
+ ref={containerRef}
87
+ onTouchStart={dragStart}
88
+ onTouchEnd={dragEnd}
89
+ onTouchMove={drag}
90
+ onMouseDown={dragStart}
91
+ onMouseUp={dragEnd}
92
+ onMouseMove={drag}>
93
+ <div className="focal-point" style={pointStyle} ref={pointRef} />
92
94
  </div>
93
95
  );
94
96
  }
@@ -5,12 +5,12 @@ import { cropSize, CropSize, CropState, Position, Size } from "./useCrop";
5
5
  import FocalPoint from "./FocalPoint";
6
6
 
7
7
  interface ImageProps {
8
- containerSize: Size,
9
- croppedImage: string,
10
- cropState: CropState,
11
- focalPoint: Position,
12
- setCrop: (crop: CropSize) => void,
13
- setFocal: (focal: Position) => void
8
+ containerSize: Size;
9
+ croppedImage: string;
10
+ cropState: CropState;
11
+ focalPoint: Position;
12
+ setCrop: (crop: CropSize) => void;
13
+ setFocal: (focal: Position) => void;
14
14
  }
15
15
 
16
16
  export default function Image(props: ImageProps) {
@@ -40,25 +40,29 @@ export default function Image(props: ImageProps) {
40
40
  if (props.cropState.cropping) {
41
41
  return (
42
42
  <div className="image-wrapper" style={style}>
43
- <ReactCrop src={props.cropState.image.uncropped_url}
44
- crop={cropSize(props.cropState)}
45
- minWidth={10}
46
- minHeight={10}
47
- onChange={props.setCrop} />
43
+ <ReactCrop
44
+ src={props.cropState.image.uncropped_url}
45
+ crop={cropSize(props.cropState)}
46
+ minWidth={10}
47
+ minHeight={10}
48
+ onChange={props.setCrop}
49
+ />
48
50
  </div>
49
51
  );
50
52
  } else {
51
53
  return (
52
54
  <div className="image-wrapper" style={style}>
53
55
  {props.focalPoint && (
54
- <FocalPoint width={width} height={height}
55
- x={props.focalPoint.x}
56
- y={props.focalPoint.y}
57
- onChange={props.setFocal} />
56
+ <FocalPoint
57
+ width={width}
58
+ height={height}
59
+ x={props.focalPoint.x}
60
+ y={props.focalPoint.y}
61
+ onChange={props.setFocal}
62
+ />
58
63
  )}
59
64
  <img src={props.croppedImage} />
60
65
  </div>
61
66
  );
62
67
  }
63
-
64
68
  }
@@ -6,20 +6,26 @@ import { CropState } from "./useCrop";
6
6
  type Ratio = number | null;
7
7
 
8
8
  interface ToolbarProps {
9
- cropState: CropState,
10
- image: ImageResource,
11
- setAspect: (Ratio) => void,
12
- toggleCrop: (evt: Event) => void,
13
- toggleFocal: (evt: Event) => void
9
+ cropState: CropState;
10
+ image: ImageResource;
11
+ setAspect: (Ratio) => void;
12
+ toggleCrop: (evt: Event) => void;
13
+ toggleFocal: (evt: Event) => void;
14
14
  }
15
15
 
16
16
  export default function Toolbar(props: ToolbarProps) {
17
17
  const { cropping } = props.cropState;
18
18
 
19
19
  const aspectRatios = [
20
- ["Free", null], ["1:1", 1], ["3:2", 3/2], ["2:3", 2/3],
21
- ["4:3", 4/3], ["3:4", 3/4], ["5:4", 5/4], ["4:5", 4/5],
22
- ["16:9", 16/9]
20
+ ["Free", null],
21
+ ["1:1", 1],
22
+ ["3:2", 3 / 2],
23
+ ["2:3", 2 / 3],
24
+ ["4:3", 4 / 3],
25
+ ["3:4", 3 / 4],
26
+ ["5:4", 5 / 4],
27
+ ["4:5", 4 / 5],
28
+ ["16:9", 16 / 9]
23
29
  ];
24
30
 
25
31
  const updateAspect = (ratio: Ratio) => (evt: Event) => {
@@ -39,34 +45,36 @@ export default function Toolbar(props: ToolbarProps) {
39
45
  {width}x{height} {format}
40
46
  </span>
41
47
  </div>
42
- <button title="Crop image"
43
- onClick={props.toggleCrop}
44
- className={cropping ? "active" : ""}>
48
+ <button
49
+ title="Crop image"
50
+ onClick={props.toggleCrop}
51
+ className={cropping ? "active" : ""}>
45
52
  <i className="fa-solid fa-crop" />
46
53
  </button>
47
- <button disabled={cropping}
48
- title="Toggle focal point"
49
- onClick={props.toggleFocal}>
54
+ <button
55
+ disabled={cropping}
56
+ title="Toggle focal point"
57
+ onClick={props.toggleFocal}>
50
58
  <i className="fa-solid fa-bullseye" />
51
59
  </button>
52
- <a href={props.image.original_url}
53
- className="button"
54
- title="Download original image"
55
- disabled={cropping}
56
- download={props.image.filename}
57
- onClick={evt => cropping && evt.preventDefault()}>
60
+ <a
61
+ href={props.image.original_url}
62
+ className="button"
63
+ title="Download original image"
64
+ disabled={cropping}
65
+ download={props.image.filename}
66
+ onClick={(evt) => cropping && evt.preventDefault()}>
58
67
  <i className="fa-solid fa-download" />
59
68
  </a>
60
69
  </div>
61
70
  {cropping && (
62
71
  <div className="aspect-ratios toolbar">
63
- <div className="label">
64
- Lock aspect ratio:
65
- </div>
66
- {aspectRatios.map(ratio => (
67
- <button key={ratio[0]}
68
- className={(ratio[1] == props.cropState.aspect) ? "active" : ""}
69
- onClick={updateAspect(ratio[1])}>
72
+ <div className="label">Lock aspect ratio:</div>
73
+ {aspectRatios.map((ratio) => (
74
+ <button
75
+ key={ratio[0]}
76
+ className={ratio[1] == props.cropState.aspect ? "active" : ""}
77
+ onClick={updateAspect(ratio[1])}>
70
78
  {ratio[0]}
71
79
  </button>
72
80
  ))}