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
@@ -0,0 +1,61 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+
3
+ import useToastStore from "../stores/useToastStore";
4
+
5
+ interface ToastProps {
6
+ error: string,
7
+ notice: string
8
+ }
9
+
10
+ export default function Toast(props: ToastProps) {
11
+ const [fadeout, setFadeout] = useState(false);
12
+ const { toasts, error, notice, next } = useToastStore((state) => state);
13
+ const timerRef = useRef<number | null>(null);
14
+
15
+ const toast = toasts[0];
16
+
17
+ useEffect(() => {
18
+ if (props.error) {
19
+ error(props.error);
20
+ }
21
+ if (props.notice) {
22
+ notice(props.notice);
23
+ }
24
+ }, [props.error, props.notice]);
25
+
26
+ useEffect(() => {
27
+ setFadeout(false);
28
+ if (toast && !timerRef.current) {
29
+ timerRef.current = setTimeout(() => {
30
+ setFadeout(true);
31
+ timerRef.current = setTimeout(() => {
32
+ timerRef.current = null;
33
+ setFadeout(false);
34
+ next();
35
+ }, 500);
36
+ }, 4000);
37
+ }
38
+ return () => {
39
+ clearTimeout(timerRef.current);
40
+ };
41
+ }, [toast]);
42
+
43
+ const classNames = ["toast"];
44
+
45
+ if (toast) {
46
+ classNames.push(toast.type);
47
+ if (fadeout) {
48
+ classNames.push("fadeout");
49
+ }
50
+ }
51
+
52
+ return (
53
+ <div className="toast-wrapper" aria-live="polite">
54
+ {toast && (
55
+ <div className={classNames.join(" ")}>
56
+ {toast.message}
57
+ </div>
58
+ )}
59
+ </div>
60
+ );
61
+ }
@@ -1,9 +1,14 @@
1
- function hovering(dragState, target) {
2
- let { x, y } = dragState;
3
- var rect;
4
- if (target.rect) {
1
+ import { Draggable, DragCollection, DragState } from "./types";
2
+
3
+ function hovering(
4
+ dragState: DragState,
5
+ target: Draggable | React.MutableRefObject<HTMLDivElement>
6
+ ): boolean {
7
+ const { x, y } = dragState;
8
+ let rect: DOMRect;
9
+ if ("rect" in target) {
5
10
  rect = target.rect;
6
- } else if (target.current) {
11
+ } else if ("current" in target) {
7
12
  rect = target.current.getBoundingClientRect();
8
13
  } else {
9
14
  return false;
@@ -12,7 +17,9 @@ function hovering(dragState, target) {
12
17
  y >= rect.top && y <= rect.bottom);
13
18
  }
14
19
 
15
- export function collectionOrder(collection, dragState) {
20
+ export function collectionOrder(
21
+ collection: DragCollection, dragState: DragState
22
+ ): Draggable[] {
16
23
  const { draggables, ref } = collection;
17
24
  const { dragging } = dragState;
18
25
 
@@ -36,15 +43,18 @@ export function collectionOrder(collection, dragState) {
36
43
  return ordered;
37
44
  }
38
45
 
39
- export default function draggedOrder(collection, dragState) {
46
+ export default function draggedOrder(
47
+ collection: DragCollection, dragState: DragState
48
+ ): Draggable[] {
40
49
  let ordered = collectionOrder(collection, dragState);
41
50
 
42
51
  if (dragState.dragging && ordered.indexOf(dragState.dragging) === -1) {
43
- if (dragState.y < collection.ref.current.getBoundingClientRect().top) {
44
- ordered = [dragState.dragging, ...ordered];
45
- } else {
46
- ordered.push(dragState.dragging);
47
- }
52
+ if (collection.ref.current &&
53
+ dragState.y < collection.ref.current.getBoundingClientRect().top) {
54
+ ordered = [dragState.dragging, ...ordered];
55
+ } else {
56
+ ordered.push(dragState.dragging);
57
+ }
48
58
  }
49
59
 
50
60
  return ordered;
@@ -0,0 +1,28 @@
1
+ export type DraggableRecord = Record<string, unknown>;
2
+
3
+ export interface Draggable {
4
+ record: DraggableRecord,
5
+ ref: React.MutableRefObject<HTMLDivElement>,
6
+ rect: DOMRect | null,
7
+ handle: string
8
+ }
9
+
10
+ export interface DragCollectionAction {
11
+ type: string,
12
+ payload?: Draggable[] | Draggable | null
13
+ }
14
+
15
+ export interface DragCollection {
16
+ ref: React.MutableRefObject<HTMLDivElement>,
17
+ draggables: Draggable[],
18
+ dispatch: (DragCollectionAction) => void
19
+ }
20
+
21
+ export interface Position {
22
+ x: number | null,
23
+ y: number | null
24
+ }
25
+
26
+ export interface DragState extends Position {
27
+ dragging: Draggable | false
28
+ }
@@ -1,15 +1,24 @@
1
- import React, { useEffect, useReducer, useRef } from "react";
2
- import uniqueId from "lodash/uniqueId";
1
+ import { createRef, useEffect, useReducer, useRef } from "react";
2
+ import { uniqueId } from "lodash";
3
3
 
4
- function getPosition(draggable) {
5
- if (draggable.ref.current) {
4
+ import {
5
+ Draggable,
6
+ DraggableRecord,
7
+ DragCollectionAction,
8
+ DragCollection
9
+ } from "./types";
10
+
11
+ type Draggables = Draggable[];
12
+
13
+ function getPosition(draggable: Draggable) {
14
+ if (draggable && draggable.ref && draggable.ref.current) {
6
15
  return draggable.ref.current.getBoundingClientRect();
7
16
  } else {
8
17
  return null;
9
18
  }
10
19
  }
11
20
 
12
- function hideDraggable(draggable, callback) {
21
+ function hideDraggable(draggable: Draggable | null, callback: () => void) {
13
22
  if (draggable && draggable.ref && draggable.ref.current) {
14
23
  const prevDisplay = draggable.ref.current.style.display;
15
24
  draggable.ref.current.style.display = "none";
@@ -22,22 +31,29 @@ function hideDraggable(draggable, callback) {
22
31
  }
23
32
  }
24
33
 
25
- function dragCollectionReducer(state, action) {
34
+ function insertFiles(state: Draggable[], files: Draggable[]): Draggable[] {
35
+ const index = state.indexOf("Files");
36
+ if (index === -1 || !files) {
37
+ return state;
38
+ } else {
39
+ return [
40
+ ...state.slice(0, index),
41
+ ...files,
42
+ ...state.slice(index + 1)
43
+ ];
44
+ }
45
+ }
46
+
47
+ function dragCollectionReducer(
48
+ state: Draggable[], action: DragCollectionAction
49
+ ): Draggable[] {
26
50
  switch (action.type) {
27
51
  case "append":
28
- return [...state, ...action.payload];
52
+ return [...state, ...action.payload as Draggable[]];
29
53
  case "prepend":
30
- return [...action.payload, ...state];
54
+ return [...action.payload as Draggable[], ...state];
31
55
  case "insertFiles":
32
- var index = state.indexOf("Files");
33
-
34
- if (index === -1 || !action.payload) {
35
- return state;
36
- } else {
37
- return [...state.slice(0, index),
38
- ...action.payload,
39
- ...state.slice(index + 1)];
40
- }
56
+ return insertFiles(state, action.payload);
41
57
  case "update":
42
58
  return state.map(d => {
43
59
  return (d.handle === action.payload.handle) ? action.payload : d;
@@ -59,20 +75,22 @@ function dragCollectionReducer(state, action) {
59
75
  }
60
76
  }
61
77
 
62
- export function createDraggable(record) {
78
+ export function createDraggable(record: Record<string, unknown>): Draggable {
63
79
  return { record: record,
64
80
  rect: null,
65
- ref: React.createRef(),
81
+ ref: createRef(),
66
82
  handle: uniqueId("draggable") };
67
83
  }
68
84
 
69
- export default function useDragCollection(records) {
70
- const containerRef = useRef();
85
+ export default function useDragCollection(
86
+ records: DraggableRecord[]
87
+ ): DragCollection {
88
+ const containerRef = useRef<HTMLElement>(null);
71
89
  const [draggables, dispatch] = useReducer(
72
90
  dragCollectionReducer,
73
91
  [],
74
92
  () => records.map(r => createDraggable(r))
75
- );
93
+ ) as [Draggables, (Draggables) => Draggable[]];
76
94
 
77
95
  useEffect(() => {
78
96
  dispatch({ type: "updatePositions" });
@@ -1,20 +1,24 @@
1
1
  import { useEffect, useState } from "react";
2
2
 
3
- function containsFiles(evt) {
4
- if (!evt.dataTransfer || !evt.dataTransfer.types) {
5
- return false;
6
- }
7
- const types = evt.dataTransfer.types;
8
- for (var i = 0; i < types.length; i++) {
9
- if (types[i] === "Files" || types[i] === "application/x-moz-file") {
10
- return true;
3
+ import { Draggable, DragCollection, DragState, Position } from "./types";
4
+
5
+ function containsFiles(evt: Event) {
6
+ if ("dataTransfer" in evt) {
7
+ const dataTransfer: DataTransfer = evt.dataTransfer;
8
+ if ("types" in dataTransfer) {
9
+ const types = dataTransfer.types;
10
+ for (let i = 0; i < types.length; i++) {
11
+ if (types[i] === "Files" || types[i] === "application/x-moz-file") {
12
+ return true;
13
+ }
14
+ }
11
15
  }
12
16
  }
13
17
  return false;
14
18
  }
15
19
 
16
- function getFiles(dt) {
17
- var files = [];
20
+ function getFiles(dt: DataTransfer): File[] {
21
+ const files: File[] = [];
18
22
  if (dt.items) {
19
23
  for (let i = 0; i < dt.items.length; i++) {
20
24
  if (dt.items[i].kind == "file") {
@@ -29,36 +33,41 @@ function getFiles(dt) {
29
33
  return files;
30
34
  }
31
35
 
32
- function mousePosition(evt) {
33
- var x, y;
34
- if (evt.type == "touchmove") {
36
+ function mousePosition(evt: TouchEvent | MouseEvent): Position {
37
+ let x: number | null, y: number | null;
38
+ if ("touches" in evt && evt.type == "touchmove") {
35
39
  x = evt.touches[0].clientX;
36
40
  y = evt.touches[0].clientY;
37
- } else {
41
+ } else if (evt instanceof MouseEvent) {
38
42
  x = evt.clientX;
39
43
  y = evt.clientY;
40
44
  }
41
45
  return { x: x, y: y };
42
46
  }
43
47
 
44
- export default function useDragUploader(collections, onDragEnd) {
45
- const [dragState, setDragState] = useState({
48
+ export default function useDragUploader(
49
+ collections: DragCollection[],
50
+ onDragEnd: (dragState: DragState, files: File[]) => void
51
+ ) {
52
+ const initialState: DragState = {
46
53
  dragging: false,
47
54
  x: null, y: null
48
- });
55
+ };
56
+
57
+ const [dragState, setDragState] = useState(initialState);
49
58
 
50
- const updatePositions = (dragging) => {
59
+ const updatePositions = (dragging: Draggable | null) => {
51
60
  collections.forEach(c => {
52
61
  c.dispatch({ type: "updatePositions", payload: dragging });
53
62
  });
54
63
  };
55
64
 
56
- const startDrag = (evt, draggable) => {
65
+ const startDrag = (evt: Event, draggable: Draggable) => {
57
66
  updatePositions(draggable);
58
67
  setDragState({ dragging: draggable, ...mousePosition(evt) });
59
68
  };
60
69
 
61
- const drag = (evt) => {
70
+ const drag = (evt: Event) => {
62
71
  if (dragState.dragging) {
63
72
  evt.stopPropagation();
64
73
  evt.preventDefault();
@@ -70,13 +79,13 @@ export default function useDragUploader(collections, onDragEnd) {
70
79
  }
71
80
  };
72
81
 
73
- const dragEnd = (evt) => {
82
+ const dragEnd = (evt: Event) => {
74
83
  if (dragState.dragging) {
75
84
  const prevDragState = dragState;
76
- var files = [];
77
- evt.preventDefault();
85
+ let files: File[] = [];
78
86
  evt.stopPropagation();
79
- if (dragState.dragging == "Files") {
87
+ evt.preventDefault();
88
+ if ("dataTransfer" in evt && dragState.dragging == "Files") {
80
89
  files = getFiles(evt.dataTransfer);
81
90
  }
82
91
  setDragState({ dragging: false, x: null, y: null });
@@ -85,7 +94,7 @@ export default function useDragUploader(collections, onDragEnd) {
85
94
  }
86
95
  };
87
96
 
88
- const dragLeave = (evt) => {
97
+ const dragLeave = (evt: Event) => {
89
98
  if (dragState.dragging === "Files") {
90
99
  evt.preventDefault();
91
100
  evt.stopPropagation();
@@ -0,0 +1,21 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { Draggable } from "./types";
3
+
4
+ export default function useDraggable(
5
+ draggable: Draggable,
6
+ startDrag: (evt: React.MouseEvent, draggable: Draggable) => void
7
+ ) {
8
+ const ref = useRef<HTMLDivElement>(null);
9
+
10
+ const handleDrag = (evt: Event) => {
11
+ evt.preventDefault();
12
+ evt.stopPropagation();
13
+ startDrag(evt, draggable);
14
+ };
15
+
16
+ useEffect(() => {
17
+ draggable.ref.current = ref.current;
18
+ }, []);
19
+
20
+ return { ref: ref, onDragStart: handleDrag, draggable: true };
21
+ }
@@ -1,3 +1,4 @@
1
+ export { Draggable, DragState } from "./drag/types";
1
2
  export { default as useDragCollection,
2
3
  createDraggable } from "./drag/useDragCollection";
3
4
  export { default as useDragUploader } from "./drag/useDragUploader";
@@ -1,6 +1,8 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
2
 
3
- export default class EditPageController extends Controller {
3
+ export default class EditPageController extends Controller<HTMLFormElement> {
4
+ readonly formTarget: HTMLFormElement;
5
+
4
6
  static get targets() {
5
7
  return ["form"];
6
8
  }
@@ -1,6 +1,8 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
2
 
3
3
  export default class LoginController extends Controller {
4
+ readonly tabTargets: HTMLDivElement[];
5
+
4
6
  static get targets() {
5
7
  return ["tab"];
6
8
  }
@@ -12,12 +14,14 @@ export default class LoginController extends Controller {
12
14
  }
13
15
 
14
16
 
15
- changeTab(evt) {
17
+ changeTab(evt: Event) {
16
18
  evt.preventDefault();
17
- this.showTab(evt.target.dataset.tab);
19
+ if ("dataset" in evt.target && "tab" in evt.target.dataset) {
20
+ this.showTab(evt.target.dataset.tab);
21
+ }
18
22
  }
19
23
 
20
- showTab(tab) {
24
+ showTab(tab: string) {
21
25
  this.tabTargets.forEach((t) => {
22
26
  if (t.dataset.tab == tab) {
23
27
  t.classList.remove("hidden");
@@ -1,18 +1,21 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
2
 
3
3
  export default class MainController extends Controller {
4
+ readonly linkTargets: HTMLLinkElement[];
5
+ readonly tabTargets: HTMLDivElement[];
6
+
4
7
  static get targets() {
5
8
  return ["tab", "link"];
6
9
  }
7
10
 
8
11
  connect() {
9
- let tabs = this.tabNames();
12
+ const tabs = this.tabNames();
10
13
  if (tabs.length > 0) {
11
- let initTab = null;
14
+ let initTab: string = null;
12
15
  const tabExpression = /#(.*)$/;
13
16
 
14
17
  if (document.location.toString().match(tabExpression)) {
15
- let id = document.location.toString().match(tabExpression)[1];
18
+ const id = document.location.toString().match(tabExpression)[1];
16
19
  if (tabs.indexOf(id) !== -1) {
17
20
  initTab = id;
18
21
  }
@@ -21,27 +24,29 @@ export default class MainController extends Controller {
21
24
  this.showTab(initTab || tabs[0]);
22
25
  }
23
26
 
24
- window.addEventListener("popstate", this.stateHandler.bind(this));
27
+ window.addEventListener("popstate", this.stateHandler);
25
28
  }
26
29
 
27
30
  disconnect() {
28
- window.removeEventListener("popstate", this.stateHandler.bind(this));
31
+ window.removeEventListener("popstate", this.stateHandler);
29
32
  }
30
33
 
31
- stateHandler(evt) {
32
- if (evt.state && evt.state.tabId) {
34
+ stateHandler = (evt: Event) => {
35
+ if ("state" in evt && "tabId" in evt.state) {
33
36
  this.showTab(evt.state.tabId);
34
37
  }
35
- }
38
+ };
36
39
 
37
- changeTab(evt) {
40
+ changeTab(evt: Event) {
38
41
  evt.preventDefault();
39
- const tab = evt.target.dataset.tab;
40
- this.showTab(tab);
41
- history.pushState({ tabId: tab }, "", `${window.location.pathname}#${tab}`);
42
+ if ("dataset" in evt.target && "tab" in evt.target.dataset) {
43
+ const tab = evt.target.dataset.tab as string;
44
+ this.showTab(tab);
45
+ history.pushState({ tabId: tab }, "", `${window.location.pathname}#${tab}`);
46
+ }
42
47
  }
43
48
 
44
- showTab(tab) {
49
+ showTab(tab: string | null) {
45
50
  this.linkTargets.forEach((l) => {
46
51
  if (l.dataset.tab == tab) {
47
52
  l.classList.add("current");
@@ -59,7 +64,7 @@ export default class MainController extends Controller {
59
64
  });
60
65
  }
61
66
 
62
- tabNames () {
67
+ tabNames (): string[] {
63
68
  return this.linkTargets.map((l) => l.dataset.tab);
64
69
  }
65
70
  }
@@ -1,6 +1,7 @@
1
- import Rails from "@rails/ujs";
1
+ import { start as startRails } from "@rails/ujs";
2
2
  import { Application } from "@hotwired/stimulus";
3
- require("react_ujs");
3
+ import "react_ujs";
4
+ import { FC } from "react";
4
5
 
5
6
  import * as Components from "./components";
6
7
 
@@ -11,14 +12,14 @@ import PageOptionsController from "./controllers/PageOptionsController";
11
12
 
12
13
  import RichText from "./features/RichText";
13
14
 
14
- export function registerComponent(name, component) {
15
+ export function registerComponent(name: string, component: FC) {
15
16
  window[name] = component;
16
17
  }
17
18
 
18
19
  export default function startPages () {
19
- Rails.start();
20
- for (var name in Components) {
21
- registerComponent(name, Components[name]);
20
+ startRails();
21
+ for (const name in Components) {
22
+ registerComponent(name, Components[name] as FC);
22
23
  }
23
24
  RichText.start();
24
25
 
@@ -33,6 +34,6 @@ export * from "./components";
33
34
  export * from "./hooks";
34
35
  export * from "./stores";
35
36
 
36
- export * from "./lib/request.js";
37
+ export * from "./lib/request";
37
38
  export { default as copyToClipboard,
38
39
  copySupported } from "./lib/copyToClipboard";