pages_core 3.15.3 → 3.15.5
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 +1 -1
- data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
- data/app/assets/builds/pages_core/admin.css +378 -253
- data/app/assets/builds/pages_core/mailer.css +41 -6
- data/app/assets/builds/pages_core_fonts/121b837e.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/216e5c23.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/3017b52f.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/489746b9.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/49775483.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/49c9e472.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/4a119645.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/5d56d7a8.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/61ea75a6.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/62cbb778.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/647d26c.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/67764053.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/6bb0fd00.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/6c0194a2.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/71423409.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/7584e61d.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/77bcfa1c.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/7aca0cc5.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/9a09533f.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/a51f5bc8.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/a80b2975.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/a891f617.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/ad6083f3.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/b29a61ff.woff2 +0 -0
- data/app/assets/builds/{fonts/6569749d.ttf → pages_core_fonts/b30b0656.ttf} +0 -0
- data/app/assets/builds/pages_core_fonts/b3a5f48c.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/bc73ee06.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/c38c6d45.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/c5ce0b1f.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/c8d53904.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/ce13c169.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/d43bd0d5.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/e1c7d368.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/e1e8175d.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/e318f796.woff2 +0 -0
- data/app/assets/builds/{fonts/ee32bc60.ttf → pages_core_fonts/e7acb7d9.ttf} +0 -0
- data/app/assets/builds/pages_core_fonts/ee5514c6.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/f4e495e2.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/f736ec65.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/f741c7ba.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/f7767345.woff2 +0 -0
- data/app/assets/builds/pages_core_fonts/fe9eb751.woff2 +0 -0
- data/app/assets/stylesheets/pages_core/admin/components/forms.css +2 -2
- data/app/assets/stylesheets/pages_core/admin/components/header.css +1 -1
- data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +1 -1
- data/app/assets/stylesheets/pages_core/admin/global/fonts.css +38 -38
- data/app/controllers/{pages_core → admin}/admin_controller.rb +1 -3
- data/app/controllers/attachments_controller.rb +40 -0
- data/app/controllers/concerns/pages_core/document_title_controller.rb +16 -0
- data/app/controllers/concerns/pages_core/page_parameters.rb +1 -1
- data/app/controllers/concerns/pages_core/pages/preview_controller.rb +49 -0
- data/app/controllers/concerns/pages_core/pages/rss_controller.rb +43 -0
- data/app/controllers/errors_controller.rb +2 -0
- data/app/controllers/images_controller.rb +13 -0
- data/app/controllers/pages_core/frontend/pages_controller.rb +3 -4
- data/app/controllers/pages_core/frontend_controller.rb +6 -1
- data/app/controllers/pages_core/sitemaps_controller.rb +21 -52
- data/app/helpers/pages_core/admin/image_uploads_helper.rb +1 -1
- data/app/helpers/pages_core/application_helper.rb +0 -3
- data/app/helpers/pages_core/attachments_helper.rb +0 -10
- data/app/helpers/pages_core/feed_tags_helper.rb +31 -0
- data/app/helpers/pages_core/frontend_helper.rb +3 -0
- data/app/helpers/pages_core/head_tags_helper.rb +80 -70
- data/app/helpers/pages_core/page_path_helper.rb +1 -12
- data/app/javascript/components/Attachments/Attachment.tsx +3 -3
- data/app/javascript/components/Attachments/AttachmentEditor.tsx +5 -5
- data/app/javascript/components/Attachments/Deleted.tsx +28 -0
- data/app/javascript/components/Attachments/List.tsx +11 -24
- data/app/javascript/components/Attachments/Placeholder.tsx +0 -2
- data/app/javascript/components/Attachments.tsx +2 -3
- data/app/javascript/components/DateRangeSelect.tsx +13 -10
- data/app/javascript/components/DateTimeSelect.tsx +11 -11
- data/app/javascript/components/EditableImage.tsx +3 -3
- data/app/javascript/components/FileUploadButton.tsx +3 -3
- data/app/javascript/components/ImageCropper/FocalPoint.tsx +10 -14
- data/app/javascript/components/ImageCropper/Image.tsx +19 -25
- data/app/javascript/components/ImageCropper/Toolbar.tsx +27 -26
- data/app/javascript/components/ImageCropper/useContainerSize.ts +25 -0
- data/app/javascript/components/ImageCropper/useCrop.ts +28 -13
- data/app/javascript/components/ImageCropper/useImageCropperContext.ts +13 -0
- data/app/javascript/components/ImageCropper.tsx +24 -83
- data/app/javascript/components/ImageEditor/Form.tsx +25 -28
- data/app/javascript/components/ImageEditor/useImageEditor.ts +63 -0
- data/app/javascript/components/ImageEditor/useImageEditorContext.ts +14 -0
- data/app/javascript/components/ImageEditor.tsx +28 -42
- data/app/javascript/components/ImageGrid/Deleted.tsx +28 -0
- data/app/javascript/components/ImageGrid/DragElement.tsx +5 -5
- data/app/javascript/components/ImageGrid/FilePlaceholder.tsx +0 -2
- data/app/javascript/components/ImageGrid/Grid.tsx +15 -24
- data/app/javascript/components/ImageGrid/GridImage.tsx +4 -4
- data/app/javascript/components/ImageGrid/Placeholder.tsx +2 -4
- data/app/javascript/components/ImageGrid.tsx +2 -4
- data/app/javascript/components/ImageUploader.tsx +5 -5
- data/app/javascript/components/LabelledField.tsx +6 -6
- data/app/javascript/components/Modal.tsx +16 -13
- data/app/javascript/components/PageForm/Block.tsx +3 -3
- data/app/javascript/components/PageForm/Content.tsx +11 -15
- data/app/javascript/components/PageForm/Dates.tsx +3 -11
- data/app/javascript/components/PageForm/Files.tsx +2 -4
- data/app/javascript/components/PageForm/Form.tsx +3 -9
- data/app/javascript/components/PageForm/Images.tsx +2 -4
- data/app/javascript/components/PageForm/LocaleLinks.tsx +4 -11
- data/app/javascript/components/PageForm/Metadata.tsx +8 -13
- data/app/javascript/components/PageForm/Options.tsx +28 -11
- data/app/javascript/components/PageForm/PageDescription.tsx +7 -14
- data/app/javascript/components/PageForm/PathSegment.tsx +5 -10
- data/app/javascript/components/PageForm/TabPanel.tsx +3 -6
- data/app/javascript/components/PageForm/Tabs.tsx +2 -4
- data/app/javascript/components/PageForm/UnconfiguredContent.tsx +7 -12
- data/app/javascript/components/PageForm/pageParams.ts +3 -2
- data/app/javascript/components/PageForm/usePage.ts +1 -46
- data/app/javascript/components/PageForm/usePageFormContext.ts +8 -0
- data/app/javascript/components/PageForm/useTabs.ts +1 -1
- data/app/javascript/components/PageForm/utils.ts +49 -0
- data/app/javascript/components/PageForm.tsx +52 -48
- data/app/javascript/components/PageImages.tsx +1 -3
- data/app/javascript/components/PageTree/Button.tsx +25 -0
- data/app/javascript/components/PageTree/CollapseArrow.tsx +34 -0
- data/app/javascript/components/PageTree/CollapsedLabel.tsx +21 -0
- data/app/javascript/components/PageTree/EditPageName.tsx +68 -0
- data/app/javascript/components/PageTree/Node.tsx +143 -413
- data/app/javascript/components/PageTree/PageName.tsx +6 -4
- data/app/javascript/components/PageTree/StatusLabel.tsx +10 -0
- data/app/javascript/components/PageTree/tree.ts +268 -0
- data/app/javascript/components/PageTree/usePageTree.ts +268 -0
- data/app/javascript/components/PageTree/usePageTreeContext.ts +13 -0
- data/app/javascript/components/PageTree.tsx +194 -214
- data/app/javascript/components/{RichTextToolbarButton.tsx → RichTextArea/ToolbarButton.tsx} +3 -5
- data/app/javascript/components/RichTextArea/actions.ts +106 -0
- data/app/javascript/components/RichTextArea/useMaybeControlledValue.ts +14 -0
- data/app/javascript/components/RichTextArea.tsx +91 -209
- data/app/javascript/components/TagEditor/AddTagForm.tsx +2 -2
- data/app/javascript/components/TagEditor/Editor.tsx +3 -5
- data/app/javascript/components/TagEditor/Tag.tsx +3 -5
- data/app/javascript/components/TagEditor/useTags.ts +7 -4
- data/app/javascript/components/TagEditor.tsx +2 -4
- data/app/javascript/components/Toast.tsx +5 -5
- data/app/javascript/components/drag/draggedOrder.ts +6 -6
- data/app/javascript/components/drag/useDragCollection.ts +21 -25
- data/app/javascript/components/drag/useDragUploader.ts +20 -18
- data/app/javascript/components/drag/useDraggable.ts +3 -3
- data/app/javascript/features/RichText.tsx +0 -1
- data/app/javascript/features/contentTabs.ts +2 -2
- data/app/javascript/stores/useModalStore.ts +1 -1
- data/app/javascript/stores/useToastStore.ts +2 -2
- data/app/javascript/types/Attachments.ts +11 -11
- data/app/javascript/types/Crop.ts +16 -12
- data/app/javascript/types/Drag.ts +21 -23
- data/app/javascript/types/Images.ts +8 -8
- data/app/javascript/types/PageEditor.ts +11 -4
- data/app/javascript/types/Pages.ts +22 -27
- data/app/javascript/types/Tags.ts +5 -6
- data/app/javascript/types/Template.ts +4 -4
- data/app/javascript/types.ts +2 -2
- data/app/models/attachment.rb +5 -9
- data/app/models/autopublisher.rb +1 -1
- data/app/models/concerns/pages_core/page_model/redirectable.rb +1 -2
- data/app/models/concerns/pages_core/page_model/searchable.rb +1 -1
- data/app/models/concerns/pages_core/page_model/status.rb +2 -4
- data/app/models/concerns/pages_core/searchable_document.rb +2 -4
- data/app/models/image.rb +0 -15
- data/app/models/page_builder.rb +4 -6
- data/app/resources/admin/page_resource.rb +2 -2
- data/app/resources/export/page_resource.rb +1 -1
- data/app/services/pages_core/invite_service.rb +1 -2
- data/app/views/layouts/admin.html.erb +1 -0
- data/app/views/pages_core/sitemaps/index.xml.builder +10 -0
- data/config/routes.rb +4 -3
- data/db/migrate/20240917142300_add_skip_index_to_pages.rb +7 -0
- data/lib/pages_core/engine.rb +15 -17
- data/lib/pages_core/sitemap.rb +58 -0
- data/lib/pages_core/templates/configuration_proxy.rb +3 -3
- data/lib/pages_core.rb +7 -4
- data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +2 -2
- data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +13 -5
- data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +2 -6
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +3 -1
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +2 -3
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +1 -1
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +6 -5
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +1 -1
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +9 -6
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +42 -26
- data/lib/rails/generators/pages_core/install/templates/application_controller.rb +0 -6
- data/lib/rails/generators/pages_core/rspec/templates/rails_helper.rb +1 -1
- metadata +81 -49
- data/app/assets/builds/fonts/7b7db107.woff2 +0 -0
- data/app/assets/builds/fonts/921961e9.woff2 +0 -0
- data/app/controller_dummies/admin/admin_controller.rb +0 -6
- data/app/controller_dummies/application_controller.rb +0 -6
- data/app/controller_dummies/attachments_controller.rb +0 -4
- data/app/controller_dummies/frontend_controller.rb +0 -4
- data/app/controller_dummies/images_controller.rb +0 -4
- data/app/controller_dummies/page_files_controller.rb +0 -4
- data/app/controller_dummies/pages_controller.rb +0 -4
- data/app/controller_dummies/sitemaps_controller.rb +0 -4
- data/app/controllers/concerns/pages_core/preview_pages_controller.rb +0 -47
- data/app/controllers/concerns/pages_core/rss_controller.rb +0 -41
- data/app/controllers/pages_core/attachments_controller.rb +0 -42
- data/app/controllers/pages_core/frontend/page_files_controller.rb +0 -25
- data/app/controllers/pages_core/images_controller.rb +0 -15
- data/app/helpers/pages_core/meta_tags_helper.rb +0 -96
- data/app/helpers/pages_core/open_graph_tags_helper.rb +0 -49
- data/app/javascript/components/PageTree/Draggable.tsx +0 -338
- data/app/javascript/lib/Tree.ts +0 -305
- data/app/javascript/types/Trees.ts +0 -19
- data/app/views/sitemaps/show.xml.builder +0 -11
@@ -1,233 +1,213 @@
|
|
1
|
-
import
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
2
2
|
|
3
|
-
import Tree from "../lib/Tree";
|
4
|
-
import { postJson, putJson } from "../lib/request";
|
5
|
-
import * as Trees from "../types/Trees";
|
6
3
|
import * as Pages from "../types/Pages";
|
7
|
-
|
8
|
-
import
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
}
|
15
|
-
|
16
|
-
|
4
|
+
import * as Tree from "./PageTree/tree";
|
5
|
+
import usePageTree, {
|
6
|
+
State,
|
7
|
+
movePage,
|
8
|
+
visibleChildNodes
|
9
|
+
} from "./PageTree/usePageTree";
|
10
|
+
import { PageTreeContext } from "./PageTree/usePageTreeContext";
|
11
|
+
import Node, { paddingLeft } from "./PageTree/Node";
|
12
|
+
|
13
|
+
type DragState = {
|
14
|
+
id: Tree.Id;
|
15
|
+
x: number;
|
16
|
+
y: number;
|
17
|
+
w: number;
|
18
|
+
h: number;
|
19
|
+
scrollTop: number;
|
20
|
+
scrollLeft: number;
|
21
|
+
startX: number;
|
22
|
+
startY: number;
|
23
|
+
offsetX: number;
|
24
|
+
offsetY: number;
|
25
|
+
tree: State;
|
26
|
+
};
|
27
|
+
|
28
|
+
type Props = {
|
17
29
|
dir: string;
|
18
30
|
locale: string;
|
19
|
-
pages: Pages.
|
31
|
+
pages: Pages.TreeResource[];
|
20
32
|
permissions: string[];
|
21
|
-
}
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
};
|
34
|
+
|
35
|
+
function prevAddButtonCount(state: State, id: Tree.Id) {
|
36
|
+
let count = 0;
|
37
|
+
const parentNodes = Tree.parents(state, id);
|
38
|
+
|
39
|
+
let pointer = Tree.getNodeByTop(state, state.nodes[id].top - 1);
|
40
|
+
while (pointer) {
|
41
|
+
if (
|
42
|
+
parentNodes.indexOf(pointer.id) == -1 &&
|
43
|
+
!pointer.collapsed &&
|
44
|
+
visibleChildNodes(state, pointer.id).length > 0
|
45
|
+
) {
|
46
|
+
count += 1;
|
47
|
+
}
|
48
|
+
pointer = Tree.getNodeByTop(state, pointer.top - 1);
|
36
49
|
}
|
37
|
-
return
|
50
|
+
return count;
|
38
51
|
}
|
39
52
|
|
40
|
-
export default
|
41
|
-
|
42
|
-
|
53
|
+
export default function PageTree({ dir, locale, pages, permissions }: Props) {
|
54
|
+
const [state, dispatch] = usePageTree(pages, locale, dir, permissions);
|
55
|
+
|
56
|
+
const [dragging, setDragging] = useState(false);
|
57
|
+
const [dragState, setDragState] = useState<DragState | null>(null);
|
58
|
+
|
59
|
+
const getDraggingDom = () => {
|
60
|
+
if (dragging) {
|
61
|
+
const dragStateStyles = {
|
62
|
+
top: dragState.y,
|
63
|
+
left: dragState.x,
|
64
|
+
width: dragState.w
|
65
|
+
};
|
66
|
+
|
67
|
+
return (
|
68
|
+
<div className="draggable" style={dragStateStyles}>
|
69
|
+
<Node id={dragState.id} />
|
70
|
+
</div>
|
71
|
+
);
|
72
|
+
}
|
73
|
+
};
|
43
74
|
|
44
|
-
|
45
|
-
|
75
|
+
const dragStart = (id: Tree.Id, dom: HTMLDivElement, e: React.MouseEvent) => {
|
76
|
+
// Only drag on left click
|
77
|
+
if (e.button !== 0) {
|
78
|
+
return;
|
79
|
+
}
|
46
80
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
81
|
+
setDragState({
|
82
|
+
id: id,
|
83
|
+
w: dom.offsetWidth,
|
84
|
+
h: dom.offsetHeight,
|
85
|
+
x: dom.offsetLeft,
|
86
|
+
y: dom.offsetTop,
|
87
|
+
scrollTop: document.body.scrollTop,
|
88
|
+
scrollLeft: document.body.scrollLeft,
|
89
|
+
startX: dom.offsetLeft,
|
90
|
+
startY: dom.offsetTop,
|
91
|
+
offsetX: e.clientX,
|
92
|
+
offsetY: e.clientY,
|
93
|
+
tree: { ...state }
|
94
|
+
});
|
95
|
+
};
|
96
|
+
|
97
|
+
const drag = useCallback(
|
98
|
+
(e: MouseEvent) => {
|
99
|
+
if (!dragState) {
|
100
|
+
return;
|
101
|
+
} else if (!dragging) {
|
102
|
+
const distance =
|
103
|
+
Math.abs(e.clientX - dragState.offsetX) +
|
104
|
+
Math.abs(e.clientY - dragState.offsetY);
|
105
|
+
if (distance >= 15) {
|
106
|
+
setDragging(true);
|
107
|
+
} else {
|
108
|
+
return null;
|
109
|
+
}
|
54
110
|
}
|
55
|
-
return depth;
|
56
|
-
};
|
57
111
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
112
|
+
let tree = dragState.tree;
|
113
|
+
let node = tree.nodes[dragState.id];
|
114
|
+
|
115
|
+
const pos = {
|
116
|
+
x:
|
117
|
+
dragState.startX +
|
118
|
+
e.clientX -
|
119
|
+
dragState.offsetX +
|
120
|
+
(document.body.scrollLeft - dragState.scrollLeft),
|
121
|
+
y:
|
122
|
+
dragState.startY +
|
123
|
+
e.clientY -
|
124
|
+
dragState.offsetY +
|
125
|
+
(document.body.scrollTop - dragState.scrollTop)
|
126
|
+
};
|
127
|
+
|
128
|
+
const move = (target: Tree.Id, placement: Tree.MovePlacement) => {
|
129
|
+
tree = {
|
130
|
+
...tree,
|
131
|
+
...Tree.indexPositions(
|
132
|
+
Tree.moveRelative(tree, node.id, target, placement)
|
133
|
+
)
|
134
|
+
};
|
135
|
+
node = tree.nodes[dragState.id];
|
136
|
+
};
|
137
|
+
|
138
|
+
const diffX = pos.x - paddingLeft / 2 - (node.left - 2) * paddingLeft;
|
139
|
+
const diffY =
|
140
|
+
pos.y -
|
141
|
+
dragState.h / 2 -
|
142
|
+
(node.top - 2 + prevAddButtonCount(tree, dragState.id)) * dragState.h;
|
143
|
+
|
144
|
+
if (diffX < 0) {
|
145
|
+
// left
|
146
|
+
if (node.parent && !Tree.nextSibling(tree, node.id)) {
|
147
|
+
move(node.parent, "after");
|
148
|
+
}
|
149
|
+
} else if (diffX > paddingLeft) {
|
150
|
+
// right
|
151
|
+
const prev = Tree.prevSibling(tree, node.id);
|
152
|
+
if (prev && !prev.collapsed) {
|
153
|
+
move(prev.id, "append");
|
154
|
+
}
|
67
155
|
}
|
68
|
-
if (index.children && index.children.length) {
|
69
|
-
index.children.forEach((c) => walk(c));
|
70
|
-
}
|
71
|
-
};
|
72
|
-
walk(1);
|
73
|
-
}
|
74
|
-
|
75
|
-
createPage(index: Trees.Index<Pages.TreeNode>, attributes: Pages.TreeItem) {
|
76
|
-
void postJson(`/admin/${this.props.locale}/pages.json`, {
|
77
|
-
page: attributes
|
78
|
-
}).then((response: Pages.TreeItem) => this.updateNode(index, response));
|
79
|
-
}
|
80
156
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
(
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
blocks: {
|
98
|
-
name: { [locale]: "All Pages" }
|
99
|
-
},
|
100
|
-
permissions: this.props.permissions,
|
101
|
-
root: true,
|
102
|
-
children: parentMap[0],
|
103
|
-
collapsed: false
|
104
|
-
});
|
105
|
-
this.applyCollapsed(tree);
|
106
|
-
tree.updateNodesPosition();
|
107
|
-
return tree;
|
108
|
-
}
|
109
|
-
|
110
|
-
movePage(
|
111
|
-
index: Trees.Index<Pages.TreeNode>,
|
112
|
-
parent: Trees.Index<Pages.TreeNode>,
|
113
|
-
position: number
|
114
|
-
) {
|
115
|
-
const data = {
|
116
|
-
parent_id: parent.node.id,
|
117
|
-
position: position
|
118
|
-
};
|
119
|
-
const url = `/admin/${this.props.locale}/pages/${index.node.id}/move.json`;
|
120
|
-
this.performUpdate(index, url, data);
|
121
|
-
}
|
122
|
-
|
123
|
-
performUpdate(
|
124
|
-
index: Trees.Index<Pages.TreeNode>,
|
125
|
-
url: string,
|
126
|
-
data: Record<string, unknown>
|
127
|
-
) {
|
128
|
-
void putJson(url, data).then((response: Pages.TreeItem) =>
|
129
|
-
this.updateNode(index, response)
|
130
|
-
);
|
131
|
-
}
|
132
|
-
|
133
|
-
render() {
|
134
|
-
const addChild = (id: Trees.Id, attributes: Pages.TreeNode) => {
|
135
|
-
const tree = this.state.tree;
|
136
|
-
const index = tree.append(attributes, id);
|
137
|
-
this.reorderChildren(id);
|
138
|
-
this.setCollapsed(id, false);
|
139
|
-
this.createPage(index, attributes);
|
140
|
-
this.setState({ tree: tree });
|
141
|
-
};
|
142
|
-
|
143
|
-
const movedPage = (id: Trees.Id) => {
|
144
|
-
const tree = this.state.tree;
|
145
|
-
const index = tree.getIndex(id);
|
146
|
-
this.reorderChildren(index.parent);
|
147
|
-
|
148
|
-
const parent = tree.getIndex(index.parent);
|
149
|
-
const position = parent.children.indexOf(id) + 1;
|
150
|
-
|
151
|
-
this.movePage(index, parent, position);
|
152
|
-
this.setState({ tree: tree });
|
153
|
-
};
|
154
|
-
|
155
|
-
const toggleCollapsed = (id: Trees.Id) => {
|
156
|
-
const tree = this.state.tree;
|
157
|
-
const node = tree.getIndex(id).node;
|
158
|
-
this.setCollapsed(id, !node.collapsed);
|
159
|
-
this.setState({ tree: tree });
|
160
|
-
};
|
161
|
-
|
162
|
-
const updatePage = (id: Trees.Id, attributes: Pages.TreeItem) => {
|
163
|
-
const tree = this.state.tree;
|
164
|
-
const index = tree.getIndex(id);
|
165
|
-
const url = `/admin/${this.props.locale}/pages/${index.node.id}.json`;
|
166
|
-
this.updateNode(index, attributes);
|
167
|
-
|
168
|
-
const data: Record<string, unknown> = { ...attributes };
|
169
|
-
if ("blocks" in attributes && "name" in attributes.blocks) {
|
170
|
-
data.name = attributes.blocks.name[this.props.locale];
|
157
|
+
if (diffY < 0 - dragState.h * 0.5) {
|
158
|
+
// up
|
159
|
+
move(Tree.getNodeByTop(tree, node.top - 1).id, "before");
|
160
|
+
} else if (diffY > dragState.h * 1.5) {
|
161
|
+
// down
|
162
|
+
const below =
|
163
|
+
Tree.nextSibling(tree, node.id) ||
|
164
|
+
Tree.getNodeByTop(tree, node.id + node.height);
|
165
|
+
|
166
|
+
if (below && below.parent !== node.id) {
|
167
|
+
if (below.childNodes.length > 0 && !below.collapsed) {
|
168
|
+
move(below.id, "prepend");
|
169
|
+
} else {
|
170
|
+
move(below.id, "after");
|
171
|
+
}
|
172
|
+
}
|
171
173
|
}
|
172
|
-
this.performUpdate(index, url, { page: data });
|
173
|
-
};
|
174
174
|
|
175
|
-
|
176
|
-
|
177
|
-
|
175
|
+
setDragState({ ...dragState, ...pos, tree: tree });
|
176
|
+
},
|
177
|
+
[dragging, dragState]
|
178
|
+
);
|
178
179
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
movedPage={movedPage}
|
184
|
-
toggleCollapsed={toggleCollapsed}
|
185
|
-
updatePage={updatePage}
|
186
|
-
updateTree={updateTree}
|
187
|
-
locale={this.props.locale}
|
188
|
-
dir={this.props.dir}
|
189
|
-
/>
|
190
|
-
);
|
191
|
-
}
|
192
|
-
|
193
|
-
reorderChildren(id: Trees.Id) {
|
194
|
-
const tree = this.state.tree;
|
195
|
-
const index = this.state.tree.getIndex(id);
|
196
|
-
const node = index.node;
|
197
|
-
if (!node.news_page) {
|
198
|
-
return;
|
180
|
+
const dragEnd = useCallback(() => {
|
181
|
+
if (dragging) {
|
182
|
+
movePage(dragState.tree, dragState.id, dispatch);
|
183
|
+
setDragging(false);
|
199
184
|
}
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
updateNode(index: Trees.Index<Pages.TreeNode>, attributes: Pages.TreeItem) {
|
230
|
-
index.node = { ...index.node, ...attributes };
|
231
|
-
this.setState({ tree: this.state.tree });
|
232
|
-
}
|
185
|
+
setDragState(null);
|
186
|
+
}, [dragging, dragState, dispatch]);
|
187
|
+
|
188
|
+
useEffect(() => {
|
189
|
+
window.addEventListener("mousemove", drag);
|
190
|
+
window.addEventListener("mouseup", dragEnd);
|
191
|
+
return () => {
|
192
|
+
window.removeEventListener("mousemove", drag);
|
193
|
+
window.removeEventListener("mouseup", dragEnd);
|
194
|
+
};
|
195
|
+
}, [drag, dragEnd]);
|
196
|
+
|
197
|
+
return (
|
198
|
+
<PageTreeContext.Provider
|
199
|
+
value={{
|
200
|
+
state: (dragging && dragState.tree) || state,
|
201
|
+
dispatch: dispatch
|
202
|
+
}}>
|
203
|
+
<div className="page-tree">
|
204
|
+
{getDraggingDom()}
|
205
|
+
<Node
|
206
|
+
id={state.rootId}
|
207
|
+
onDragStart={dragStart}
|
208
|
+
dragging={dragging && dragState.id}
|
209
|
+
/>
|
210
|
+
</div>
|
211
|
+
</PageTreeContext.Provider>
|
212
|
+
);
|
233
213
|
}
|
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
interface Props {
|
1
|
+
type Props = {
|
4
2
|
className: string;
|
5
3
|
name: string;
|
6
4
|
onClick: (evt: React.MouseEvent) => void;
|
7
|
-
}
|
5
|
+
};
|
8
6
|
|
9
|
-
export default function
|
7
|
+
export default function ToolbarButton(props: Props) {
|
10
8
|
return (
|
11
9
|
<a
|
12
10
|
title={props.name}
|
@@ -0,0 +1,106 @@
|
|
1
|
+
type Replacement = [string, string, string];
|
2
|
+
|
3
|
+
export type ActionFn = (str: string) => Replacement;
|
4
|
+
|
5
|
+
type Action = {
|
6
|
+
name: string;
|
7
|
+
className: string;
|
8
|
+
fn: ActionFn;
|
9
|
+
hotkey?: string;
|
10
|
+
};
|
11
|
+
|
12
|
+
function strToList(str: string, prefix: string): string {
|
13
|
+
return str
|
14
|
+
.split("\n")
|
15
|
+
.map((line) => prefix + " " + line)
|
16
|
+
.join("\n");
|
17
|
+
}
|
18
|
+
|
19
|
+
function relativeUrl(str: string): string {
|
20
|
+
let url: URL = null;
|
21
|
+
|
22
|
+
if (!str.match(/^https:\/\//) || !document || !document.location) {
|
23
|
+
return str;
|
24
|
+
}
|
25
|
+
|
26
|
+
try {
|
27
|
+
url = new URL(str);
|
28
|
+
} catch (error) {
|
29
|
+
console.log("Error parsing URL: ", error);
|
30
|
+
}
|
31
|
+
|
32
|
+
if (
|
33
|
+
url &&
|
34
|
+
url.hostname === document.location.hostname &&
|
35
|
+
(document.location.port || "80") === (url.port || "80")
|
36
|
+
) {
|
37
|
+
return url.pathname;
|
38
|
+
}
|
39
|
+
return str;
|
40
|
+
}
|
41
|
+
|
42
|
+
function emailLink(selection: string): Replacement {
|
43
|
+
const address = prompt("Enter email address", "");
|
44
|
+
const name = selection.length > 0 ? selection : address;
|
45
|
+
return ['"', name, `":mailto:${address}`];
|
46
|
+
}
|
47
|
+
|
48
|
+
function link(selection: string): Replacement {
|
49
|
+
const name = selection.length > 0 ? selection : "Link text";
|
50
|
+
const url = prompt("Enter link URL", "");
|
51
|
+
if (url) {
|
52
|
+
return ['"', name, `":${relativeUrl(url)}`];
|
53
|
+
} else {
|
54
|
+
return ["", name, ""];
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
export const simpleActions: Action[] = [
|
59
|
+
{
|
60
|
+
name: "bold",
|
61
|
+
className: "bold",
|
62
|
+
hotkey: "b",
|
63
|
+
fn: (str: string) => ["<b>", str, "</b>"]
|
64
|
+
},
|
65
|
+
{
|
66
|
+
name: "italic",
|
67
|
+
className: "italic",
|
68
|
+
hotkey: "i",
|
69
|
+
fn: (str: string) => ["<i>", str, "</i>"]
|
70
|
+
}
|
71
|
+
];
|
72
|
+
|
73
|
+
export const advancedActions: Action[] = [
|
74
|
+
{
|
75
|
+
name: "Heading 2",
|
76
|
+
className: "header h2",
|
77
|
+
fn: (str: string) => ["h2. ", str, ""]
|
78
|
+
},
|
79
|
+
{
|
80
|
+
name: "Heading 3",
|
81
|
+
className: "header h3",
|
82
|
+
fn: (str: string) => ["h3. ", str, ""]
|
83
|
+
},
|
84
|
+
{
|
85
|
+
name: "Heading 4",
|
86
|
+
className: "header h4",
|
87
|
+
fn: (str: string) => ["h4. ", str, ""]
|
88
|
+
},
|
89
|
+
{
|
90
|
+
name: "Blockquote",
|
91
|
+
className: "quote-left",
|
92
|
+
fn: (str: string) => ["bq. ", str, ""]
|
93
|
+
},
|
94
|
+
{
|
95
|
+
name: "List",
|
96
|
+
className: "list-ul",
|
97
|
+
fn: (str: string) => ["", strToList(str, "*"), ""]
|
98
|
+
},
|
99
|
+
{
|
100
|
+
name: "Ordered list",
|
101
|
+
className: "list-ol",
|
102
|
+
fn: (str: string) => ["", strToList(str, "#"), ""]
|
103
|
+
},
|
104
|
+
{ name: "Link", className: "link", fn: link },
|
105
|
+
{ name: "Email link", className: "envelope", fn: emailLink }
|
106
|
+
];
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { useState } from "react";
|
2
|
+
|
3
|
+
export default function useMaybeControlledValue<T>(
|
4
|
+
initialValue: T,
|
5
|
+
onChange?: (nextValue: T) => void
|
6
|
+
): [T, (nextValue: T) => void] {
|
7
|
+
const [value, setValue] = useState(initialValue);
|
8
|
+
|
9
|
+
if (onChange) {
|
10
|
+
return [initialValue, onChange];
|
11
|
+
} else {
|
12
|
+
return [value, setValue];
|
13
|
+
}
|
14
|
+
}
|