pages_core 3.12.4 → 3.12.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 +8 -43
- data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
- data/app/assets/builds/pages_core/admin.css +264 -133
- data/app/assets/stylesheets/pages_core/admin/components/attachments.css +3 -4
- data/app/assets/stylesheets/pages_core/admin/components/forms.css +17 -16
- data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +8 -4
- data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +1 -1
- data/app/assets/stylesheets/pages_core/admin/components/list_table.css +11 -3
- data/app/assets/stylesheets/pages_core/admin/components/modal.css +9 -5
- data/app/assets/stylesheets/pages_core/admin/components/page_tree.css +5 -1
- data/app/assets/stylesheets/pages_core/admin/components/toast.css +2 -2
- data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +4 -2
- data/app/assets/stylesheets/pages_core/admin/vars.css +2 -1
- data/app/controllers/admin/calendars_controller.rb +2 -2
- data/app/controllers/admin/categories_controller.rb +3 -3
- data/app/controllers/admin/news_controller.rb +6 -6
- data/app/controllers/admin/pages_controller.rb +12 -11
- data/app/controllers/admin/users_controller.rb +1 -1
- data/app/controllers/concerns/pages_core/preview_pages_controller.rb +15 -17
- data/app/controllers/pages_core/admin_controller.rb +2 -2
- data/app/controllers/pages_core/base_controller.rb +1 -8
- data/app/controllers/pages_core/frontend/pages_controller.rb +13 -5
- data/app/controllers/pages_core/frontend_controller.rb +12 -7
- data/app/helpers/admin/menu_helper.rb +2 -0
- data/app/helpers/admin/pages_helper.rb +1 -4
- data/app/helpers/pages_core/admin/admin_helper.rb +0 -1
- data/app/helpers/pages_core/admin/content_tabs_helper.rb +9 -2
- data/app/helpers/pages_core/application_helper.rb +2 -3
- data/app/helpers/pages_core/frontend_helper.rb +1 -1
- data/app/helpers/pages_core/head_tags_helper.rb +15 -46
- data/app/helpers/pages_core/images_helper.rb +76 -21
- data/app/helpers/pages_core/locales_helper.rb +9 -0
- data/app/helpers/pages_core/open_graph_tags_helper.rb +3 -5
- data/app/helpers/pages_core/page_path_helper.rb +1 -1
- data/app/javascript/components/Attachments/Attachment.tsx +55 -52
- data/app/javascript/components/Attachments/AttachmentEditor.tsx +45 -50
- data/app/javascript/components/Attachments/Placeholder.tsx +1 -2
- data/app/javascript/components/Attachments.jsx +69 -57
- data/app/javascript/components/DateRangeSelect.jsx +94 -54
- data/app/javascript/components/EditableImage.tsx +20 -16
- data/app/javascript/components/FileUploadButton.tsx +12 -12
- data/app/javascript/components/ImageCropper/FocalPoint.tsx +22 -20
- data/app/javascript/components/ImageCropper/Image.tsx +20 -16
- data/app/javascript/components/ImageCropper/Toolbar.tsx +35 -27
- data/app/javascript/components/ImageCropper/useCrop.ts +105 -91
- data/app/javascript/components/ImageCropper.tsx +34 -25
- data/app/javascript/components/ImageEditor/Form.tsx +32 -43
- data/app/javascript/components/ImageEditor.tsx +29 -21
- data/app/javascript/components/ImageGrid/DragElement.tsx +6 -4
- data/app/javascript/components/ImageGrid/GridImage.tsx +56 -52
- data/app/javascript/components/ImageGrid/Placeholder.tsx +1 -1
- data/app/javascript/components/ImageGrid.jsx +132 -101
- data/app/javascript/components/ImageUploader.tsx +59 -55
- data/app/javascript/components/Modal.tsx +2 -4
- data/app/javascript/components/PageDates.jsx +25 -20
- data/app/javascript/components/PageFiles.jsx +7 -5
- data/app/javascript/components/PageImages.tsx +9 -7
- data/app/javascript/components/PageTree/Draggable.tsx +46 -40
- data/app/javascript/components/PageTree/Node.tsx +111 -95
- data/app/javascript/components/PageTree/types.ts +9 -9
- data/app/javascript/components/PageTree.tsx +44 -29
- data/app/javascript/components/RichTextArea.jsx +51 -37
- data/app/javascript/components/RichTextToolbarButton.tsx +8 -5
- data/app/javascript/components/TagEditor/AddTagForm.tsx +11 -10
- data/app/javascript/components/TagEditor/Tag.tsx +10 -8
- data/app/javascript/components/TagEditor.tsx +15 -10
- data/app/javascript/components/Toast.tsx +3 -7
- data/app/javascript/components/drag/draggedOrder.ts +16 -15
- data/app/javascript/components/drag/types.ts +12 -12
- data/app/javascript/components/drag/useDragCollection.ts +36 -42
- data/app/javascript/components/drag/useDragUploader.ts +3 -2
- data/app/javascript/components/drag.ts +5 -4
- data/app/javascript/controllers/LoginController.ts +0 -1
- data/app/javascript/controllers/MainController.ts +6 -2
- data/app/javascript/controllers/PageOptionsController.js +7 -2
- data/app/javascript/features/RichText.tsx +9 -7
- data/app/javascript/index.ts +5 -3
- data/app/javascript/lib/Tree.ts +27 -24
- data/app/javascript/lib/copyToClipboard.ts +5 -4
- data/app/javascript/lib/readyHandler.ts +4 -4
- data/app/javascript/lib/request.ts +7 -3
- data/app/javascript/stores/useModalStore.ts +3 -3
- data/app/javascript/stores/useToastStore.ts +14 -12
- data/app/javascript/types.ts +22 -22
- data/app/models/concerns/pages_core/page_model/templateable.rb +1 -1
- data/app/views/admin/calendars/show.html.erb +1 -1
- data/app/views/admin/news/index.html.erb +1 -1
- data/app/views/admin/pages/_edit_files.html.erb +1 -1
- data/app/views/admin/pages/_edit_images.html.erb +1 -1
- data/app/views/admin/pages/_list_item.html.erb +1 -1
- data/app/views/admin/pages/_search_bar.html.erb +1 -1
- data/app/views/admin/pages/deleted.html.erb +2 -2
- data/app/views/admin/pages/edit.html.erb +3 -3
- data/app/views/admin/pages/index.html.erb +4 -4
- data/app/views/admin/pages/new.html.erb +1 -1
- data/app/views/admin/pages/search.html.erb +3 -3
- data/app/views/feeds/pages.rss.builder +2 -2
- data/app/views/layouts/admin/_page_header.html.erb +4 -4
- data/app/views/layouts/admin.html.erb +1 -2
- data/config/locales/en.yml +1 -0
- data/lib/pages_core/pages_plugin.rb +5 -3
- data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +15 -13
- data/lib/tasks/pages/reports.rake +26 -0
- metadata +32 -4
- data/app/helpers/pages_core/admin/deprecated_admin_helper.rb +0 -40
- data/app/views/pages_core/_google_analytics.html.erb +0 -8
|
@@ -5,31 +5,36 @@ import { Attributes, PageNode } from "./PageTree/types";
|
|
|
5
5
|
import Draggable from "./PageTree/Draggable";
|
|
6
6
|
|
|
7
7
|
interface Page extends Record<string, unknown> {
|
|
8
|
-
parent_page_id: number | null
|
|
8
|
+
parent_page_id: number | null;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
type CollapsedState = Record<number, boolean>;
|
|
12
12
|
|
|
13
13
|
interface ParentMap {
|
|
14
|
-
[index: number]: Page[]
|
|
14
|
+
[index: number]: Page[];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
interface PageTreeProps {
|
|
18
|
-
dir: string
|
|
19
|
-
locale: string
|
|
20
|
-
pages: Page[]
|
|
21
|
-
permissions: string[]
|
|
18
|
+
dir: string;
|
|
19
|
+
locale: string;
|
|
20
|
+
pages: Page[];
|
|
21
|
+
permissions: string[];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface PageTreeState {
|
|
25
|
-
tree: Tree<PageNode
|
|
25
|
+
tree: Tree<PageNode>;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function collapsedState(): CollapsedState {
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
if (
|
|
30
|
+
window &&
|
|
31
|
+
window.localStorage &&
|
|
32
|
+
typeof window.localStorage.collapsedPages != "undefined"
|
|
33
|
+
) {
|
|
34
|
+
return JSON.parse(
|
|
35
|
+
window.localStorage.getItem("collapsedPages")
|
|
36
|
+
) as CollapsedState;
|
|
37
|
+
}
|
|
33
38
|
return {};
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -62,15 +67,16 @@ export default class PageTree extends Component<PageTreeProps, PageTreeState> {
|
|
|
62
67
|
node.collapsed = true;
|
|
63
68
|
}
|
|
64
69
|
if (index.children && index.children.length) {
|
|
65
|
-
index.children.forEach(c => walk(c));
|
|
70
|
+
index.children.forEach((c) => walk(c));
|
|
66
71
|
}
|
|
67
72
|
};
|
|
68
73
|
walk(1);
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
createPage(index: TreeIndex<PageNode>, attributes: Attributes) {
|
|
72
|
-
void postJson(`/admin/${index.node.locale}/pages.json`, {
|
|
73
|
-
|
|
77
|
+
void postJson(`/admin/${index.node.locale}/pages.json`, {
|
|
78
|
+
page: attributes
|
|
79
|
+
}).then((response: Attributes) => this.updateNode(index, response));
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
buildTree(pages: Page[]) {
|
|
@@ -81,7 +87,9 @@ export default class PageTree extends Component<PageTreeProps, PageTreeState> {
|
|
|
81
87
|
return m;
|
|
82
88
|
}, {});
|
|
83
89
|
|
|
84
|
-
pages.forEach((p: Page) => {
|
|
90
|
+
pages.forEach((p: Page) => {
|
|
91
|
+
p.children = parentMap[p.id] || [];
|
|
92
|
+
});
|
|
85
93
|
|
|
86
94
|
const tree = new Tree({
|
|
87
95
|
name: "All Pages",
|
|
@@ -95,7 +103,11 @@ export default class PageTree extends Component<PageTreeProps, PageTreeState> {
|
|
|
95
103
|
return tree;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
movePage(
|
|
106
|
+
movePage(
|
|
107
|
+
index: TreeIndex<PageNode>,
|
|
108
|
+
parent: TreeIndex<PageNode>,
|
|
109
|
+
position: number
|
|
110
|
+
) {
|
|
99
111
|
const data = {
|
|
100
112
|
parent_id: parent.node.id,
|
|
101
113
|
position: position
|
|
@@ -105,8 +117,9 @@ export default class PageTree extends Component<PageTreeProps, PageTreeState> {
|
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
performUpdate(index: TreeIndex, url: string, data: Attributes) {
|
|
108
|
-
void putJson(url, data)
|
|
109
|
-
|
|
120
|
+
void putJson(url, data).then((response: Page) =>
|
|
121
|
+
this.updateNode(index, response)
|
|
122
|
+
);
|
|
110
123
|
}
|
|
111
124
|
|
|
112
125
|
render() {
|
|
@@ -116,7 +129,7 @@ export default class PageTree extends Component<PageTreeProps, PageTreeState> {
|
|
|
116
129
|
this.reorderChildren(id);
|
|
117
130
|
this.setCollapsed(id, false);
|
|
118
131
|
this.createPage(index, attributes);
|
|
119
|
-
this.setState({tree: tree});
|
|
132
|
+
this.setState({ tree: tree });
|
|
120
133
|
};
|
|
121
134
|
|
|
122
135
|
const movedPage = (id: TreeId) => {
|
|
@@ -135,7 +148,7 @@ export default class PageTree extends Component<PageTreeProps, PageTreeState> {
|
|
|
135
148
|
const tree = this.state.tree;
|
|
136
149
|
const node = tree.getIndex(id).node;
|
|
137
150
|
this.setCollapsed(id, !node.collapsed);
|
|
138
|
-
this.setState({tree: tree});
|
|
151
|
+
this.setState({ tree: tree });
|
|
139
152
|
};
|
|
140
153
|
|
|
141
154
|
const updatePage = (id: TreeId, attributes: Attributes) => {
|
|
@@ -150,15 +163,17 @@ export default class PageTree extends Component<PageTreeProps, PageTreeState> {
|
|
|
150
163
|
this.setState({ tree: tree });
|
|
151
164
|
};
|
|
152
165
|
|
|
153
|
-
return(
|
|
154
|
-
<Draggable
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
return (
|
|
167
|
+
<Draggable
|
|
168
|
+
tree={this.state.tree}
|
|
169
|
+
addChild={addChild}
|
|
170
|
+
movedPage={movedPage}
|
|
171
|
+
toggleCollapsed={toggleCollapsed}
|
|
172
|
+
updatePage={updatePage}
|
|
173
|
+
updateTree={updateTree}
|
|
174
|
+
locale={this.props.locale}
|
|
175
|
+
dir={this.props.dir}
|
|
176
|
+
/>
|
|
162
177
|
);
|
|
163
178
|
}
|
|
164
179
|
|
|
@@ -20,59 +20,60 @@ export default class RichTextArea extends React.Component {
|
|
|
20
20
|
actions() {
|
|
21
21
|
const simple = [
|
|
22
22
|
{
|
|
23
|
-
name:
|
|
23
|
+
name: "bold",
|
|
24
24
|
className: "bold",
|
|
25
|
-
hotkey:
|
|
26
|
-
fn:
|
|
25
|
+
hotkey: "b",
|
|
26
|
+
fn: (str) => ["<b>", str, "</b>"]
|
|
27
27
|
},
|
|
28
|
-
{
|
|
28
|
+
{
|
|
29
|
+
name: "italic",
|
|
29
30
|
className: "italic",
|
|
30
|
-
hotkey:
|
|
31
|
-
fn:
|
|
32
|
-
}
|
|
31
|
+
hotkey: "i",
|
|
32
|
+
fn: (str) => ["<i>", str, "</i>"]
|
|
33
|
+
}
|
|
33
34
|
];
|
|
34
35
|
|
|
35
36
|
const advanced = [
|
|
36
37
|
{
|
|
37
|
-
name:
|
|
38
|
+
name: "Heading 2",
|
|
38
39
|
className: "header h2",
|
|
39
|
-
fn:
|
|
40
|
+
fn: (str) => ["h2. ", str, ""]
|
|
40
41
|
},
|
|
41
42
|
{
|
|
42
|
-
name:
|
|
43
|
+
name: "Heading 3",
|
|
43
44
|
className: "header h3",
|
|
44
|
-
fn:
|
|
45
|
+
fn: (str) => ["h3. ", str, ""]
|
|
45
46
|
},
|
|
46
47
|
{
|
|
47
|
-
name:
|
|
48
|
+
name: "Heading 4",
|
|
48
49
|
className: "header h4",
|
|
49
|
-
fn:
|
|
50
|
+
fn: (str) => ["h4. ", str, ""]
|
|
50
51
|
},
|
|
51
52
|
{
|
|
52
|
-
name:
|
|
53
|
+
name: "Blockquote",
|
|
53
54
|
className: "quote-left",
|
|
54
|
-
fn:
|
|
55
|
+
fn: (str) => ["bq. ", str, ""]
|
|
55
56
|
},
|
|
56
57
|
{
|
|
57
|
-
name:
|
|
58
|
+
name: "List",
|
|
58
59
|
className: "list-ul",
|
|
59
|
-
fn:
|
|
60
|
+
fn: (str) => ["", this.strToList(str, "*"), ""]
|
|
60
61
|
},
|
|
61
62
|
{
|
|
62
|
-
name:
|
|
63
|
+
name: "Ordered list",
|
|
63
64
|
className: "list-ol",
|
|
64
|
-
fn:
|
|
65
|
+
fn: (str) => ["", this.strToList(str, "#"), ""]
|
|
65
66
|
},
|
|
66
67
|
{
|
|
67
|
-
name:
|
|
68
|
+
name: "Link",
|
|
68
69
|
className: "link",
|
|
69
|
-
fn:
|
|
70
|
+
fn: this.link
|
|
70
71
|
},
|
|
71
72
|
{
|
|
72
|
-
name:
|
|
73
|
+
name: "Email link",
|
|
73
74
|
className: "envelope",
|
|
74
|
-
fn:
|
|
75
|
-
}
|
|
75
|
+
fn: this.emailLink
|
|
76
|
+
}
|
|
76
77
|
];
|
|
77
78
|
|
|
78
79
|
return this.props.simple ? simple : [...simple, ...advanced];
|
|
@@ -86,12 +87,12 @@ export default class RichTextArea extends React.Component {
|
|
|
86
87
|
emailLink(selection) {
|
|
87
88
|
var address = prompt("Enter email address", "");
|
|
88
89
|
let name = selection.length > 0 ? selection : address;
|
|
89
|
-
return ["
|
|
90
|
+
return ['"', name, `":mailto:${address}`];
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
getSelection() {
|
|
93
94
|
let { selectionStart, selectionEnd, value } = this.inputRef.current;
|
|
94
|
-
return value.substr(selectionStart,
|
|
95
|
+
return value.substr(selectionStart, selectionEnd - selectionStart);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
handleChange(evt) {
|
|
@@ -107,13 +108,16 @@ export default class RichTextArea extends React.Component {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
let hotkeys = {};
|
|
110
|
-
this.actions().forEach(a => {
|
|
111
|
+
this.actions().forEach((a) => {
|
|
111
112
|
if (a.hotkey) {
|
|
112
113
|
hotkeys[a.hotkey] = a.fn;
|
|
113
114
|
}
|
|
114
115
|
});
|
|
115
116
|
|
|
116
|
-
if (
|
|
117
|
+
if (
|
|
118
|
+
(evt.metaKey || evt.ctrlKey) &&
|
|
119
|
+
Object.prototype.hasOwnProperty.call(hotkeys, key)
|
|
120
|
+
) {
|
|
117
121
|
evt.preventDefault();
|
|
118
122
|
this.applyAction(hotkeys[key]);
|
|
119
123
|
}
|
|
@@ -123,7 +127,7 @@ export default class RichTextArea extends React.Component {
|
|
|
123
127
|
let name = selection.length > 0 ? selection : "Link text";
|
|
124
128
|
var url = prompt("Enter link URL", "");
|
|
125
129
|
if (url) {
|
|
126
|
-
return ["
|
|
130
|
+
return ['"', name, `":${this.relativeUrl(url)}`];
|
|
127
131
|
} else {
|
|
128
132
|
return ["", name, ""];
|
|
129
133
|
}
|
|
@@ -156,9 +160,11 @@ export default class RichTextArea extends React.Component {
|
|
|
156
160
|
console.log("Error parsing URL: ", error);
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
if (
|
|
164
|
+
url &&
|
|
165
|
+
url.hostname == document.location.hostname &&
|
|
166
|
+
(document.location.port || "80") == (url.port || "80")
|
|
167
|
+
) {
|
|
162
168
|
return url.pathname;
|
|
163
169
|
}
|
|
164
170
|
return str;
|
|
@@ -176,12 +182,14 @@ export default class RichTextArea extends React.Component {
|
|
|
176
182
|
return (
|
|
177
183
|
<div className="rich-text-area">
|
|
178
184
|
<div className="rich-text toolbar">
|
|
179
|
-
{this.actions().map(a =>
|
|
185
|
+
{this.actions().map((a) => (
|
|
180
186
|
<RichTextToolbarButton
|
|
181
187
|
key={a.name}
|
|
182
188
|
name={a.name}
|
|
183
189
|
className={a.className}
|
|
184
|
-
onClick={clickHandler(a.fn)}
|
|
190
|
+
onClick={clickHandler(a.fn)}
|
|
191
|
+
/>
|
|
192
|
+
))}
|
|
185
193
|
</div>
|
|
186
194
|
<textarea
|
|
187
195
|
className="rich"
|
|
@@ -192,7 +200,8 @@ export default class RichTextArea extends React.Component {
|
|
|
192
200
|
rows={rows}
|
|
193
201
|
onChange={this.handleChange}
|
|
194
202
|
onKeyDown={this.handleKeyPress}
|
|
195
|
-
{...this.localeOptions()}
|
|
203
|
+
{...this.localeOptions()}
|
|
204
|
+
/>
|
|
196
205
|
</div>
|
|
197
206
|
);
|
|
198
207
|
}
|
|
@@ -203,7 +212,9 @@ export default class RichTextArea extends React.Component {
|
|
|
203
212
|
|
|
204
213
|
textarea.value =
|
|
205
214
|
value.substr(0, selectionStart) +
|
|
206
|
-
prefix +
|
|
215
|
+
prefix +
|
|
216
|
+
replacement +
|
|
217
|
+
postfix +
|
|
207
218
|
value.substr(selectionEnd, value.length);
|
|
208
219
|
|
|
209
220
|
textarea.focus({ preventScroll: true });
|
|
@@ -215,7 +226,10 @@ export default class RichTextArea extends React.Component {
|
|
|
215
226
|
}
|
|
216
227
|
|
|
217
228
|
strToList(str, prefix) {
|
|
218
|
-
return str
|
|
229
|
+
return str
|
|
230
|
+
.split("\n")
|
|
231
|
+
.map((l) => prefix + " " + l)
|
|
232
|
+
.join("\n");
|
|
219
233
|
}
|
|
220
234
|
}
|
|
221
235
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
interface RichTextToolbarButtonProps {
|
|
4
|
-
className: string
|
|
5
|
-
name: string
|
|
6
|
-
onClick: (evt: Event) => void
|
|
4
|
+
className: string;
|
|
5
|
+
name: string;
|
|
6
|
+
onClick: (evt: Event) => void;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export default function RichTextToolbarButton(
|
|
9
|
+
export default function RichTextToolbarButton(
|
|
10
|
+
props: RichTextToolbarButtonProps
|
|
11
|
+
) {
|
|
10
12
|
return (
|
|
11
|
-
<a
|
|
13
|
+
<a
|
|
14
|
+
title={props.name}
|
|
12
15
|
className={"button " + props.className}
|
|
13
16
|
onClick={props.onClick}>
|
|
14
17
|
<i className={"fa-solid fa-" + props.className} />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { ChangeEvent, useState } from "react";
|
|
2
2
|
|
|
3
3
|
interface AddTagFormProps {
|
|
4
|
-
addTag: (string) => void
|
|
4
|
+
addTag: (string) => void;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export default function AddTagForm(props: AddTagFormProps) {
|
|
@@ -25,15 +25,16 @@ export default function AddTagForm(props: AddTagFormProps) {
|
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
27
|
<div className="add-tag-form">
|
|
28
|
-
<input
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
<input
|
|
29
|
+
name="add-tag"
|
|
30
|
+
type="text"
|
|
31
|
+
className="add-tag"
|
|
32
|
+
value={tag}
|
|
33
|
+
onKeyDown={handleKeyDown}
|
|
34
|
+
onChange={handleChange}
|
|
35
|
+
placeholder="Add tag..."
|
|
36
|
+
/>
|
|
37
|
+
<button onClick={submit} disabled={!tag}>
|
|
37
38
|
Add
|
|
38
39
|
</button>
|
|
39
40
|
</div>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
interface TagProps {
|
|
4
|
-
enabled: boolean
|
|
5
|
-
tag: string
|
|
6
|
-
toggleEnabled: (string) => void
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
tag: string;
|
|
6
|
+
toggleEnabled: (string) => void;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export default function Tag(props: TagProps): JSX.Element {
|
|
@@ -19,11 +19,13 @@ export default function Tag(props: TagProps): JSX.Element {
|
|
|
19
19
|
return (
|
|
20
20
|
<span className={classes.join(" ")}>
|
|
21
21
|
<label className="check-box">
|
|
22
|
-
<input
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
<input
|
|
23
|
+
type="checkbox"
|
|
24
|
+
name={"tag-" + props.tag}
|
|
25
|
+
value="1"
|
|
26
|
+
checked={props.enabled}
|
|
27
|
+
onChange={handleChange}
|
|
28
|
+
/>
|
|
27
29
|
<span className="name">{props.tag}</span>
|
|
28
30
|
</label>
|
|
29
31
|
</span>
|
|
@@ -4,9 +4,9 @@ import AddTagForm from "./TagEditor/AddTagForm";
|
|
|
4
4
|
import Tag from "./TagEditor/Tag";
|
|
5
5
|
|
|
6
6
|
interface TagEditorProps {
|
|
7
|
-
name: string
|
|
8
|
-
enabled: string[]
|
|
9
|
-
tags: string[]
|
|
7
|
+
name: string;
|
|
8
|
+
enabled: string[];
|
|
9
|
+
tags: string[];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function onlyUnique(value: string, index: number, self: string[]): number {
|
|
@@ -21,12 +21,14 @@ export default function TagEditor(props: TagEditorProps) {
|
|
|
21
21
|
|
|
22
22
|
const normalize = (tag: string): string => {
|
|
23
23
|
return (
|
|
24
|
-
tagList.filter(t => t.toLowerCase() == tag.toLowerCase())[0] || tag
|
|
24
|
+
tagList.filter((t) => t.toLowerCase() == tag.toLowerCase())[0] || tag
|
|
25
25
|
);
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const tagEnabled = (tag: string): boolean => {
|
|
29
|
-
return
|
|
29
|
+
return (
|
|
30
|
+
enabled.map((t) => t.toLowerCase()).indexOf(tag.toLowerCase()) !== -1
|
|
31
|
+
);
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
const toggleEnabled = (tag: string) => {
|
|
@@ -49,11 +51,14 @@ export default function TagEditor(props: TagEditorProps) {
|
|
|
49
51
|
return (
|
|
50
52
|
<div className="tag-editor clearfix">
|
|
51
53
|
<input type="hidden" name={props.name} value={JSON.stringify(enabled)} />
|
|
52
|
-
{tagList.map((t) =>
|
|
53
|
-
<Tag
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
{tagList.map((t) => (
|
|
55
|
+
<Tag
|
|
56
|
+
key={t}
|
|
57
|
+
tag={t}
|
|
58
|
+
enabled={tagEnabled(t)}
|
|
59
|
+
toggleEnabled={toggleEnabled}
|
|
60
|
+
/>
|
|
61
|
+
))}
|
|
57
62
|
<AddTagForm addTag={addTag} />
|
|
58
63
|
</div>
|
|
59
64
|
);
|
|
@@ -3,8 +3,8 @@ import React, { useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import useToastStore from "../stores/useToastStore";
|
|
4
4
|
|
|
5
5
|
interface ToastProps {
|
|
6
|
-
error: string
|
|
7
|
-
notice: string
|
|
6
|
+
error: string;
|
|
7
|
+
notice: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export default function Toast(props: ToastProps) {
|
|
@@ -51,11 +51,7 @@ export default function Toast(props: ToastProps) {
|
|
|
51
51
|
|
|
52
52
|
return (
|
|
53
53
|
<div className="toast-wrapper" aria-live="polite">
|
|
54
|
-
{toast && (
|
|
55
|
-
<div className={classNames.join(" ")}>
|
|
56
|
-
{toast.message}
|
|
57
|
-
</div>
|
|
58
|
-
)}
|
|
54
|
+
{toast && <div className={classNames.join(" ")}>{toast.message}</div>}
|
|
59
55
|
</div>
|
|
60
56
|
);
|
|
61
57
|
}
|
|
@@ -13,12 +13,12 @@ function hovering(
|
|
|
13
13
|
} else {
|
|
14
14
|
return false;
|
|
15
15
|
}
|
|
16
|
-
return
|
|
17
|
-
y >= rect.top && y <= rect.bottom);
|
|
16
|
+
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
export function collectionOrder(
|
|
21
|
-
collection: DragCollection,
|
|
20
|
+
collection: DragCollection,
|
|
21
|
+
dragState: DragState
|
|
22
22
|
): Draggable[] {
|
|
23
23
|
const { draggables, ref } = collection;
|
|
24
24
|
const { dragging } = dragState;
|
|
@@ -27,14 +27,12 @@ export function collectionOrder(
|
|
|
27
27
|
return draggables;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
let ordered = draggables.filter(d => d.handle !== dragging.handle);
|
|
30
|
+
let ordered = draggables.filter((d) => d.handle !== dragging.handle);
|
|
31
31
|
if (hovering(dragState, ref)) {
|
|
32
|
-
const hovered = ordered.filter(d => hovering(dragState, d))[0];
|
|
32
|
+
const hovered = ordered.filter((d) => hovering(dragState, d))[0];
|
|
33
33
|
if (hovered) {
|
|
34
34
|
const index = ordered.indexOf(hovered);
|
|
35
|
-
ordered = [...ordered.slice(0, index),
|
|
36
|
-
dragging,
|
|
37
|
-
...ordered.slice(index)];
|
|
35
|
+
ordered = [...ordered.slice(0, index), dragging, ...ordered.slice(index)];
|
|
38
36
|
} else {
|
|
39
37
|
ordered = [...ordered, dragging];
|
|
40
38
|
}
|
|
@@ -44,17 +42,20 @@ export function collectionOrder(
|
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
export default function draggedOrder(
|
|
47
|
-
collection: DragCollection,
|
|
45
|
+
collection: DragCollection,
|
|
46
|
+
dragState: DragState
|
|
48
47
|
): Draggable[] {
|
|
49
48
|
let ordered = collectionOrder(collection, dragState);
|
|
50
49
|
|
|
51
50
|
if (dragState.dragging && ordered.indexOf(dragState.dragging) === -1) {
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
if (
|
|
52
|
+
collection.ref.current &&
|
|
53
|
+
dragState.y < collection.ref.current.getBoundingClientRect().top
|
|
54
|
+
) {
|
|
55
|
+
ordered = [dragState.dragging, ...ordered];
|
|
56
|
+
} else {
|
|
57
|
+
ordered.push(dragState.dragging);
|
|
58
|
+
}
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
return ordered;
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
export type DraggableRecord = Record<string, unknown>;
|
|
2
2
|
|
|
3
3
|
export interface Draggable {
|
|
4
|
-
record: DraggableRecord
|
|
5
|
-
ref: React.MutableRefObject<HTMLDivElement
|
|
6
|
-
rect: DOMRect | null
|
|
7
|
-
handle: string
|
|
4
|
+
record: DraggableRecord;
|
|
5
|
+
ref: React.MutableRefObject<HTMLDivElement>;
|
|
6
|
+
rect: DOMRect | null;
|
|
7
|
+
handle: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export interface DragCollectionAction {
|
|
11
|
-
type: string
|
|
12
|
-
payload?: Draggable[] | Draggable | null
|
|
11
|
+
type: string;
|
|
12
|
+
payload?: Draggable[] | Draggable | null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface DragCollection {
|
|
16
|
-
ref: React.MutableRefObject<HTMLDivElement
|
|
17
|
-
draggables: Draggable[]
|
|
18
|
-
dispatch: (DragCollectionAction) => void
|
|
16
|
+
ref: React.MutableRefObject<HTMLDivElement>;
|
|
17
|
+
draggables: Draggable[];
|
|
18
|
+
dispatch: (DragCollectionAction) => void;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export interface Position {
|
|
22
|
-
x: number | null
|
|
23
|
-
y: number | null
|
|
22
|
+
x: number | null;
|
|
23
|
+
y: number | null;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface DragState extends Position {
|
|
27
|
-
dragging: Draggable | false
|
|
27
|
+
dragging: Draggable | false;
|
|
28
28
|
}
|