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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +59 -14
  4. data/app/assets/builds/pages_core/admin-dist.js.map +7 -0
  5. data/app/assets/builds/pages_core/admin.css +39 -0
  6. data/app/assets/stylesheets/pages_core/admin/components/search.css +27 -0
  7. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +6 -0
  8. data/app/controllers/admin/pages_controller.rb +12 -11
  9. data/app/controllers/concerns/pages_core/rss_controller.rb +17 -1
  10. data/app/controllers/pages_core/admin_controller.rb +6 -0
  11. data/app/controllers/pages_core/frontend/pages_controller.rb +9 -5
  12. data/app/controllers/pages_core/sitemaps_controller.rb +3 -5
  13. data/app/helpers/admin/pages_helper.rb +32 -0
  14. data/app/javascript/admin-dist.ts +2 -0
  15. data/app/javascript/components/Attachments/{Attachment.jsx → Attachment.tsx} +42 -33
  16. data/app/javascript/components/Attachments/{AttachmentEditor.jsx → AttachmentEditor.tsx} +23 -23
  17. data/app/javascript/components/{EditableImage.jsx → EditableImage.tsx} +27 -24
  18. data/app/javascript/components/{FileUploadButton.jsx → FileUploadButton.tsx} +15 -16
  19. data/app/javascript/components/ImageCropper/FocalPoint.tsx +94 -0
  20. data/app/javascript/components/ImageCropper/{Image.jsx → Image.tsx} +13 -14
  21. data/app/javascript/components/ImageCropper/{Toolbar.jsx → Toolbar.tsx} +16 -12
  22. data/app/javascript/components/ImageCropper/{useCrop.js → useCrop.ts} +80 -37
  23. data/app/javascript/components/{ImageCropper.jsx → ImageCropper.tsx} +17 -15
  24. data/app/javascript/components/ImageEditor/{Form.jsx → Form.tsx} +24 -23
  25. data/app/javascript/components/{ImageEditor.jsx → ImageEditor.tsx} +17 -15
  26. data/app/javascript/components/ImageGrid/{DragElement.jsx → DragElement.tsx} +12 -10
  27. data/app/javascript/components/ImageGrid/{GridImage.jsx → GridImage.tsx} +40 -30
  28. data/app/javascript/components/ImageGrid/{Placeholder.jsx → Placeholder.tsx} +5 -6
  29. data/app/javascript/components/ImageGrid.jsx +3 -4
  30. data/app/javascript/components/{ImageUploader.jsx → ImageUploader.tsx} +46 -41
  31. data/app/javascript/components/Modal.tsx +48 -0
  32. data/app/javascript/components/PageImages.tsx +28 -0
  33. data/app/javascript/components/{PageTreeDraggable.jsx → PageTree/Draggable.tsx} +79 -57
  34. data/app/javascript/components/{PageTreeNode.jsx → PageTree/Node.tsx} +79 -70
  35. data/app/javascript/components/PageTree/types.ts +15 -0
  36. data/app/javascript/components/PageTree.tsx +206 -0
  37. data/app/javascript/components/RichTextToolbarButton.tsx +17 -0
  38. data/app/javascript/components/TagEditor/{AddTagForm.jsx → AddTagForm.tsx} +9 -10
  39. data/app/javascript/components/TagEditor/{Tag.jsx → Tag.tsx} +8 -9
  40. data/app/javascript/components/{TagEditor.jsx → TagEditor.tsx} +12 -13
  41. data/app/javascript/components/Toast.tsx +61 -0
  42. data/app/javascript/components/drag/{draggedOrder.js → draggedOrder.ts} +22 -12
  43. data/app/javascript/components/drag/types.ts +28 -0
  44. data/app/javascript/components/drag/{useDragCollection.js → useDragCollection.ts} +40 -22
  45. data/app/javascript/components/drag/{useDragUploader.js → useDragUploader.ts} +34 -25
  46. data/app/javascript/components/drag/useDraggable.ts +21 -0
  47. data/app/javascript/components/{drag.js → drag.ts} +1 -0
  48. data/app/javascript/controllers/{EditPageController.js → EditPageController.ts} +3 -1
  49. data/app/javascript/controllers/{LoginController.js → LoginController.ts} +7 -3
  50. data/app/javascript/controllers/{MainController.js → MainController.ts} +19 -14
  51. data/app/javascript/{index.js → index.ts} +8 -7
  52. data/app/javascript/lib/{Tree.js → Tree.ts} +106 -85
  53. data/app/javascript/lib/{copyToClipboard.js → copyToClipboard.ts} +1 -1
  54. data/app/javascript/lib/{readyHandler.js → readyHandler.ts} +4 -2
  55. data/app/javascript/lib/{request.js → request.ts} +11 -5
  56. data/app/javascript/stores/useModalStore.ts +15 -0
  57. data/app/javascript/stores/useToastStore.ts +26 -0
  58. data/app/javascript/stores.ts +2 -0
  59. data/app/javascript/types.ts +30 -0
  60. data/app/policies/page_policy.rb +4 -0
  61. data/app/views/admin/calendars/_sidebar.html.erb +3 -0
  62. data/app/views/admin/news/_sidebar.html.erb +3 -0
  63. data/app/views/admin/pages/_list_item.html.erb +4 -22
  64. data/app/views/admin/pages/_search_bar.html.erb +12 -0
  65. data/app/views/admin/pages/index.html.erb +3 -0
  66. data/app/views/admin/pages/search.html.erb +54 -0
  67. data/app/views/feeds/pages.rss.builder +3 -9
  68. data/config/routes.rb +1 -0
  69. data/lib/pages_core/configuration/pages.rb +0 -1
  70. data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +33 -17
  71. data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +0 -1
  72. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +40 -0
  73. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +68 -0
  74. data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +17 -0
  75. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.postcss.css +4 -0
  76. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +24 -0
  77. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/layout.css +21 -0
  78. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +5 -0
  79. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +5 -0
  80. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +18 -0
  81. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +6 -0
  82. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +65 -0
  83. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +131 -0
  84. data/lib/rails/generators/pages_core/install/templates/pages_initializer.rb +0 -3
  85. metadata +68 -62
  86. data/app/javascript/admin-dist.js +0 -2
  87. data/app/javascript/components/ImageCropper/FocalPoint.jsx +0 -93
  88. data/app/javascript/components/Modal.jsx +0 -59
  89. data/app/javascript/components/PageImages.jsx +0 -25
  90. data/app/javascript/components/PageTree.jsx +0 -196
  91. data/app/javascript/components/RichTextToolbarButton.jsx +0 -20
  92. data/app/javascript/components/Toast.jsx +0 -72
  93. data/app/javascript/components/drag/useDraggable.js +0 -17
  94. data/app/javascript/stores/ModalStore.jsx +0 -12
  95. data/app/javascript/stores/ToastStore.jsx +0 -14
  96. data/app/javascript/stores.js +0 -2
  97. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/GridOverlay.js +0 -66
  98. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/ResponsiveEmbeds.js +0 -72
  99. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.sass.scss +0 -15
  100. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.scss +0 -12
  101. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.scss +0 -26
  102. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/breakpoints.scss +0 -42
  103. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/clearfix.scss +0 -7
  104. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/fonts.scss +0 -32
  105. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid.scss +0 -168
  106. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid_overlay.scss +0 -44
  107. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.scss +0 -8
  108. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.scss +0 -90
  109. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/vendor/normalize.css +0 -349
  110. /data/app/javascript/components/Attachments/{Placeholder.jsx → Placeholder.tsx} +0 -0
  111. /data/app/javascript/components/ImageGrid/{FilePlaceholder.jsx → FilePlaceholder.tsx} +0 -0
  112. /data/app/javascript/{components.js → components.ts} +0 -0
  113. /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 ToastStore from "../../stores/ToastStore";
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
- export default function GridImage(props) {
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
- ToastStore.dispatch({
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
- let classes = ["grid-image"];
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 image={image}
68
- src={src || image.thumbnail_url}
69
- width={250}
70
- caption={true}
71
- locale={props.locale}
72
- locales={props.locales}
73
- onUpdate={props.onUpdate} />
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
- export default function Placeholder(props) {
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 ToastStore from "../stores/ToastStore";
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
- ToastStore.dispatch({
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 ToastStore from "../stores/ToastStore";
4
+ import useToastStore from "../stores/useToastStore";
5
+ import { ImageResource, Locale } from "../types";
6
6
  import { post } from "../lib/request";
7
7
 
8
- function getFiles(dt) {
9
- var files = [];
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.dataTransfer.items) {
41
- for (var i = 0; i < evt.dataTransfer.items.length; i++) {
42
- evt.dataTransfer.items.remove(i);
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 = getFiles(evt.dataTransfer);
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
- let validTypes = ["image/gif",
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
- let locale = props.locale;
84
- let locales = props.locales ? Object.keys(props.locales) : [locale];
102
+ const locale = props.locale;
103
+ const locales = props.locales ? Object.keys(props.locales) : [locale];
85
104
 
86
- let data = new FormData();
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
- ToastStore.dispatch({
103
- type: "ERROR",
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
- let classes = ["image-uploader"];
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 PropTypes from "prop-types";
30
- import PageTreeNode from "./PageTreeNode";
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
- export default class PageTreeDraggable extends React.Component {
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
- var tree = this.props.tree;
54
- var dragging = this.state.dragging;
88
+ const tree = this.props.tree;
89
+ const dragging = this.state.dragging;
55
90
  if (dragging && dragging.id) {
56
- var draggingIndex = tree.getIndex(dragging.id);
57
- var draggingStyles = {
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
- <PageTreeNode
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
- var dragging = this.state.dragging;
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
- var root = tree.getIndex(1);
123
+ const root = tree.getIndex(1);
89
124
  return (
90
125
  <div className="page-tree">
91
126
  {this.getDraggingDom()}
92
- <PageTreeNode
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
- let newNode = {
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
- let parentNodes = [];
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
- let dragging = this.state.dragging;
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
- var distance = Math.abs(e.clientX - this._offsetX) +
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
- var tree = this.props.tree;
172
- var dragging = this.state.dragging;
173
- var paddingLeft = this.props.paddingLeft;
174
- var newIndex = null;
175
- var index = tree.getIndex(dragging.id);
176
- var collapsed = index.node.collapsed;
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
- var _startX = this._startX;
179
- var _startY = this._startY;
180
- var _offsetX = this._offsetX;
181
- var _offsetY = this._offsetY;
213
+ const _startX = this._startX;
214
+ const _startY = this._startY;
215
+ const _offsetX = this._offsetX;
216
+ const _offsetY = this._offsetY;
182
217
 
183
- var pos = {
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
- var diffX = dragging.x - paddingLeft/2 - (index.left-2) * paddingLeft;
191
- var diffY = dragging.y - dragging.h/2 - (index.top-2 + this.prevAddButtonCount(tree, index)) * dragging.h;
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.prev) {
201
- var prev = tree.getIndex(index.prev);
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
- var above = tree.getNodeByTop(index.top-1);
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
- let below = index.next ?
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
- var self = this;
265
- this._dragListener = (e) => self.drag(e);
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
- PageTreeDraggable.defaultProps = {
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
- };