pages_core 3.12.1 → 3.12.2
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/assets/builds/pages_core/admin-dist.js +59 -14
- data/app/assets/builds/pages_core/admin-dist.js.map +7 -0
- data/app/assets/builds/pages_core/admin.css +39 -0
- data/app/assets/stylesheets/pages_core/admin/components/search.css +27 -0
- data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +6 -0
- data/app/controllers/admin/pages_controller.rb +12 -11
- data/app/controllers/concerns/pages_core/rss_controller.rb +17 -1
- data/app/controllers/pages_core/admin_controller.rb +6 -0
- data/app/controllers/pages_core/frontend/pages_controller.rb +9 -5
- data/app/controllers/pages_core/sitemaps_controller.rb +3 -5
- data/app/helpers/admin/pages_helper.rb +32 -0
- data/app/javascript/admin-dist.ts +2 -0
- data/app/javascript/components/Attachments/{Attachment.jsx → Attachment.tsx} +42 -33
- data/app/javascript/components/Attachments/{AttachmentEditor.jsx → AttachmentEditor.tsx} +23 -23
- data/app/javascript/components/{EditableImage.jsx → EditableImage.tsx} +27 -24
- data/app/javascript/components/{FileUploadButton.jsx → FileUploadButton.tsx} +15 -16
- data/app/javascript/components/ImageCropper/FocalPoint.tsx +94 -0
- data/app/javascript/components/ImageCropper/{Image.jsx → Image.tsx} +13 -14
- data/app/javascript/components/ImageCropper/{Toolbar.jsx → Toolbar.tsx} +16 -12
- data/app/javascript/components/ImageCropper/{useCrop.js → useCrop.ts} +80 -37
- data/app/javascript/components/{ImageCropper.jsx → ImageCropper.tsx} +17 -15
- data/app/javascript/components/ImageEditor/{Form.jsx → Form.tsx} +24 -23
- data/app/javascript/components/{ImageEditor.jsx → ImageEditor.tsx} +17 -15
- data/app/javascript/components/ImageGrid/{DragElement.jsx → DragElement.tsx} +12 -10
- data/app/javascript/components/ImageGrid/{GridImage.jsx → GridImage.tsx} +40 -30
- data/app/javascript/components/ImageGrid/{Placeholder.jsx → Placeholder.tsx} +5 -6
- data/app/javascript/components/ImageGrid.jsx +3 -4
- data/app/javascript/components/{ImageUploader.jsx → ImageUploader.tsx} +46 -41
- data/app/javascript/components/Modal.tsx +48 -0
- data/app/javascript/components/PageImages.tsx +28 -0
- data/app/javascript/components/{PageTreeDraggable.jsx → PageTree/Draggable.tsx} +79 -57
- data/app/javascript/components/{PageTreeNode.jsx → PageTree/Node.tsx} +79 -70
- data/app/javascript/components/PageTree/types.ts +15 -0
- data/app/javascript/components/PageTree.tsx +206 -0
- data/app/javascript/components/RichTextToolbarButton.tsx +17 -0
- data/app/javascript/components/TagEditor/{AddTagForm.jsx → AddTagForm.tsx} +9 -10
- data/app/javascript/components/TagEditor/{Tag.jsx → Tag.tsx} +8 -9
- data/app/javascript/components/{TagEditor.jsx → TagEditor.tsx} +12 -13
- data/app/javascript/components/Toast.tsx +61 -0
- data/app/javascript/components/drag/{draggedOrder.js → draggedOrder.ts} +22 -12
- data/app/javascript/components/drag/types.ts +28 -0
- data/app/javascript/components/drag/{useDragCollection.js → useDragCollection.ts} +40 -22
- data/app/javascript/components/drag/{useDragUploader.js → useDragUploader.ts} +34 -25
- data/app/javascript/components/drag/useDraggable.ts +21 -0
- data/app/javascript/components/{drag.js → drag.ts} +1 -0
- data/app/javascript/controllers/{EditPageController.js → EditPageController.ts} +3 -1
- data/app/javascript/controllers/{LoginController.js → LoginController.ts} +7 -3
- data/app/javascript/controllers/{MainController.js → MainController.ts} +19 -14
- data/app/javascript/{index.js → index.ts} +8 -7
- data/app/javascript/lib/{Tree.js → Tree.ts} +106 -85
- data/app/javascript/lib/{copyToClipboard.js → copyToClipboard.ts} +1 -1
- data/app/javascript/lib/{readyHandler.js → readyHandler.ts} +4 -2
- data/app/javascript/lib/{request.js → request.ts} +11 -5
- data/app/javascript/stores/useModalStore.ts +15 -0
- data/app/javascript/stores/useToastStore.ts +26 -0
- data/app/javascript/stores.ts +2 -0
- data/app/javascript/types.ts +30 -0
- data/app/policies/page_policy.rb +4 -0
- data/app/views/admin/calendars/_sidebar.html.erb +3 -0
- data/app/views/admin/news/_sidebar.html.erb +3 -0
- data/app/views/admin/pages/_list_item.html.erb +4 -22
- data/app/views/admin/pages/_search_bar.html.erb +12 -0
- data/app/views/admin/pages/index.html.erb +3 -0
- data/app/views/admin/pages/search.html.erb +54 -0
- data/app/views/feeds/pages.rss.builder +3 -9
- data/config/routes.rb +1 -0
- data/lib/pages_core/configuration/pages.rb +0 -1
- data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +33 -17
- data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +0 -1
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +40 -0
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +68 -0
- data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +17 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.postcss.css +4 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +24 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/layout.css +21 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +5 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +5 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +18 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +6 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +65 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +131 -0
- data/lib/rails/generators/pages_core/install/templates/pages_initializer.rb +0 -3
- metadata +68 -62
- data/app/javascript/admin-dist.js +0 -2
- data/app/javascript/components/ImageCropper/FocalPoint.jsx +0 -93
- data/app/javascript/components/Modal.jsx +0 -59
- data/app/javascript/components/PageImages.jsx +0 -25
- data/app/javascript/components/PageTree.jsx +0 -196
- data/app/javascript/components/RichTextToolbarButton.jsx +0 -20
- data/app/javascript/components/Toast.jsx +0 -72
- data/app/javascript/components/drag/useDraggable.js +0 -17
- data/app/javascript/stores/ModalStore.jsx +0 -12
- data/app/javascript/stores/ToastStore.jsx +0 -14
- data/app/javascript/stores.js +0 -2
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/GridOverlay.js +0 -66
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/ResponsiveEmbeds.js +0 -72
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.sass.scss +0 -15
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.scss +0 -12
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.scss +0 -26
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/breakpoints.scss +0 -42
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/clearfix.scss +0 -7
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/fonts.scss +0 -32
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid.scss +0 -168
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid_overlay.scss +0 -44
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.scss +0 -8
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.scss +0 -90
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/vendor/normalize.css +0 -349
- /data/app/javascript/components/Attachments/{Placeholder.jsx → Placeholder.tsx} +0 -0
- /data/app/javascript/components/ImageGrid/{FilePlaceholder.jsx → FilePlaceholder.tsx} +0 -0
- /data/app/javascript/{components.js → components.ts} +0 -0
- /data/app/javascript/{hooks.js → hooks.ts} +0 -0
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
import copyToClipboard from "../../lib/copyToClipboard";
|
|
4
3
|
import EditableImage from "../EditableImage";
|
|
5
|
-
import
|
|
4
|
+
import useToastStore from "../../stores/useToastStore";
|
|
5
|
+
import { ImageResource, Locale } from "../../types";
|
|
6
6
|
import Placeholder from "./Placeholder";
|
|
7
7
|
|
|
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
|
|
15
|
+
}
|
|
16
|
+
|
|
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
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function GridImage(props: GridImageProps) {
|
|
11
33
|
const { attributeName, draggable } = props;
|
|
12
34
|
const record = draggable.record;
|
|
13
35
|
const image = record.image;
|
|
14
36
|
|
|
37
|
+
const notice = useToastStore((state) => state.notice);
|
|
38
|
+
|
|
15
39
|
const [src, setSrc] = useState(record.src || null);
|
|
16
40
|
|
|
17
41
|
const dragAttrs = useDraggable(draggable, props.startDrag);
|
|
@@ -24,22 +48,20 @@ export default function GridImage(props) {
|
|
|
24
48
|
}
|
|
25
49
|
}, []);
|
|
26
50
|
|
|
27
|
-
const copyEmbed = (evt) => {
|
|
51
|
+
const copyEmbed = (evt: Event) => {
|
|
28
52
|
evt.preventDefault();
|
|
29
53
|
copyToClipboard(`[image:${image.id}]`);
|
|
30
|
-
|
|
31
|
-
type: "NOTICE", message: "Embed code copied to clipboard"
|
|
32
|
-
});
|
|
54
|
+
notice("Embed code copied to clipboard");
|
|
33
55
|
};
|
|
34
56
|
|
|
35
|
-
const deleteImage = (evt) => {
|
|
57
|
+
const deleteImage = (evt: Event) => {
|
|
36
58
|
evt.preventDefault();
|
|
37
59
|
if (props.deleteImage) {
|
|
38
60
|
props.deleteImage();
|
|
39
61
|
}
|
|
40
62
|
};
|
|
41
63
|
|
|
42
|
-
|
|
64
|
+
const classes = ["grid-image"];
|
|
43
65
|
if (props.placeholder) {
|
|
44
66
|
classes.push("placeholder");
|
|
45
67
|
}
|
|
@@ -64,13 +86,15 @@ export default function GridImage(props) {
|
|
|
64
86
|
<Placeholder src={src} />}
|
|
65
87
|
{image &&
|
|
66
88
|
<>
|
|
67
|
-
<EditableImage
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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} />
|
|
74
98
|
<div className="actions">
|
|
75
99
|
{props.showEmbed && (
|
|
76
100
|
<button onClick={copyEmbed}>
|
|
@@ -87,17 +111,3 @@ export default function GridImage(props) {
|
|
|
87
111
|
</div>
|
|
88
112
|
);
|
|
89
113
|
}
|
|
90
|
-
GridImage.propTypes = {
|
|
91
|
-
draggable: PropTypes.object,
|
|
92
|
-
deleteImage: PropTypes.func,
|
|
93
|
-
startDrag: PropTypes.func,
|
|
94
|
-
locale: PropTypes.string,
|
|
95
|
-
locales: PropTypes.object,
|
|
96
|
-
onUpdate: PropTypes.func,
|
|
97
|
-
attributeName: PropTypes.string,
|
|
98
|
-
placeholder: PropTypes.bool,
|
|
99
|
-
enablePrimary: PropTypes.bool,
|
|
100
|
-
showEmbed: PropTypes.bool,
|
|
101
|
-
primary: PropTypes.bool,
|
|
102
|
-
position: PropTypes.number,
|
|
103
|
-
};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
interface PlaceholderProps {
|
|
4
|
+
src: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default function Placeholder(props: PlaceholderProps) {
|
|
5
8
|
if (props.src) {
|
|
6
9
|
return (
|
|
7
10
|
<div className="temp-image">
|
|
@@ -17,7 +20,3 @@ export default function Placeholder(props) {
|
|
|
17
20
|
);
|
|
18
21
|
}
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
Placeholder.propTypes = {
|
|
22
|
-
src: PropTypes.string
|
|
23
|
-
};
|
|
@@ -4,7 +4,7 @@ import FileUploadButton from "./FileUploadButton";
|
|
|
4
4
|
import DragElement from "./ImageGrid/DragElement";
|
|
5
5
|
import FilePlaceholder from "./ImageGrid/FilePlaceholder";
|
|
6
6
|
import GridImage from "./ImageGrid/GridImage";
|
|
7
|
-
import
|
|
7
|
+
import useToastStore from "../stores/useToastStore";
|
|
8
8
|
import { post } from "../lib/request";
|
|
9
9
|
|
|
10
10
|
import { createDraggable,
|
|
@@ -49,6 +49,7 @@ export default function ImageGrid(props) {
|
|
|
49
49
|
const primary = useDragCollection(initPrimary);
|
|
50
50
|
const images = useDragCollection(initImages);
|
|
51
51
|
const [deleted, setDeleted] = useState([]);
|
|
52
|
+
const error = useToastStore((state) => state.error);
|
|
52
53
|
|
|
53
54
|
const containerRef = useRef();
|
|
54
55
|
|
|
@@ -99,9 +100,7 @@ export default function ImageGrid(props) {
|
|
|
99
100
|
post("/admin/images.json", data)
|
|
100
101
|
.then(json => {
|
|
101
102
|
if (json.status === "error") {
|
|
102
|
-
|
|
103
|
-
type: "ERROR", message: "Error uploading image: " + json.error
|
|
104
|
-
});
|
|
103
|
+
error("Error uploading image: " + json.error);
|
|
105
104
|
dispatchAll({ type: "remove", payload: draggable });
|
|
106
105
|
} else {
|
|
107
106
|
dispatchAll({
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
2
|
import EditableImage from "./EditableImage";
|
|
4
3
|
import FileUploadButton from "./FileUploadButton";
|
|
5
|
-
import
|
|
4
|
+
import useToastStore from "../stores/useToastStore";
|
|
5
|
+
import { ImageResource, Locale } from "../types";
|
|
6
6
|
import { post } from "../lib/request";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
type ImageResponse = ImageResource | { status: "error", error: string }
|
|
9
|
+
|
|
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
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getFiles(dt: DataTransfer): File[] {
|
|
22
|
+
const files: File[] = [];
|
|
10
23
|
if (dt.items) {
|
|
11
24
|
for (let i = 0; i < dt.items.length; i++) {
|
|
12
25
|
if (dt.items[i].kind == "file") {
|
|
@@ -21,13 +34,14 @@ function getFiles(dt) {
|
|
|
21
34
|
return files;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
export default function ImageUploader(props) {
|
|
37
|
+
export default function ImageUploader(props: ImageUploaderProps) {
|
|
25
38
|
const [uploading, setUploading] = useState(false);
|
|
26
39
|
const [dragover, setDragover] = useState(false);
|
|
27
40
|
const [image, setImage] = useState(props.image);
|
|
28
41
|
const [src, setSrc] = useState(props.src);
|
|
42
|
+
const error = useToastStore((state) => state.error);
|
|
29
43
|
|
|
30
|
-
const handleDragOver = (evt) => {
|
|
44
|
+
const handleDragOver = (evt: Event) => {
|
|
31
45
|
evt.preventDefault();
|
|
32
46
|
setDragover(true);
|
|
33
47
|
};
|
|
@@ -36,39 +50,44 @@ export default function ImageUploader(props) {
|
|
|
36
50
|
setDragover(false);
|
|
37
51
|
};
|
|
38
52
|
|
|
39
|
-
const handleDragEnd = (evt) => {
|
|
40
|
-
if (evt
|
|
41
|
-
|
|
42
|
-
evt.dataTransfer.items.
|
|
53
|
+
const handleDragEnd = (evt: Event) => {
|
|
54
|
+
if ("dataTransfer" in evt) {
|
|
55
|
+
if ("items" in evt.dataTransfer && "remove" in evt.dataTransfer.items) {
|
|
56
|
+
for (let i = 0; i < evt.dataTransfer.items.length; i++) {
|
|
57
|
+
evt.dataTransfer.items.remove(i);
|
|
58
|
+
}
|
|
59
|
+
} else if ("clearData" in evt.dataTransfer) {
|
|
60
|
+
evt.dataTransfer.clearData();
|
|
43
61
|
}
|
|
44
|
-
} else {
|
|
45
|
-
evt.dataTransfer.clearData();
|
|
46
62
|
}
|
|
47
63
|
setDragover(false);
|
|
48
64
|
};
|
|
49
65
|
|
|
50
|
-
const handleDrop = (evt) => {
|
|
51
|
-
let files =
|
|
66
|
+
const handleDrop = (evt: Event) => {
|
|
67
|
+
let files: File[] = [];
|
|
68
|
+
if ("dataTransfer" in evt) {
|
|
69
|
+
files = getFiles(evt.dataTransfer);
|
|
70
|
+
}
|
|
52
71
|
evt.preventDefault();
|
|
53
72
|
if (files.length > 0) {
|
|
54
73
|
uploadImage(files[0]);
|
|
55
74
|
}
|
|
56
75
|
};
|
|
57
76
|
|
|
58
|
-
const handleRemove = (evt) => {
|
|
77
|
+
const handleRemove = (evt: Event) => {
|
|
59
78
|
evt.preventDefault();
|
|
60
79
|
setImage(null);
|
|
61
80
|
setSrc(null);
|
|
62
81
|
};
|
|
63
82
|
|
|
64
|
-
const receiveFiles = (files) => {
|
|
83
|
+
const receiveFiles = (files: File[]) => {
|
|
65
84
|
if (files.length > 0) {
|
|
66
85
|
uploadImage(files[0]);
|
|
67
86
|
}
|
|
68
87
|
};
|
|
69
88
|
|
|
70
|
-
const uploadImage = (file) => {
|
|
71
|
-
|
|
89
|
+
const uploadImage = (file: File) => {
|
|
90
|
+
const validTypes = ["image/gif",
|
|
72
91
|
"image/jpeg",
|
|
73
92
|
"image/pjpeg",
|
|
74
93
|
"image/png",
|
|
@@ -80,10 +99,10 @@ export default function ImageUploader(props) {
|
|
|
80
99
|
return;
|
|
81
100
|
}
|
|
82
101
|
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
const locale = props.locale;
|
|
103
|
+
const locales = props.locales ? Object.keys(props.locales) : [locale];
|
|
85
104
|
|
|
86
|
-
|
|
105
|
+
const data = new FormData();
|
|
87
106
|
|
|
88
107
|
setImage(null);
|
|
89
108
|
setSrc(null);
|
|
@@ -95,22 +114,19 @@ export default function ImageUploader(props) {
|
|
|
95
114
|
data.append(`image[alternative][${l}]`, (props.alternative || ""));
|
|
96
115
|
});
|
|
97
116
|
|
|
98
|
-
post("/admin/images.json", data)
|
|
99
|
-
.then(response => {
|
|
117
|
+
void post("/admin/images.json", data)
|
|
118
|
+
.then((response: ImageResponse) => {
|
|
100
119
|
setUploading(false);
|
|
101
|
-
if (response.status === "error") {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
message: "Error uploading image: " + response.error
|
|
105
|
-
});
|
|
106
|
-
} else {
|
|
120
|
+
if ("status" in response && response.status === "error") {
|
|
121
|
+
error(`Error uploading image: ${response.error}`);
|
|
122
|
+
} else if ("thumbnail_url" in response) {
|
|
107
123
|
setSrc(response.thumbnail_url);
|
|
108
124
|
setImage(response);
|
|
109
125
|
}
|
|
110
126
|
});
|
|
111
127
|
};
|
|
112
128
|
|
|
113
|
-
|
|
129
|
+
const classes = ["image-uploader"];
|
|
114
130
|
if (uploading) {
|
|
115
131
|
classes.push("uploading");
|
|
116
132
|
} else if (dragover) {
|
|
@@ -158,14 +174,3 @@ export default function ImageUploader(props) {
|
|
|
158
174
|
</div>
|
|
159
175
|
);
|
|
160
176
|
}
|
|
161
|
-
|
|
162
|
-
ImageUploader.propTypes = {
|
|
163
|
-
locale: PropTypes.string,
|
|
164
|
-
locales: PropTypes.object,
|
|
165
|
-
image: PropTypes.object,
|
|
166
|
-
src: PropTypes.string,
|
|
167
|
-
width: PropTypes.number,
|
|
168
|
-
caption: PropTypes.bool,
|
|
169
|
-
attr: PropTypes.string,
|
|
170
|
-
alternative: PropTypes.string
|
|
171
|
-
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
import useModalStore from "../stores/useModalStore";
|
|
4
|
+
|
|
5
|
+
export default function Modal() {
|
|
6
|
+
const component = useModalStore((state) => state.component);
|
|
7
|
+
const close = useModalStore((state) => state.close);
|
|
8
|
+
|
|
9
|
+
const handleClose = (evt: Event) => {
|
|
10
|
+
evt.stopPropagation();
|
|
11
|
+
evt.preventDefault();
|
|
12
|
+
close();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const handleKeypress = (evt: KeyboardEvent) => {
|
|
16
|
+
if (component && (evt.key == "Escape" || evt.keyCode === 27)) {
|
|
17
|
+
handleClose(evt);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (component) {
|
|
23
|
+
document.body.classList.add("modal");
|
|
24
|
+
} else {
|
|
25
|
+
document.body.classList.remove("modal");
|
|
26
|
+
}
|
|
27
|
+
}, [component]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
window.addEventListener("keypress", handleKeypress);
|
|
31
|
+
return () => {
|
|
32
|
+
window.removeEventListener("keypress", handleKeypress);
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
if (component) {
|
|
37
|
+
return (
|
|
38
|
+
<div className="modal-wrapper open">
|
|
39
|
+
<div className="background" onClick={handleClose} />
|
|
40
|
+
<div className="modal">
|
|
41
|
+
{component}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
} else {
|
|
46
|
+
return (<div className="modal-wrapper"></div>);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ImageResource, Locale } from "../types";
|
|
3
|
+
import ImageGrid from "./ImageGrid";
|
|
4
|
+
|
|
5
|
+
interface PageImage {
|
|
6
|
+
id: number | null,
|
|
7
|
+
image: ImageResource
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface PageImagesProps {
|
|
11
|
+
locale: string,
|
|
12
|
+
locales: { [index: string]: Locale },
|
|
13
|
+
records: PageImage[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function PageImages(props: PageImagesProps) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="page-images">
|
|
19
|
+
<ImageGrid attribute="page[page_images_attributes]"
|
|
20
|
+
primaryAttribute="page[image_id]"
|
|
21
|
+
enablePrimary={true}
|
|
22
|
+
showEmbed={true}
|
|
23
|
+
locale={props.locale}
|
|
24
|
+
locales={props.locales}
|
|
25
|
+
records={props.records} />
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -25,19 +25,54 @@
|
|
|
25
25
|
SOFTWARE.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import React from "react";
|
|
29
|
-
import
|
|
30
|
-
import
|
|
28
|
+
import React, { Component } from "react";
|
|
29
|
+
import Tree, { TreeId, TreeIndex } from "../../lib/Tree";
|
|
30
|
+
import { Attributes, PageNode } from "./types";
|
|
31
|
+
import Node from "./Node";
|
|
32
|
+
|
|
33
|
+
interface DragState {
|
|
34
|
+
id: number | null,
|
|
35
|
+
x: number | null,
|
|
36
|
+
y: number | null,
|
|
37
|
+
w: number | null,
|
|
38
|
+
h: number | null,
|
|
39
|
+
scrollTop: number | null,
|
|
40
|
+
scrollLeft: number | null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface DraggableProps {
|
|
44
|
+
addChild: (index: TreeIndex) => void,
|
|
45
|
+
dir: string,
|
|
46
|
+
locale: string,
|
|
47
|
+
movedPage: (id: TreeId) => void,
|
|
48
|
+
paddingLeft: number,
|
|
49
|
+
toggleCollapsed: (id: TreeId) => void,
|
|
50
|
+
tree: Tree<PageNode>,
|
|
51
|
+
updatePage: (id: TreeId, attributes: Attributes) => void,
|
|
52
|
+
updateTree: (Tree) => void
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface DraggableState {
|
|
56
|
+
dragging: DragState
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default class Draggable extends Component<DraggableProps, DraggableState> {
|
|
60
|
+
_dragListener: (evt: MouseEvent) => void;
|
|
61
|
+
_dragEndListener: () => void;
|
|
62
|
+
_startX: number;
|
|
63
|
+
_startY: number;
|
|
64
|
+
_offsetX: number;
|
|
65
|
+
_offsetY: number;
|
|
66
|
+
dragging: DragState;
|
|
31
67
|
|
|
32
|
-
|
|
33
|
-
constructor(props) {
|
|
68
|
+
constructor(props: DraggableProps) {
|
|
34
69
|
super(props);
|
|
35
70
|
this.state = {
|
|
36
71
|
dragging: this.initDragging()
|
|
37
72
|
};
|
|
38
73
|
}
|
|
39
74
|
|
|
40
|
-
initDragging() {
|
|
75
|
+
initDragging(): DragState {
|
|
41
76
|
return {
|
|
42
77
|
id: null,
|
|
43
78
|
x: null,
|
|
@@ -50,11 +85,11 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
50
85
|
}
|
|
51
86
|
|
|
52
87
|
getDraggingDom() {
|
|
53
|
-
|
|
54
|
-
|
|
88
|
+
const tree = this.props.tree;
|
|
89
|
+
const dragging = this.state.dragging;
|
|
55
90
|
if (dragging && dragging.id) {
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
const draggingIndex = tree.getIndex(dragging.id);
|
|
92
|
+
const draggingStyles = {
|
|
58
93
|
top: dragging.y,
|
|
59
94
|
left: dragging.x,
|
|
60
95
|
width: dragging.w
|
|
@@ -62,7 +97,7 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
62
97
|
|
|
63
98
|
return (
|
|
64
99
|
<div className="draggable" style={draggingStyles}>
|
|
65
|
-
<
|
|
100
|
+
<Node
|
|
66
101
|
tree={tree}
|
|
67
102
|
index={draggingIndex}
|
|
68
103
|
paddingLeft={this.props.paddingLeft}
|
|
@@ -76,7 +111,7 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
76
111
|
|
|
77
112
|
render() {
|
|
78
113
|
const { tree, dir, locale } = this.props;
|
|
79
|
-
|
|
114
|
+
const dragging = this.state.dragging;
|
|
80
115
|
|
|
81
116
|
if (!tree) {
|
|
82
117
|
return (
|
|
@@ -85,11 +120,11 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
85
120
|
</div>
|
|
86
121
|
);
|
|
87
122
|
} else {
|
|
88
|
-
|
|
123
|
+
const root = tree.getIndex(1);
|
|
89
124
|
return (
|
|
90
125
|
<div className="page-tree">
|
|
91
126
|
{this.getDraggingDom()}
|
|
92
|
-
<
|
|
127
|
+
<Node
|
|
93
128
|
tree={tree}
|
|
94
129
|
index={root}
|
|
95
130
|
key={root.id}
|
|
@@ -106,8 +141,8 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
106
141
|
}
|
|
107
142
|
}
|
|
108
143
|
|
|
109
|
-
addChild(parent) {
|
|
110
|
-
|
|
144
|
+
addChild(parent: TreeIndex<PageNode>) {
|
|
145
|
+
const newNode = {
|
|
111
146
|
name: "",
|
|
112
147
|
status: 0,
|
|
113
148
|
editing: true,
|
|
@@ -120,9 +155,9 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
120
155
|
this.props.addChild(parent.id, newNode);
|
|
121
156
|
}
|
|
122
157
|
|
|
123
|
-
prevAddButtonCount(tree, index) {
|
|
158
|
+
prevAddButtonCount(tree: Tree, index: TreeIndex) {
|
|
124
159
|
let count = 0;
|
|
125
|
-
|
|
160
|
+
const parentNodes = [];
|
|
126
161
|
let pointer = tree.getIndex(index.parent);
|
|
127
162
|
while (pointer) {
|
|
128
163
|
parentNodes.push(pointer);
|
|
@@ -147,16 +182,16 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
147
182
|
}
|
|
148
183
|
|
|
149
184
|
scrollOffset() {
|
|
150
|
-
|
|
185
|
+
const dragging = this.state.dragging;
|
|
151
186
|
return {
|
|
152
187
|
top: document.body.scrollTop - dragging.scrollTop,
|
|
153
188
|
left: document.body.scrollLeft - dragging.scrollLeft
|
|
154
189
|
};
|
|
155
190
|
}
|
|
156
191
|
|
|
157
|
-
drag(e) {
|
|
192
|
+
drag(e: MouseEvent) {
|
|
158
193
|
if (this._start) {
|
|
159
|
-
|
|
194
|
+
const distance = Math.abs(e.clientX - this._offsetX) +
|
|
160
195
|
Math.abs(e.clientY - this._offsetY);
|
|
161
196
|
if (distance >= 15) {
|
|
162
197
|
this.setState({
|
|
@@ -168,27 +203,27 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
168
203
|
}
|
|
169
204
|
}
|
|
170
205
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
206
|
+
const tree = this.props.tree;
|
|
207
|
+
const dragging = this.state.dragging;
|
|
208
|
+
const paddingLeft = this.props.paddingLeft;
|
|
209
|
+
let newIndex: TreeIndex = null;
|
|
210
|
+
let index = tree.getIndex(dragging.id);
|
|
211
|
+
const collapsed = index.node.collapsed;
|
|
177
212
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
213
|
+
const _startX = this._startX;
|
|
214
|
+
const _startY = this._startY;
|
|
215
|
+
const _offsetX = this._offsetX;
|
|
216
|
+
const _offsetY = this._offsetY;
|
|
182
217
|
|
|
183
|
-
|
|
218
|
+
const pos = {
|
|
184
219
|
x: _startX + e.clientX - _offsetX + this.scrollOffset().left,
|
|
185
220
|
y: _startY + e.clientY - _offsetY + this.scrollOffset().top
|
|
186
221
|
};
|
|
187
222
|
dragging.x = pos.x;
|
|
188
223
|
dragging.y = pos.y;
|
|
189
224
|
|
|
190
|
-
|
|
191
|
-
|
|
225
|
+
const diffX = dragging.x - paddingLeft/2 - (index.left-2) * paddingLeft;
|
|
226
|
+
const diffY = dragging.y - dragging.h/2 - (index.top-2 + this.prevAddButtonCount(tree, index)) * dragging.h;
|
|
192
227
|
|
|
193
228
|
if (diffX < 0) {
|
|
194
229
|
// left
|
|
@@ -197,8 +232,8 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
197
232
|
}
|
|
198
233
|
} else if (diffX > paddingLeft) {
|
|
199
234
|
// right
|
|
200
|
-
if (index
|
|
201
|
-
|
|
235
|
+
if ("prev" in index) {
|
|
236
|
+
const prev = tree.getIndex(index.prev);
|
|
202
237
|
|
|
203
238
|
if (!prev.node.leaf && !prev.node.collapsed) {
|
|
204
239
|
newIndex = tree.move(index.id, index.prev, "append");
|
|
@@ -214,11 +249,11 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
214
249
|
|
|
215
250
|
if (diffY < (0 - dragging.h * 0.5)) {
|
|
216
251
|
// up
|
|
217
|
-
|
|
252
|
+
const above = tree.getNodeByTop(index.top-1);
|
|
218
253
|
newIndex = tree.move(index.id, above.id, "before");
|
|
219
254
|
} else if (diffY > dragging.h * 1.5) {
|
|
220
255
|
// down
|
|
221
|
-
|
|
256
|
+
const below = index.next ?
|
|
222
257
|
tree.getIndex(index.next) :
|
|
223
258
|
tree.getNodeByTop(index.top + index.height);
|
|
224
259
|
|
|
@@ -239,7 +274,7 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
239
274
|
this.setState({ dragging: dragging });
|
|
240
275
|
}
|
|
241
276
|
|
|
242
|
-
dragStart(id, dom, e) {
|
|
277
|
+
dragStart(id: TreeId, dom: HTMLDivElement, e: MouseEvent) {
|
|
243
278
|
// Only drag on left click
|
|
244
279
|
if (e.button !== 0) {
|
|
245
280
|
return;
|
|
@@ -261,9 +296,8 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
261
296
|
this._offsetY = e.clientY;
|
|
262
297
|
this._start = true;
|
|
263
298
|
|
|
264
|
-
|
|
265
|
-
this.
|
|
266
|
-
this._dragEndListener = () => self.dragEnd();
|
|
299
|
+
this._dragListener = (e: Event) => { this.drag(e); };
|
|
300
|
+
this._dragEndListener = () => this.dragEnd();
|
|
267
301
|
|
|
268
302
|
window.addEventListener("mousemove", this._dragListener);
|
|
269
303
|
window.addEventListener("mouseup", this._dragEndListener);
|
|
@@ -283,27 +317,15 @@ export default class PageTreeDraggable extends React.Component {
|
|
|
283
317
|
window.removeEventListener("mouseup", this._dragEndListener);
|
|
284
318
|
}
|
|
285
319
|
|
|
286
|
-
toggleCollapse(nodeId) {
|
|
320
|
+
toggleCollapse(nodeId: TreeId) {
|
|
287
321
|
this.props.toggleCollapsed(nodeId);
|
|
288
322
|
}
|
|
289
323
|
|
|
290
|
-
updatePage(index, attributes) {
|
|
324
|
+
updatePage(index: TreeIndex, attributes: Attributes) {
|
|
291
325
|
this.props.updatePage(index.id, attributes);
|
|
292
326
|
}
|
|
293
327
|
}
|
|
294
328
|
|
|
295
|
-
|
|
329
|
+
Draggable.defaultProps = {
|
|
296
330
|
paddingLeft: 15
|
|
297
331
|
};
|
|
298
|
-
|
|
299
|
-
PageTreeDraggable.propTypes = {
|
|
300
|
-
tree: PropTypes.object,
|
|
301
|
-
addChild: PropTypes.func,
|
|
302
|
-
movedPage: PropTypes.func,
|
|
303
|
-
toggleCollapsed: PropTypes.func,
|
|
304
|
-
paddingLeft: PropTypes.number,
|
|
305
|
-
updatePage: PropTypes.func,
|
|
306
|
-
updateTree: PropTypes.func,
|
|
307
|
-
locale: PropTypes.string,
|
|
308
|
-
dir: PropTypes.string
|
|
309
|
-
};
|