pages_core 3.12.1 → 3.12.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +135 -50
  4. data/app/assets/builds/pages_core/admin-dist.js.map +7 -0
  5. data/app/assets/builds/pages_core/admin.css +72 -20
  6. data/app/assets/stylesheets/pages_core/admin/components/attachments.css +1 -1
  7. data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +2 -2
  8. data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +8 -8
  9. data/app/assets/stylesheets/pages_core/admin/components/image_uploader.css +2 -2
  10. data/app/assets/stylesheets/pages_core/admin/components/layout.css +2 -2
  11. data/app/assets/stylesheets/pages_core/admin/components/modal.css +2 -2
  12. data/app/assets/stylesheets/pages_core/admin/components/search.css +27 -0
  13. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +6 -0
  14. data/app/controllers/admin/pages_controller.rb +12 -11
  15. data/app/controllers/concerns/pages_core/rss_controller.rb +17 -1
  16. data/app/controllers/pages_core/admin_controller.rb +6 -0
  17. data/app/controllers/pages_core/frontend/pages_controller.rb +9 -5
  18. data/app/controllers/pages_core/sitemaps_controller.rb +3 -5
  19. data/app/helpers/admin/pages_helper.rb +32 -0
  20. data/app/helpers/pages_core/images_helper.rb +28 -7
  21. data/app/javascript/admin-dist.ts +2 -0
  22. data/app/javascript/components/Attachments/{Attachment.jsx → Attachment.tsx} +42 -33
  23. data/app/javascript/components/Attachments/{AttachmentEditor.jsx → AttachmentEditor.tsx} +23 -23
  24. data/app/javascript/components/{EditableImage.jsx → EditableImage.tsx} +27 -24
  25. data/app/javascript/components/{FileUploadButton.jsx → FileUploadButton.tsx} +15 -16
  26. data/app/javascript/components/ImageCropper/FocalPoint.tsx +94 -0
  27. data/app/javascript/components/ImageCropper/{Image.jsx → Image.tsx} +13 -14
  28. data/app/javascript/components/ImageCropper/{Toolbar.jsx → Toolbar.tsx} +16 -12
  29. data/app/javascript/components/ImageCropper/{useCrop.js → useCrop.ts} +80 -37
  30. data/app/javascript/components/{ImageCropper.jsx → ImageCropper.tsx} +17 -15
  31. data/app/javascript/components/ImageEditor/{Form.jsx → Form.tsx} +24 -23
  32. data/app/javascript/components/{ImageEditor.jsx → ImageEditor.tsx} +17 -15
  33. data/app/javascript/components/ImageGrid/{DragElement.jsx → DragElement.tsx} +12 -10
  34. data/app/javascript/components/ImageGrid/{GridImage.jsx → GridImage.tsx} +40 -30
  35. data/app/javascript/components/ImageGrid/{Placeholder.jsx → Placeholder.tsx} +5 -6
  36. data/app/javascript/components/ImageGrid.jsx +3 -4
  37. data/app/javascript/components/{ImageUploader.jsx → ImageUploader.tsx} +46 -41
  38. data/app/javascript/components/Modal.tsx +48 -0
  39. data/app/javascript/components/PageImages.tsx +28 -0
  40. data/app/javascript/components/{PageTreeDraggable.jsx → PageTree/Draggable.tsx} +79 -57
  41. data/app/javascript/components/{PageTreeNode.jsx → PageTree/Node.tsx} +79 -70
  42. data/app/javascript/components/PageTree/types.ts +15 -0
  43. data/app/javascript/components/PageTree.tsx +206 -0
  44. data/app/javascript/components/RichTextToolbarButton.tsx +17 -0
  45. data/app/javascript/components/TagEditor/{AddTagForm.jsx → AddTagForm.tsx} +9 -10
  46. data/app/javascript/components/TagEditor/{Tag.jsx → Tag.tsx} +8 -9
  47. data/app/javascript/components/{TagEditor.jsx → TagEditor.tsx} +12 -13
  48. data/app/javascript/components/Toast.tsx +61 -0
  49. data/app/javascript/components/drag/{draggedOrder.js → draggedOrder.ts} +22 -12
  50. data/app/javascript/components/drag/types.ts +28 -0
  51. data/app/javascript/components/drag/{useDragCollection.js → useDragCollection.ts} +40 -22
  52. data/app/javascript/components/drag/{useDragUploader.js → useDragUploader.ts} +34 -25
  53. data/app/javascript/components/drag/useDraggable.ts +21 -0
  54. data/app/javascript/components/{drag.js → drag.ts} +1 -0
  55. data/app/javascript/controllers/{EditPageController.js → EditPageController.ts} +3 -1
  56. data/app/javascript/controllers/{LoginController.js → LoginController.ts} +7 -3
  57. data/app/javascript/controllers/{MainController.js → MainController.ts} +19 -14
  58. data/app/javascript/features/{RichText.jsx → RichText.tsx} +3 -3
  59. data/app/javascript/{index.js → index.ts} +8 -7
  60. data/app/javascript/lib/{Tree.js → Tree.ts} +106 -85
  61. data/app/javascript/lib/{copyToClipboard.js → copyToClipboard.ts} +1 -1
  62. data/app/javascript/lib/{readyHandler.js → readyHandler.ts} +4 -2
  63. data/app/javascript/lib/{request.js → request.ts} +11 -5
  64. data/app/javascript/stores/useModalStore.ts +15 -0
  65. data/app/javascript/stores/useToastStore.ts +26 -0
  66. data/app/javascript/stores.ts +2 -0
  67. data/app/javascript/types.ts +30 -0
  68. data/app/policies/page_policy.rb +4 -0
  69. data/app/views/admin/calendars/_sidebar.html.erb +3 -0
  70. data/app/views/admin/news/_sidebar.html.erb +3 -0
  71. data/app/views/admin/pages/_list_item.html.erb +4 -22
  72. data/app/views/admin/pages/_search_bar.html.erb +12 -0
  73. data/app/views/admin/pages/index.html.erb +3 -0
  74. data/app/views/admin/pages/search.html.erb +54 -0
  75. data/app/views/feeds/pages.rss.builder +3 -9
  76. data/config/routes.rb +1 -0
  77. data/lib/pages_core/configuration/pages.rb +0 -1
  78. data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +33 -17
  79. data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +0 -1
  80. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +40 -0
  81. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +68 -0
  82. data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +17 -0
  83. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.postcss.css +4 -0
  84. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +24 -0
  85. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/layout.css +21 -0
  86. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +5 -0
  87. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +5 -0
  88. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +18 -0
  89. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +6 -0
  90. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +65 -0
  91. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +131 -0
  92. data/lib/rails/generators/pages_core/install/templates/pages_initializer.rb +0 -3
  93. metadata +69 -63
  94. data/app/javascript/admin-dist.js +0 -2
  95. data/app/javascript/components/ImageCropper/FocalPoint.jsx +0 -93
  96. data/app/javascript/components/Modal.jsx +0 -59
  97. data/app/javascript/components/PageImages.jsx +0 -25
  98. data/app/javascript/components/PageTree.jsx +0 -196
  99. data/app/javascript/components/RichTextToolbarButton.jsx +0 -20
  100. data/app/javascript/components/Toast.jsx +0 -72
  101. data/app/javascript/components/drag/useDraggable.js +0 -17
  102. data/app/javascript/stores/ModalStore.jsx +0 -12
  103. data/app/javascript/stores/ToastStore.jsx +0 -14
  104. data/app/javascript/stores.js +0 -2
  105. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/GridOverlay.js +0 -66
  106. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/ResponsiveEmbeds.js +0 -72
  107. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.sass.scss +0 -15
  108. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.scss +0 -12
  109. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.scss +0 -26
  110. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/breakpoints.scss +0 -42
  111. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/clearfix.scss +0 -7
  112. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/fonts.scss +0 -32
  113. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid.scss +0 -168
  114. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid_overlay.scss +0 -44
  115. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.scss +0 -8
  116. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.scss +0 -90
  117. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/vendor/normalize.css +0 -349
  118. /data/app/javascript/components/Attachments/{Placeholder.jsx → Placeholder.tsx} +0 -0
  119. /data/app/javascript/components/ImageGrid/{FilePlaceholder.jsx → FilePlaceholder.tsx} +0 -0
  120. /data/app/javascript/{components.js → components.ts} +0 -0
  121. /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,5 +1,5 @@
1
1
  import React from "react";
2
- import ReactDOM from "react-dom";
2
+ import { createRoot } from "react-dom/client";
3
3
  import RichTextArea from "../components/RichTextArea";
4
4
  import readyHandler from "../lib/readyHandler";
5
5
 
@@ -11,10 +11,10 @@ class RichText {
11
11
  });
12
12
  }
13
13
 
14
- enhance (elem) {
14
+ enhance (elem: HTMLTextAreaElement) {
15
15
  const container = document.createElement("div");
16
16
  elem.parentNode.appendChild(container);
17
- ReactDOM.render(
17
+ createRoot(container).render(
18
18
  <RichTextArea value={elem.value}
19
19
  name={elem.name}
20
20
  rows={elem.rows}
@@ -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";