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,129 +1,69 @@
|
|
1
|
-
import
|
2
|
-
import
|
1
|
+
import { useRef, ChangeEvent } from "react";
|
2
|
+
import ToolbarButton from "./RichTextArea/ToolbarButton";
|
3
|
+
import useMaybeControlledValue from "./RichTextArea/useMaybeControlledValue";
|
3
4
|
|
4
|
-
|
5
|
+
import {
|
6
|
+
ActionFn,
|
7
|
+
simpleActions,
|
8
|
+
advancedActions
|
9
|
+
} from "./RichTextArea/actions";
|
10
|
+
|
11
|
+
type Props = {
|
5
12
|
id: string;
|
6
13
|
name: string;
|
7
14
|
value: string;
|
8
|
-
rows
|
15
|
+
rows?: number;
|
9
16
|
className?: string;
|
10
17
|
simple?: boolean;
|
11
18
|
lang?: string;
|
12
19
|
dir?: string;
|
13
20
|
onChange?: (str: string) => void;
|
14
|
-
}
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
rows: props.rows || 5
|
39
|
-
};
|
40
|
-
this.inputRef = createRef<HTMLTextAreaElement>();
|
41
|
-
}
|
42
|
-
|
43
|
-
actions = () => {
|
44
|
-
const simple: Action[] = [
|
45
|
-
{
|
46
|
-
name: "bold",
|
47
|
-
className: "bold",
|
48
|
-
hotkey: "b",
|
49
|
-
fn: (str: string) => ["<b>", str, "</b>"]
|
50
|
-
},
|
51
|
-
{
|
52
|
-
name: "italic",
|
53
|
-
className: "italic",
|
54
|
-
hotkey: "i",
|
55
|
-
fn: (str: string) => ["<i>", str, "</i>"]
|
56
|
-
}
|
57
|
-
];
|
58
|
-
|
59
|
-
const advanced: Action[] = [
|
60
|
-
{
|
61
|
-
name: "Heading 2",
|
62
|
-
className: "header h2",
|
63
|
-
fn: (str: string) => ["h2. ", str, ""]
|
64
|
-
},
|
65
|
-
{
|
66
|
-
name: "Heading 3",
|
67
|
-
className: "header h3",
|
68
|
-
fn: (str: string) => ["h3. ", str, ""]
|
69
|
-
},
|
70
|
-
{
|
71
|
-
name: "Heading 4",
|
72
|
-
className: "header h4",
|
73
|
-
fn: (str: string) => ["h4. ", str, ""]
|
74
|
-
},
|
75
|
-
{
|
76
|
-
name: "Blockquote",
|
77
|
-
className: "quote-left",
|
78
|
-
fn: (str: string) => ["bq. ", str, ""]
|
79
|
-
},
|
80
|
-
{
|
81
|
-
name: "List",
|
82
|
-
className: "list-ul",
|
83
|
-
fn: (str: string) => ["", this.strToList(str, "*"), ""]
|
84
|
-
},
|
85
|
-
{
|
86
|
-
name: "Ordered list",
|
87
|
-
className: "list-ol",
|
88
|
-
fn: (str: string) => ["", this.strToList(str, "#"), ""]
|
89
|
-
},
|
90
|
-
{ name: "Link", className: "link", fn: this.link },
|
91
|
-
{ name: "Email link", className: "envelope", fn: this.emailLink }
|
92
|
-
];
|
93
|
-
|
94
|
-
return this.props.simple ? simple : [...simple, ...advanced];
|
95
|
-
};
|
96
|
-
|
97
|
-
applyAction(fn: ActionFn) {
|
98
|
-
const [prefix, replacement, postfix] = fn(this.getSelection());
|
99
|
-
this.replaceSelection(prefix, replacement, postfix);
|
100
|
-
}
|
101
|
-
|
102
|
-
emailLink = (selection: string): Replacement => {
|
103
|
-
const address = prompt("Enter email address", "");
|
104
|
-
const name = selection.length > 0 ? selection : address;
|
105
|
-
return ['"', name, `":mailto:${address}`];
|
21
|
+
};
|
22
|
+
|
23
|
+
export default function RichTextArea({
|
24
|
+
id,
|
25
|
+
name,
|
26
|
+
value: initialValue,
|
27
|
+
rows,
|
28
|
+
className,
|
29
|
+
simple,
|
30
|
+
lang,
|
31
|
+
dir,
|
32
|
+
onChange
|
33
|
+
}: Props) {
|
34
|
+
const [value, setValue] = useMaybeControlledValue(initialValue, onChange);
|
35
|
+
|
36
|
+
const inputRef = useRef<HTMLTextAreaElement>(null);
|
37
|
+
|
38
|
+
const actions = simple
|
39
|
+
? simpleActions
|
40
|
+
: [...simpleActions, ...advancedActions];
|
41
|
+
|
42
|
+
const applyAction = (fn: ActionFn) => {
|
43
|
+
const [prefix, replacement, postfix] = fn(getSelection());
|
44
|
+
replaceSelection(prefix, replacement, postfix);
|
106
45
|
};
|
107
46
|
|
108
|
-
getSelection = (): string => {
|
109
|
-
const
|
110
|
-
|
47
|
+
const getSelection = (): string => {
|
48
|
+
const textarea = inputRef.current;
|
49
|
+
const { selectionStart, selectionEnd, value } = textarea;
|
50
|
+
return value.substring(selectionStart, selectionEnd);
|
111
51
|
};
|
112
52
|
|
113
|
-
handleChange = (evt: ChangeEvent<HTMLTextAreaElement>) => {
|
114
|
-
|
53
|
+
const handleChange = (evt: ChangeEvent<HTMLTextAreaElement>) => {
|
54
|
+
setValue(evt.target.value);
|
115
55
|
};
|
116
56
|
|
117
|
-
handleKeyPress = (evt: React.KeyboardEvent) => {
|
57
|
+
const handleKeyPress = (evt: React.KeyboardEvent) => {
|
118
58
|
let key: string;
|
119
|
-
if (evt.
|
120
|
-
key =
|
121
|
-
} else if (evt.
|
59
|
+
if (evt.key >= "A" && evt.key <= "Z") {
|
60
|
+
key = evt.key.toLowerCase();
|
61
|
+
} else if (evt.key === "Enter") {
|
122
62
|
key = "enter";
|
123
63
|
}
|
124
64
|
|
125
65
|
const hotkeys: Record<string, ActionFn> = {};
|
126
|
-
|
66
|
+
actions.forEach((a) => {
|
127
67
|
if (a.hotkey) {
|
128
68
|
hotkeys[a.hotkey] = a.fn;
|
129
69
|
}
|
@@ -131,133 +71,75 @@ export default class RichTextArea extends Component<Props, State> {
|
|
131
71
|
|
132
72
|
if ((evt.metaKey || evt.ctrlKey) && key in hotkeys) {
|
133
73
|
evt.preventDefault();
|
134
|
-
|
74
|
+
applyAction(hotkeys[key]);
|
135
75
|
}
|
136
76
|
};
|
137
77
|
|
138
|
-
|
139
|
-
const name = selection.length > 0 ? selection : "Link text";
|
140
|
-
const url = prompt("Enter link URL", "");
|
141
|
-
if (url) {
|
142
|
-
return ['"', name, `":${this.relativeUrl(url)}`];
|
143
|
-
} else {
|
144
|
-
return ["", name, ""];
|
145
|
-
}
|
146
|
-
};
|
147
|
-
|
148
|
-
localeOptions() {
|
78
|
+
const localeOptions = () => {
|
149
79
|
const opts: React.HTMLProps<HTMLTextAreaElement> = {};
|
150
80
|
|
151
|
-
if (
|
152
|
-
opts.lang =
|
81
|
+
if (lang) {
|
82
|
+
opts.lang = lang;
|
153
83
|
}
|
154
84
|
|
155
|
-
if (
|
156
|
-
opts.dir =
|
85
|
+
if (dir) {
|
86
|
+
opts.dir = dir;
|
157
87
|
}
|
158
88
|
|
159
89
|
return opts;
|
160
|
-
}
|
161
|
-
|
162
|
-
relativeUrl(str: string): string {
|
163
|
-
let url: URL = null;
|
164
|
-
|
165
|
-
if (!str.match(/^https:\/\//) || !document || !document.location) {
|
166
|
-
return str;
|
167
|
-
}
|
168
|
-
|
169
|
-
try {
|
170
|
-
url = new URL(str);
|
171
|
-
} catch (error) {
|
172
|
-
console.log("Error parsing URL: ", error);
|
173
|
-
}
|
174
|
-
|
175
|
-
if (
|
176
|
-
url &&
|
177
|
-
url.hostname == document.location.hostname &&
|
178
|
-
(document.location.port || "80") == (url.port || "80")
|
179
|
-
) {
|
180
|
-
return url.pathname;
|
181
|
-
}
|
182
|
-
return str;
|
183
|
-
}
|
184
|
-
|
185
|
-
render() {
|
186
|
-
const { rows } = this.state;
|
187
|
-
const { id, name } = this.props;
|
188
|
-
const value = this.getValue();
|
189
|
-
|
190
|
-
const clickHandler = (fn: ActionFn) => (evt: React.MouseEvent) => {
|
191
|
-
evt.preventDefault();
|
192
|
-
this.applyAction(fn);
|
193
|
-
};
|
194
|
-
|
195
|
-
return (
|
196
|
-
<div className="rich-text-area">
|
197
|
-
<div className="rich-text toolbar">
|
198
|
-
{this.actions().map((a) => (
|
199
|
-
<RichTextToolbarButton
|
200
|
-
key={a.name}
|
201
|
-
name={a.name}
|
202
|
-
className={a.className}
|
203
|
-
onClick={clickHandler(a.fn)}
|
204
|
-
/>
|
205
|
-
))}
|
206
|
-
</div>
|
207
|
-
<textarea
|
208
|
-
className={this.props.className || "rich"}
|
209
|
-
ref={this.inputRef}
|
210
|
-
id={id}
|
211
|
-
name={name}
|
212
|
-
value={value}
|
213
|
-
rows={rows}
|
214
|
-
onChange={this.handleChange}
|
215
|
-
onKeyDown={this.handleKeyPress}
|
216
|
-
{...this.localeOptions()}
|
217
|
-
/>
|
218
|
-
</div>
|
219
|
-
);
|
220
|
-
}
|
90
|
+
};
|
221
91
|
|
222
|
-
replaceSelection = (
|
223
|
-
|
92
|
+
const replaceSelection = (
|
93
|
+
prefix: string,
|
94
|
+
replacement: string,
|
95
|
+
postfix: string
|
96
|
+
) => {
|
97
|
+
const textarea = inputRef.current;
|
224
98
|
const { selectionStart, selectionEnd, value } = textarea;
|
225
99
|
|
226
100
|
textarea.value =
|
227
|
-
value.
|
101
|
+
value.substring(0, selectionStart) +
|
228
102
|
prefix +
|
229
103
|
replacement +
|
230
104
|
postfix +
|
231
|
-
value.
|
105
|
+
value.substring(selectionEnd);
|
232
106
|
|
233
107
|
textarea.focus({ preventScroll: true });
|
234
108
|
textarea.setSelectionRange(
|
235
109
|
selectionStart + prefix.length,
|
236
110
|
selectionStart + prefix.length + replacement.length
|
237
111
|
);
|
238
|
-
|
112
|
+
setValue(textarea.value);
|
239
113
|
};
|
240
114
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
.join("\n");
|
246
|
-
}
|
247
|
-
|
248
|
-
getValue() {
|
249
|
-
if (this.props.onChange) {
|
250
|
-
return this.props.value;
|
251
|
-
} else {
|
252
|
-
return this.state.value;
|
253
|
-
}
|
254
|
-
}
|
115
|
+
const clickHandler = (fn: ActionFn) => (evt: React.MouseEvent) => {
|
116
|
+
evt.preventDefault();
|
117
|
+
applyAction(fn);
|
118
|
+
};
|
255
119
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
120
|
+
return (
|
121
|
+
<div className="rich-text-area">
|
122
|
+
<div className="rich-text toolbar">
|
123
|
+
{actions.map((action) => (
|
124
|
+
<ToolbarButton
|
125
|
+
key={action.name}
|
126
|
+
name={action.name}
|
127
|
+
className={action.className}
|
128
|
+
onClick={clickHandler(action.fn)}
|
129
|
+
/>
|
130
|
+
))}
|
131
|
+
</div>
|
132
|
+
<textarea
|
133
|
+
className={className || "rich"}
|
134
|
+
ref={inputRef}
|
135
|
+
id={id}
|
136
|
+
name={name}
|
137
|
+
value={value || ""}
|
138
|
+
rows={rows || 5}
|
139
|
+
onChange={handleChange}
|
140
|
+
onKeyDown={handleKeyPress}
|
141
|
+
{...localeOptions()}
|
142
|
+
/>
|
143
|
+
</div>
|
144
|
+
);
|
263
145
|
}
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import
|
1
|
+
import { ChangeEvent, MouseEvent, KeyboardEvent, useState } from "react";
|
2
2
|
|
3
3
|
import * as Tags from "../../types/Tags";
|
4
4
|
|
5
|
-
|
5
|
+
type Props = {
|
6
6
|
dispatch: (action: Tags.Action) => void;
|
7
7
|
}
|
8
8
|
|
@@ -1,16 +1,14 @@
|
|
1
|
-
import React from "react";
|
2
|
-
|
3
1
|
import { allTags, isEnabled } from "./useTags";
|
4
2
|
|
5
3
|
import AddTagForm from "./AddTagForm";
|
6
4
|
import Tag from "./Tag";
|
7
5
|
import * as Tags from "../../types/Tags";
|
8
6
|
|
9
|
-
|
7
|
+
type Props = {
|
10
8
|
name: string;
|
11
9
|
state: Tags.State;
|
12
|
-
dispatch:
|
13
|
-
}
|
10
|
+
dispatch: React.Dispatch<Tags.Action>;
|
11
|
+
};
|
14
12
|
|
15
13
|
export default function Editor(props: Props) {
|
16
14
|
const { name, state, dispatch } = props;
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import React from "react";
|
2
|
-
|
3
1
|
import * as Tags from "../../types/Tags";
|
4
2
|
|
5
|
-
|
3
|
+
type Props = {
|
6
4
|
enabled: boolean;
|
7
5
|
tag: string;
|
8
|
-
dispatch:
|
9
|
-
}
|
6
|
+
dispatch: React.Dispatch<Tags.Action>;
|
7
|
+
};
|
10
8
|
|
11
9
|
export default function Tag(props: Props) {
|
12
10
|
const handleChange = () => {
|
@@ -32,15 +32,18 @@ function toggle(tag: string, state: Tags.State) {
|
|
32
32
|
|
33
33
|
function reducer(state: Tags.State, action: Tags.Action) {
|
34
34
|
const { type, payload } = action;
|
35
|
-
const normalized = normalize(payload, state);
|
36
35
|
switch (type) {
|
37
|
-
case "addTag":
|
36
|
+
case "addTag": {
|
37
|
+
const normalized = normalize(payload, state);
|
38
38
|
return {
|
39
39
|
tags: [...state.tags, normalized].filter(onlyUnique),
|
40
40
|
enabled: [...state.enabled, normalized].filter(onlyUnique)
|
41
41
|
};
|
42
|
+
}
|
42
43
|
case "toggleTag":
|
43
|
-
return toggle(
|
44
|
+
return toggle(normalize(payload, state), state);
|
45
|
+
case "update":
|
46
|
+
return payload;
|
44
47
|
default:
|
45
48
|
return state;
|
46
49
|
}
|
@@ -49,7 +52,7 @@ function reducer(state: Tags.State, action: Tags.Action) {
|
|
49
52
|
export default function useTags(
|
50
53
|
initTags: string[],
|
51
54
|
initEnabled: string[]
|
52
|
-
): [Tags.State,
|
55
|
+
): [Tags.State, React.Dispatch<Tags.Action>] {
|
53
56
|
const [state, dispatch] = useReducer(reducer, {
|
54
57
|
tags: initTags,
|
55
58
|
enabled: initEnabled
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import React from "react";
|
2
|
-
|
3
1
|
import useTags from "./TagEditor/useTags";
|
4
2
|
import Editor from "./TagEditor/Editor";
|
5
3
|
import * as Tags from "../types/Tags";
|
6
4
|
|
7
|
-
|
5
|
+
type Props = Tags.State & {
|
8
6
|
name: string;
|
9
|
-
}
|
7
|
+
};
|
10
8
|
|
11
9
|
export default function TagEditor(props: Props) {
|
12
10
|
const [state, dispatch] = useTags(props.tags, props.enabled);
|
@@ -1,11 +1,11 @@
|
|
1
|
-
import
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
2
2
|
|
3
3
|
import useToastStore from "../stores/useToastStore";
|
4
4
|
|
5
|
-
|
5
|
+
type Props = {
|
6
6
|
error: string;
|
7
7
|
notice: string;
|
8
|
-
}
|
8
|
+
};
|
9
9
|
|
10
10
|
export default function Toast(props: Props) {
|
11
11
|
const [fadeout, setFadeout] = useState(false);
|
@@ -21,7 +21,7 @@ export default function Toast(props: Props) {
|
|
21
21
|
if (props.notice) {
|
22
22
|
notice(props.notice);
|
23
23
|
}
|
24
|
-
}, [props.error, props.notice]);
|
24
|
+
}, [props.error, props.notice, error, notice]);
|
25
25
|
|
26
26
|
useEffect(() => {
|
27
27
|
setFadeout(false);
|
@@ -38,7 +38,7 @@ export default function Toast(props: Props) {
|
|
38
38
|
return () => {
|
39
39
|
clearTimeout(timerRef.current);
|
40
40
|
};
|
41
|
-
}, [toast]);
|
41
|
+
}, [toast, next]);
|
42
42
|
|
43
43
|
const classNames = ["toast"];
|
44
44
|
|
@@ -2,7 +2,7 @@ import * as Drag from "../../types/Drag";
|
|
2
2
|
|
3
3
|
function hovering<T>(
|
4
4
|
dragState: Drag.State<T>,
|
5
|
-
target: Drag.
|
5
|
+
target: Drag.DraggableOrFiles<T> | React.MutableRefObject<HTMLDivElement>
|
6
6
|
) {
|
7
7
|
const { x, y } = dragState;
|
8
8
|
let rect: DOMRect;
|
@@ -22,7 +22,7 @@ function hovering<T>(
|
|
22
22
|
export function collectionOrder<T>(
|
23
23
|
collection: Drag.Collection<T>,
|
24
24
|
dragState: Drag.State<T>
|
25
|
-
): Array<Drag.
|
25
|
+
): Array<Drag.DraggableOrFiles<T>> {
|
26
26
|
const { draggables, ref } = collection;
|
27
27
|
const { dragging } = dragState;
|
28
28
|
|
@@ -30,11 +30,11 @@ export function collectionOrder<T>(
|
|
30
30
|
return draggables;
|
31
31
|
}
|
32
32
|
|
33
|
-
let ordered
|
34
|
-
if (
|
33
|
+
let ordered = draggables;
|
34
|
+
if (dragging !== "Files") {
|
35
35
|
ordered = draggables
|
36
|
-
.filter((d) =>
|
37
|
-
.filter((d
|
36
|
+
.filter((d) => d !== "Files")
|
37
|
+
.filter((d) => d.handle !== dragging.handle);
|
38
38
|
}
|
39
39
|
if (hovering(dragState, ref)) {
|
40
40
|
const hovered = ordered.filter((d) => hovering(dragState, d))[0];
|
@@ -11,20 +11,20 @@ function getPosition<T>(draggable: Drag.Draggable<T>) {
|
|
11
11
|
}
|
12
12
|
}
|
13
13
|
|
14
|
-
function
|
15
|
-
|
16
|
-
callback: () => Drag.
|
14
|
+
function hideDragging<T>(
|
15
|
+
dragging: Drag.DraggableOrFiles<T> | null,
|
16
|
+
callback: () => Drag.DraggableOrFiles<T>[]
|
17
17
|
) {
|
18
18
|
if (
|
19
|
-
|
20
|
-
|
21
|
-
"ref" in
|
22
|
-
|
19
|
+
dragging &&
|
20
|
+
dragging !== "Files" &&
|
21
|
+
"ref" in dragging &&
|
22
|
+
dragging.ref.current
|
23
23
|
) {
|
24
|
-
const prevDisplay =
|
25
|
-
|
24
|
+
const prevDisplay = dragging.ref.current.style.display;
|
25
|
+
dragging.ref.current.style.display = "none";
|
26
26
|
const result = callback();
|
27
|
-
|
27
|
+
dragging.ref.current.style.display = prevDisplay;
|
28
28
|
return result;
|
29
29
|
} else {
|
30
30
|
return callback();
|
@@ -32,9 +32,9 @@ function hideDraggable<T>(
|
|
32
32
|
}
|
33
33
|
|
34
34
|
function insertFiles<T>(
|
35
|
-
state: Drag.
|
36
|
-
files: Drag.
|
37
|
-
): Drag.
|
35
|
+
state: Drag.DraggableOrFiles<T>[],
|
36
|
+
files: Drag.DraggableOrFiles<T>[]
|
37
|
+
): Drag.DraggableOrFiles<T>[] {
|
38
38
|
const index = state.indexOf("Files");
|
39
39
|
if (index === -1 || !files) {
|
40
40
|
return state;
|
@@ -43,10 +43,10 @@ function insertFiles<T>(
|
|
43
43
|
}
|
44
44
|
}
|
45
45
|
|
46
|
-
function
|
47
|
-
state: Drag.
|
46
|
+
function reducer<T>(
|
47
|
+
state: Drag.DraggableOrFiles<T>[],
|
48
48
|
action: Drag.CollectionAction<T>
|
49
|
-
): Drag.
|
49
|
+
): Drag.DraggableOrFiles<T>[] {
|
50
50
|
switch (action.type) {
|
51
51
|
case "append":
|
52
52
|
return [...state, ...action.payload];
|
@@ -56,12 +56,10 @@ function dragCollectionReducer<T = Drag.DraggableRecord>(
|
|
56
56
|
return insertFiles(state, action.payload);
|
57
57
|
case "update":
|
58
58
|
return state.map((d: Drag.Draggable<T>) => {
|
59
|
-
return d.handle ===
|
60
|
-
? action.payload
|
61
|
-
: d;
|
59
|
+
return d.handle === action.payload.handle ? action.payload : d;
|
62
60
|
});
|
63
61
|
case "updatePositions":
|
64
|
-
return
|
62
|
+
return hideDragging(action.payload, () => {
|
65
63
|
return state.map((d: Drag.Draggable<T>) => {
|
66
64
|
return { ...d, rect: getPosition(d) };
|
67
65
|
});
|
@@ -81,9 +79,7 @@ function dragCollectionReducer<T = Drag.DraggableRecord>(
|
|
81
79
|
}
|
82
80
|
}
|
83
81
|
|
84
|
-
export function createDraggable<T
|
85
|
-
record: T
|
86
|
-
): Drag.Draggable<T> {
|
82
|
+
export function createDraggable<T>(record: T): Drag.Draggable<T> {
|
87
83
|
return {
|
88
84
|
record: record,
|
89
85
|
rect: null,
|
@@ -92,11 +88,11 @@ export function createDraggable<T = Drag.DraggableRecord>(
|
|
92
88
|
};
|
93
89
|
}
|
94
90
|
|
95
|
-
export default function useDragCollection<T
|
91
|
+
export default function useDragCollection<T>(
|
96
92
|
records: Array<T>
|
97
93
|
): Drag.Collection<T> {
|
98
94
|
const containerRef = useRef<HTMLDivElement>(null);
|
99
|
-
const [draggables, dispatch] = useReducer(
|
95
|
+
const [draggables, dispatch] = useReducer(reducer<T>, [], () =>
|
100
96
|
records.map((r) => createDraggable(r))
|
101
97
|
);
|
102
98
|
|
@@ -8,6 +8,16 @@ type AnyTouchEvent =
|
|
8
8
|
| React.MouseEvent
|
9
9
|
| React.TouchEvent;
|
10
10
|
|
11
|
+
type StartDrag<T> = (
|
12
|
+
evt: AnyTouchEvent,
|
13
|
+
draggable: Drag.DraggableOrFiles<T>
|
14
|
+
) => void;
|
15
|
+
|
16
|
+
type Listeners = {
|
17
|
+
onDragOver: (evt: AnyTouchEvent) => void;
|
18
|
+
onDrop: (evt: AnyTouchEvent) => void;
|
19
|
+
};
|
20
|
+
|
11
21
|
function containsFiles(evt: AnyTouchEvent) {
|
12
22
|
if ("dataTransfer" in evt) {
|
13
23
|
const dataTransfer = evt.dataTransfer as DataTransfer;
|
@@ -52,29 +62,23 @@ function mousePosition(evt: AnyTouchEvent): Drag.Position {
|
|
52
62
|
export default function useDragUploader<T>(
|
53
63
|
collections: Drag.Collection<T>[],
|
54
64
|
onDragEnd: (dragState: Drag.State<T>, files: File[]) => void
|
55
|
-
): [
|
56
|
-
Drag.State<T
|
57
|
-
(evt: AnyTouchEvent, draggable: Drag.Item<T>) => void,
|
58
|
-
{
|
59
|
-
onDragOver: (evt: AnyTouchEvent) => void;
|
60
|
-
onDrop: (evt: AnyTouchEvent) => void;
|
61
|
-
}
|
62
|
-
] {
|
63
|
-
const initialState: Drag.State<T> = {
|
65
|
+
): [Drag.State<T>, StartDrag<T>, Listeners] {
|
66
|
+
const [dragState, setDragState] = useState<Drag.State<T>>({
|
64
67
|
dragging: false,
|
65
68
|
x: null,
|
66
69
|
y: null
|
67
|
-
};
|
68
|
-
|
69
|
-
const [dragState, setDragState] = useState(initialState);
|
70
|
+
});
|
70
71
|
|
71
|
-
const updatePositions = (dragging?: Drag.
|
72
|
+
const updatePositions = (dragging?: Drag.DraggableOrFiles<T>) => {
|
72
73
|
collections.forEach((c) => {
|
73
74
|
c.dispatch({ type: "updatePositions", payload: dragging });
|
74
75
|
});
|
75
76
|
};
|
76
77
|
|
77
|
-
const startDrag = (
|
78
|
+
const startDrag = (
|
79
|
+
evt: AnyTouchEvent,
|
80
|
+
draggable: Drag.DraggableOrFiles<T>
|
81
|
+
) => {
|
78
82
|
updatePositions(draggable);
|
79
83
|
setDragState({ dragging: draggable, ...mousePosition(evt) });
|
80
84
|
};
|
@@ -84,10 +88,8 @@ export default function useDragUploader<T>(
|
|
84
88
|
evt.stopPropagation();
|
85
89
|
evt.preventDefault();
|
86
90
|
setDragState({ ...dragState, ...mousePosition(evt) });
|
87
|
-
} else {
|
88
|
-
|
89
|
-
startDrag(evt, "Files");
|
90
|
-
}
|
91
|
+
} else if (containsFiles(evt)) {
|
92
|
+
startDrag(evt, "Files");
|
91
93
|
}
|
92
94
|
};
|
93
95
|
|