pages_core 3.13.0 → 3.15.0

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 (257) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +19 -8
  4. data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
  5. data/app/assets/builds/pages_core/admin.css +704 -388
  6. data/app/assets/fonts/Inter-Black.woff2 +0 -0
  7. data/app/assets/fonts/Inter-BlackItalic.woff2 +0 -0
  8. data/app/assets/fonts/Inter-Bold.woff2 +0 -0
  9. data/app/assets/fonts/Inter-BoldItalic.woff2 +0 -0
  10. data/app/assets/fonts/Inter-ExtraBold.woff2 +0 -0
  11. data/app/assets/fonts/Inter-ExtraBoldItalic.woff2 +0 -0
  12. data/app/assets/fonts/Inter-ExtraLight.woff2 +0 -0
  13. data/app/assets/fonts/Inter-ExtraLightItalic.woff2 +0 -0
  14. data/app/assets/fonts/Inter-Italic.woff2 +0 -0
  15. data/app/assets/fonts/Inter-Light.woff2 +0 -0
  16. data/app/assets/fonts/Inter-LightItalic.woff2 +0 -0
  17. data/app/assets/fonts/Inter-Medium.woff2 +0 -0
  18. data/app/assets/fonts/Inter-MediumItalic.woff2 +0 -0
  19. data/app/assets/fonts/Inter-Regular.woff2 +0 -0
  20. data/app/assets/fonts/Inter-SemiBold.woff2 +0 -0
  21. data/app/assets/fonts/Inter-SemiBoldItalic.woff2 +0 -0
  22. data/app/assets/fonts/Inter-Thin.woff2 +0 -0
  23. data/app/assets/fonts/Inter-ThinItalic.woff2 +0 -0
  24. data/app/assets/fonts/InterDisplay-Black.woff2 +0 -0
  25. data/app/assets/fonts/InterDisplay-BlackItalic.woff2 +0 -0
  26. data/app/assets/fonts/InterDisplay-Bold.woff2 +0 -0
  27. data/app/assets/fonts/InterDisplay-BoldItalic.woff2 +0 -0
  28. data/app/assets/fonts/InterDisplay-ExtraBold.woff2 +0 -0
  29. data/app/assets/fonts/InterDisplay-ExtraBoldItalic.woff2 +0 -0
  30. data/app/assets/fonts/InterDisplay-ExtraLight.woff2 +0 -0
  31. data/app/assets/fonts/InterDisplay-ExtraLightItalic.woff2 +0 -0
  32. data/app/assets/fonts/InterDisplay-Italic.woff2 +0 -0
  33. data/app/assets/fonts/InterDisplay-Light.woff2 +0 -0
  34. data/app/assets/fonts/InterDisplay-LightItalic.woff2 +0 -0
  35. data/app/assets/fonts/InterDisplay-Medium.woff2 +0 -0
  36. data/app/assets/fonts/InterDisplay-MediumItalic.woff2 +0 -0
  37. data/app/assets/fonts/InterDisplay-Regular.woff2 +0 -0
  38. data/app/assets/fonts/InterDisplay-SemiBold.woff2 +0 -0
  39. data/app/assets/fonts/InterDisplay-SemiBoldItalic.woff2 +0 -0
  40. data/app/assets/fonts/InterDisplay-Thin.woff2 +0 -0
  41. data/app/assets/fonts/InterDisplay-ThinItalic.woff2 +0 -0
  42. data/app/assets/fonts/InterVariable-Italic.woff2 +0 -0
  43. data/app/assets/fonts/InterVariable.woff2 +0 -0
  44. data/app/assets/stylesheets/pages_core/admin/components/archive.css +1 -1
  45. data/app/assets/stylesheets/pages_core/admin/components/attachments.css +22 -34
  46. data/app/assets/stylesheets/pages_core/admin/components/base.css +1 -68
  47. data/app/assets/stylesheets/pages_core/admin/components/forms.css +107 -48
  48. data/app/assets/stylesheets/pages_core/admin/components/header.css +56 -58
  49. data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +35 -24
  50. data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +28 -27
  51. data/app/assets/stylesheets/pages_core/admin/components/image_uploader.css +5 -5
  52. data/app/assets/stylesheets/pages_core/admin/components/layout.css +7 -1
  53. data/app/assets/stylesheets/pages_core/admin/components/list_table.css +24 -15
  54. data/app/assets/stylesheets/pages_core/admin/components/page_tree.css +63 -104
  55. data/app/assets/stylesheets/pages_core/admin/components/pagination.css +12 -13
  56. data/app/assets/stylesheets/pages_core/admin/components/search.css +1 -16
  57. data/app/assets/stylesheets/pages_core/admin/components/sidebar.css +5 -11
  58. data/app/assets/stylesheets/pages_core/admin/components/tag_editor.css +22 -36
  59. data/app/assets/stylesheets/pages_core/admin/components/toast.css +1 -2
  60. data/app/assets/stylesheets/pages_core/admin/components/toolbar.css +10 -10
  61. data/app/assets/stylesheets/pages_core/admin/components/totp.css +26 -0
  62. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +37 -51
  63. data/app/assets/stylesheets/pages_core/admin/global/fonts.css +271 -0
  64. data/app/assets/stylesheets/pages_core/admin/global/typography.css +109 -0
  65. data/app/assets/stylesheets/pages_core/admin/vars.css +1 -3
  66. data/app/assets/stylesheets/pages_core/admin.postcss.css +1 -0
  67. data/app/controllers/admin/account_recoveries_controller.rb +87 -0
  68. data/app/controllers/admin/invites_controller.rb +3 -2
  69. data/app/controllers/admin/otp_secrets_controller.rb +45 -0
  70. data/app/controllers/admin/pages_controller.rb +22 -42
  71. data/app/controllers/admin/recovery_codes_controller.rb +32 -0
  72. data/app/controllers/admin/sessions_controller.rb +65 -0
  73. data/app/controllers/admin/users_controller.rb +2 -8
  74. data/app/controllers/concerns/pages_core/authentication.rb +12 -10
  75. data/app/controllers/concerns/pages_core/error_reporting.rb +1 -1
  76. data/app/controllers/concerns/pages_core/page_parameters.rb +29 -0
  77. data/app/controllers/concerns/pages_core/policies_helper.rb +1 -1
  78. data/app/controllers/concerns/pages_core/preview_pages_controller.rb +20 -20
  79. data/app/controllers/pages_core/admin_controller.rb +1 -3
  80. data/app/controllers/pages_core/frontend/pages_controller.rb +2 -6
  81. data/app/formatters/pages_core/html_formatter.rb +2 -4
  82. data/app/helpers/admin/menu_helper.rb +5 -4
  83. data/app/helpers/admin/pages_helper.rb +1 -21
  84. data/app/helpers/pages_core/admin/admin_helper.rb +13 -3
  85. data/app/helpers/pages_core/admin/content_tabs_helper.rb +1 -2
  86. data/app/helpers/pages_core/admin/labelled_field_helper.rb +1 -1
  87. data/app/helpers/pages_core/frontend_helper.rb +1 -1
  88. data/app/helpers/pages_core/images_helper.rb +10 -8
  89. data/app/helpers/pages_core/labelled_form_builder.rb +2 -7
  90. data/app/helpers/pages_core/page_path_helper.rb +1 -1
  91. data/app/javascript/components/Attachments/Attachment.tsx +20 -18
  92. data/app/javascript/components/Attachments/AttachmentEditor.tsx +11 -9
  93. data/app/javascript/components/{Attachments.jsx → Attachments/List.tsx} +58 -63
  94. data/app/javascript/components/Attachments/useAttachments.ts +15 -0
  95. data/app/javascript/components/Attachments.tsx +14 -0
  96. data/app/javascript/components/DateRangeSelect.tsx +105 -0
  97. data/app/javascript/components/DateTimeSelect.tsx +136 -0
  98. data/app/javascript/components/EditableImage.tsx +11 -9
  99. data/app/javascript/components/FileUploadButton.tsx +7 -7
  100. data/app/javascript/components/ImageCropper/FocalPoint.tsx +9 -12
  101. data/app/javascript/components/ImageCropper/Image.tsx +10 -8
  102. data/app/javascript/components/ImageCropper/Toolbar.tsx +11 -12
  103. data/app/javascript/components/ImageCropper/useCrop.ts +24 -53
  104. data/app/javascript/components/ImageCropper.tsx +10 -15
  105. data/app/javascript/components/ImageEditor/Form.tsx +12 -8
  106. data/app/javascript/components/ImageEditor.tsx +12 -7
  107. data/app/javascript/components/ImageGrid/DragElement.tsx +9 -12
  108. data/app/javascript/components/{ImageGrid.jsx → ImageGrid/Grid.tsx} +62 -71
  109. data/app/javascript/components/ImageGrid/GridImage.tsx +22 -23
  110. data/app/javascript/components/ImageGrid/Placeholder.tsx +2 -2
  111. data/app/javascript/components/ImageGrid/useImageGrid.ts +26 -0
  112. data/app/javascript/components/ImageGrid.tsx +15 -0
  113. data/app/javascript/components/ImageUploader.tsx +35 -22
  114. data/app/javascript/components/LabelledField.tsx +34 -0
  115. data/app/javascript/components/Modal.tsx +2 -2
  116. data/app/javascript/components/PageForm/Block.tsx +81 -0
  117. data/app/javascript/components/PageForm/Content.tsx +54 -0
  118. data/app/javascript/components/PageForm/Dates.tsx +66 -0
  119. data/app/javascript/components/PageForm/Files.tsx +28 -0
  120. data/app/javascript/components/PageForm/Form.tsx +41 -0
  121. data/app/javascript/components/PageForm/Images.tsx +28 -0
  122. data/app/javascript/components/PageForm/LocaleLinks.tsx +36 -0
  123. data/app/javascript/components/PageForm/Metadata.tsx +67 -0
  124. data/app/javascript/components/PageForm/Options.tsx +180 -0
  125. data/app/javascript/components/PageForm/PageDescription.tsx +48 -0
  126. data/app/javascript/components/PageForm/PathSegment.tsx +65 -0
  127. data/app/javascript/components/PageForm/TabPanel.tsx +21 -0
  128. data/app/javascript/components/PageForm/Tabs.tsx +33 -0
  129. data/app/javascript/components/PageForm/UnconfiguredContent.tsx +42 -0
  130. data/app/javascript/components/PageForm/pageParams.ts +95 -0
  131. data/app/javascript/components/PageForm/preview.ts +23 -0
  132. data/app/javascript/components/PageForm/usePage.ts +169 -0
  133. data/app/javascript/components/PageForm/useTabs.ts +46 -0
  134. data/app/javascript/components/PageForm.tsx +163 -0
  135. data/app/javascript/components/PageImages.tsx +7 -9
  136. data/app/javascript/components/PageTree/Draggable.tsx +40 -39
  137. data/app/javascript/components/PageTree/Node.tsx +62 -56
  138. data/app/javascript/components/PageTree/PageName.tsx +28 -0
  139. data/app/javascript/components/PageTree.tsx +65 -53
  140. data/app/javascript/components/{RichTextArea.jsx → RichTextArea.tsx} +98 -79
  141. data/app/javascript/components/RichTextToolbarButton.tsx +4 -6
  142. data/app/javascript/components/TagEditor/AddTagForm.tsx +19 -12
  143. data/app/javascript/components/TagEditor/Editor.tsx +32 -0
  144. data/app/javascript/components/TagEditor/Tag.tsx +6 -4
  145. data/app/javascript/components/TagEditor/useTags.ts +58 -0
  146. data/app/javascript/components/TagEditor.tsx +8 -58
  147. data/app/javascript/components/Toast.tsx +3 -3
  148. data/app/javascript/components/drag/draggedOrder.ts +22 -14
  149. data/app/javascript/components/drag/useDragCollection.ts +35 -30
  150. data/app/javascript/components/drag/useDragUploader.ts +32 -21
  151. data/app/javascript/components/drag/useDraggable.ts +7 -6
  152. data/app/javascript/components/drag.ts +0 -1
  153. data/app/javascript/components.ts +1 -3
  154. data/app/javascript/features/RichText.tsx +2 -3
  155. data/app/javascript/features/contentTabs.ts +79 -0
  156. data/app/javascript/index.ts +5 -14
  157. data/app/javascript/lib/Tree.ts +31 -45
  158. data/app/javascript/lib/request.ts +11 -11
  159. data/app/javascript/stores/useToastStore.ts +1 -1
  160. data/app/javascript/types/Attachments.ts +29 -0
  161. data/app/javascript/types/Crop.ts +36 -0
  162. data/app/javascript/types/Drag.ts +34 -0
  163. data/app/javascript/types/Images.ts +47 -0
  164. data/app/javascript/types/PageEditor.ts +26 -0
  165. data/app/javascript/types/Pages.ts +75 -0
  166. data/app/javascript/types/Tags.ts +9 -0
  167. data/app/javascript/types/Template.ts +24 -0
  168. data/app/javascript/types/Trees.ts +19 -0
  169. data/app/javascript/types.ts +2 -25
  170. data/app/mailers/admin_mailer.rb +2 -2
  171. data/app/models/attachment.rb +1 -1
  172. data/app/models/concerns/pages_core/authenticable_user.rb +63 -0
  173. data/app/models/concerns/pages_core/emailable.rb +16 -0
  174. data/app/models/concerns/pages_core/page_model/templateable.rb +2 -16
  175. data/app/models/invite.rb +2 -6
  176. data/app/models/otp_secret.rb +101 -0
  177. data/app/models/page.rb +0 -3
  178. data/app/models/user.rb +2 -68
  179. data/app/policies/page_policy.rb +6 -2
  180. data/app/policies/user_policy.rb +4 -0
  181. data/app/resources/admin/page_resource.rb +95 -0
  182. data/app/resources/admin/page_tree_resource.rb +27 -0
  183. data/app/resources/admin/template_configuration_resource.rb +50 -0
  184. data/app/views/admin/account_recoveries/new.html.erb +22 -0
  185. data/app/views/admin/account_recoveries/show.html.erb +37 -0
  186. data/app/views/admin/invites/show.html.erb +1 -1
  187. data/app/views/admin/news/_sidebar.html.erb +2 -4
  188. data/app/views/admin/news/index.html.erb +0 -1
  189. data/app/views/admin/otp_secrets/create.html.erb +7 -0
  190. data/app/views/admin/otp_secrets/new.html.erb +60 -0
  191. data/app/views/admin/pages/_form.html.erb +10 -30
  192. data/app/views/admin/pages/_search_bar.html.erb +1 -1
  193. data/app/views/admin/pages/edit.html.erb +1 -57
  194. data/app/views/admin/pages/index.html.erb +1 -1
  195. data/app/views/admin/pages/new.html.erb +1 -44
  196. data/app/views/admin/recovery_codes/_codes.html.erb +14 -0
  197. data/app/views/admin/recovery_codes/create.html.erb +7 -0
  198. data/app/views/admin/recovery_codes/new.html.erb +11 -0
  199. data/app/views/admin/sessions/_otp_form.html.erb +13 -0
  200. data/app/views/admin/sessions/new.html.erb +31 -0
  201. data/app/views/admin/sessions/verify_otp.html.erb +19 -0
  202. data/app/views/admin/users/_access_control.html.erb +5 -1
  203. data/app/views/admin/users/_list.html.erb +12 -7
  204. data/app/views/admin/users/edit.html.erb +31 -1
  205. data/app/views/admin/users/new.html.erb +1 -1
  206. data/app/views/admin_mailer/account_recovery.text.erb +10 -0
  207. data/app/views/layouts/admin/_header.html.erb +3 -5
  208. data/app/views/layouts/admin/_page_header.html.erb +1 -2
  209. data/app/views/layouts/admin/_toast.html.erb +12 -0
  210. data/app/views/layouts/admin.html.erb +2 -2
  211. data/config/locales/en.yml +11 -7
  212. data/config/routes.rb +13 -12
  213. data/db/migrate/20240126160700_add_2fa_fields.rb +26 -0
  214. data/db/migrate/20240129201300_remove_password_reset_tokens.rb +13 -0
  215. data/db/migrate/20240131140700_change_email_to_citext.rb +18 -0
  216. data/db/migrate/20240201160700_remove_persistent_data.rb +7 -0
  217. data/db/migrate/20240508145300_remove_categories.rb +21 -0
  218. data/lib/pages_core/configuration/base.rb +2 -2
  219. data/lib/pages_core/templates/configuration.rb +1 -1
  220. data/lib/pages_core/templates/configuration_proxy.rb +2 -2
  221. data/lib/pages_core/templates/template_configuration.rb +11 -1
  222. data/lib/pages_core/templates.rb +6 -4
  223. data/lib/pages_core/version.rb +1 -1
  224. data/lib/pages_core.rb +6 -0
  225. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +6 -7
  226. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +17 -12
  227. data/lib/rails/generators/pages_core/rspec/rspec_generator.rb +0 -2
  228. data/lib/rails/generators/pages_core/rspec/templates/rails_helper.rb +3 -4
  229. metadata +143 -35
  230. data/app/assets/stylesheets/pages_core/admin/components/login.css +0 -33
  231. data/app/controllers/admin/categories_controller.rb +0 -56
  232. data/app/controllers/admin/password_resets_controller.rb +0 -85
  233. data/app/controllers/concerns/pages_core/admin/persistent_params.rb +0 -75
  234. data/app/controllers/sessions_controller.rb +0 -27
  235. data/app/helpers/pages_core/admin/page_blocks_helper.rb +0 -66
  236. data/app/helpers/pages_core/admin/page_json_helper.rb +0 -23
  237. data/app/javascript/components/DateRangeSelect.jsx +0 -225
  238. data/app/javascript/components/PageDates.jsx +0 -73
  239. data/app/javascript/components/PageFiles.jsx +0 -25
  240. data/app/javascript/components/PageTree/types.ts +0 -15
  241. data/app/javascript/components/drag/types.ts +0 -28
  242. data/app/javascript/controllers/EditPageController.ts +0 -22
  243. data/app/javascript/controllers/LoginController.ts +0 -32
  244. data/app/javascript/controllers/MainController.ts +0 -74
  245. data/app/javascript/controllers/PageOptionsController.js +0 -67
  246. data/app/models/category.rb +0 -22
  247. data/app/models/page_category.rb +0 -6
  248. data/app/models/password_reset_token.rb +0 -34
  249. data/app/views/admin/pages/_edit_content.html.erb +0 -19
  250. data/app/views/admin/pages/_edit_files.html.erb +0 -4
  251. data/app/views/admin/pages/_edit_images.html.erb +0 -4
  252. data/app/views/admin/pages/_edit_metadata.html.erb +0 -35
  253. data/app/views/admin/pages/_edit_options.html.erb +0 -91
  254. data/app/views/admin/password_resets/show.html.erb +0 -21
  255. data/app/views/admin/users/login.html.erb +0 -65
  256. data/app/views/admin_mailer/password_reset.text.erb +0 -11
  257. data/lib/rails/generators/pages_core/rspec/templates/mailer_macros.rb +0 -11
@@ -26,12 +26,15 @@
26
26
  */
27
27
 
28
28
  import React, { Component } from "react";
29
- import Tree, { TreeId, TreeIndex } from "../../lib/Tree";
30
- import { Attributes, PageNode } from "./types";
29
+
30
+ import Tree from "../../lib/Tree";
31
+ import * as Trees from "../../types/Trees";
32
+ import * as Pages from "../../types/Pages";
33
+
31
34
  import Node from "./Node";
32
35
 
33
36
  interface DragState {
34
- id: number | null;
37
+ id?: Trees.Id;
35
38
  x: number | null;
36
39
  y: number | null;
37
40
  w: number | null;
@@ -40,35 +43,35 @@ interface DragState {
40
43
  scrollLeft: number | null;
41
44
  }
42
45
 
43
- interface DraggableProps {
44
- addChild: (index: TreeIndex) => void;
46
+ interface Props {
47
+ addChild: (id: Trees.Id, attrs: Pages.TreeNode) => void;
45
48
  dir: string;
46
49
  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;
50
+ movedPage: (id: Trees.Id) => void;
51
+ paddingLeft?: number;
52
+ toggleCollapsed: (id: Trees.Id) => void;
53
+ tree: Tree<Pages.TreeNode>;
54
+ updatePage: (id: Trees.Id, attributes: Partial<Pages.TreeItem>) => void;
52
55
  updateTree: (Tree) => void;
53
56
  }
54
57
 
55
- interface DraggableState {
58
+ interface State {
56
59
  dragging: DragState;
57
60
  }
58
61
 
59
- export default class Draggable extends Component<
60
- DraggableProps,
61
- DraggableState
62
- > {
62
+ const defaultPadding = 20;
63
+
64
+ export default class Draggable extends Component<Props, State> {
63
65
  _dragListener: (evt: MouseEvent) => void;
64
66
  _dragEndListener: () => void;
67
+ _start: boolean;
65
68
  _startX: number;
66
69
  _startY: number;
67
70
  _offsetX: number;
68
71
  _offsetY: number;
69
72
  dragging: DragState;
70
73
 
71
- constructor(props: DraggableProps) {
74
+ constructor(props: Props) {
72
75
  super(props);
73
76
  this.state = {
74
77
  dragging: this.initDragging()
@@ -103,7 +106,8 @@ export default class Draggable extends Component<
103
106
  <Node
104
107
  tree={tree}
105
108
  index={draggingIndex}
106
- paddingLeft={this.props.paddingLeft}
109
+ locale={this.props.locale}
110
+ paddingLeft={this.props.paddingLeft || defaultPadding}
107
111
  />
108
112
  </div>
109
113
  );
@@ -127,9 +131,9 @@ export default class Draggable extends Component<
127
131
  tree={tree}
128
132
  index={root}
129
133
  key={root.id}
130
- paddingLeft={this.props.paddingLeft}
131
- addChild={(id) => this.addChild(id)}
132
- onDragStart={(id, dom, e) => this.dragStart(id, dom, e)}
134
+ paddingLeft={this.props.paddingLeft || defaultPadding}
135
+ addChild={(idx: Trees.Index<Pages.TreeNode>) => this.addChild(idx)}
136
+ onDragStart={this.dragStart}
133
137
  onCollapse={(nodeId) => this.toggleCollapse(nodeId)}
134
138
  updatePage={(idx, attrs) => this.updatePage(idx, attrs)}
135
139
  dragging={dragging && dragging.id}
@@ -141,21 +145,21 @@ export default class Draggable extends Component<
141
145
  }
142
146
  }
143
147
 
144
- addChild(parent: TreeIndex<PageNode>) {
145
- const newNode = {
146
- name: "",
148
+ addChild(parent: Trees.Index<Pages.TreeNode>) {
149
+ const newNode: Pages.TreeNode = {
150
+ blocks: { name: { [this.props.locale]: "" } },
147
151
  status: 0,
148
152
  editing: true,
149
153
  children: [],
150
154
  published_at: new Date(),
151
155
  pinned: false,
152
- locale: parent.node.locale,
153
- parent_page_id: parent.node.id
156
+ parent_page_id: parent.node.id,
157
+ collapsed: false
154
158
  };
155
159
  this.props.addChild(parent.id, newNode);
156
160
  }
157
161
 
158
- prevAddButtonCount(tree: Tree, index: TreeIndex) {
162
+ prevAddButtonCount(tree: Tree, index: Trees.Index) {
159
163
  let count = 0;
160
164
  const parentNodes = [];
161
165
  let pointer = tree.getIndex(index.parent);
@@ -171,7 +175,8 @@ export default class Draggable extends Component<
171
175
  if (
172
176
  parentNodes.indexOf(pointer) == -1 &&
173
177
  !pointer.node.collapsed &&
174
- pointer.node.children.filter((p) => p.status != 4).length > 0
178
+ pointer.node.children.filter((p: Pages.TreeNode) => p.status != 4)
179
+ .length > 0
175
180
  ) {
176
181
  count += 1;
177
182
  }
@@ -206,8 +211,8 @@ export default class Draggable extends Component<
206
211
 
207
212
  const tree = this.props.tree;
208
213
  const dragging = this.state.dragging;
209
- const paddingLeft = this.props.paddingLeft;
210
- let newIndex: TreeIndex = null;
214
+ const paddingLeft = this.props.paddingLeft || defaultPadding;
215
+ let newIndex: Trees.Index<Pages.TreeNode> = null;
211
216
  let index = tree.getIndex(dragging.id);
212
217
  const collapsed = index.node.collapsed;
213
218
 
@@ -239,7 +244,7 @@ export default class Draggable extends Component<
239
244
  if ("prev" in index) {
240
245
  const prev = tree.getIndex(index.prev);
241
246
 
242
- if (!prev.node.leaf && !prev.node.collapsed) {
247
+ if (!prev.node.collapsed) {
243
248
  newIndex = tree.move(index.id, index.prev, "append");
244
249
  }
245
250
  }
@@ -278,7 +283,7 @@ export default class Draggable extends Component<
278
283
  this.setState({ dragging: dragging });
279
284
  }
280
285
 
281
- dragStart(id: TreeId, dom: HTMLDivElement, e: MouseEvent) {
286
+ dragStart = (id: Trees.Id, dom: HTMLDivElement, e: React.MouseEvent) => {
282
287
  // Only drag on left click
283
288
  if (e.button !== 0) {
284
289
  return;
@@ -300,14 +305,14 @@ export default class Draggable extends Component<
300
305
  this._offsetY = e.clientY;
301
306
  this._start = true;
302
307
 
303
- this._dragListener = (e: Event) => {
308
+ this._dragListener = (e: MouseEvent) => {
304
309
  this.drag(e);
305
310
  };
306
311
  this._dragEndListener = () => this.dragEnd();
307
312
 
308
313
  window.addEventListener("mousemove", this._dragListener);
309
314
  window.addEventListener("mouseup", this._dragEndListener);
310
- }
315
+ };
311
316
 
312
317
  dragEnd() {
313
318
  if (!this._start) {
@@ -323,15 +328,11 @@ export default class Draggable extends Component<
323
328
  window.removeEventListener("mouseup", this._dragEndListener);
324
329
  }
325
330
 
326
- toggleCollapse(nodeId: TreeId) {
331
+ toggleCollapse(nodeId: Trees.Id) {
327
332
  this.props.toggleCollapsed(nodeId);
328
333
  }
329
334
 
330
- updatePage(index: TreeIndex, attributes: Attributes) {
335
+ updatePage(index: Trees.Index, attributes: Partial<Pages.TreeItem>) {
331
336
  this.props.updatePage(index.id, attributes);
332
337
  }
333
338
  }
334
-
335
- Draggable.defaultProps = {
336
- paddingLeft: 15
337
- };
@@ -25,39 +25,54 @@
25
25
  SOFTWARE.
26
26
  */
27
27
 
28
- import React, { createRef, ChangeEvent, Component, RefObject } from "react";
29
- import Tree, { TreeId, TreeIndex } from "../../lib/Tree";
30
- import { Attributes, PageNode } from "./types";
31
-
32
- interface NodeProps {
33
- addChild: (index: TreeIndex) => void;
34
- dir: string;
35
- dragging: number;
36
- index: TreeIndex<PageNode>;
37
- locale: string;
38
- onCollapse: (id: TreeId) => void;
39
- onDragStart: (id: number, element: HTMLDivElement, evt: Event) => void;
28
+ import React, {
29
+ createRef,
30
+ ChangeEvent,
31
+ Component,
32
+ CSSProperties,
33
+ FormEvent,
34
+ MouseEvent,
35
+ RefObject
36
+ } from "react";
37
+
38
+ import Tree from "../../lib/Tree";
39
+ import * as Trees from "../../types/Trees";
40
+ import * as Pages from "../../types/Pages";
41
+
42
+ import PageName from "./PageName";
43
+
44
+ interface Props {
45
+ index: Trees.Index<Pages.TreeNode>;
40
46
  paddingLeft: number;
41
- tree: Tree<PageNode>;
42
- updatePage: (index: TreeIndex, attributes: Attributes) => void;
47
+ tree: Tree<Pages.TreeNode>;
48
+ addChild?: (index: Trees.Index<Pages.TreeNode>) => void;
49
+ dir?: string;
50
+ dragging?: Trees.Id;
51
+ locale: string;
52
+ onCollapse?: (id: Trees.Id) => void;
53
+ onDragStart?: (id: number, element: HTMLDivElement, evt: MouseEvent) => void;
54
+ updatePage?: (
55
+ index: Trees.Index<Pages.TreeNode>,
56
+ attributes: Partial<Pages.TreeItem>
57
+ ) => void;
43
58
  }
44
59
 
45
- interface NodeState {
60
+ interface State {
46
61
  newName: string;
47
62
  }
48
63
 
49
64
  interface ButtonOptions {
50
65
  icon: string;
51
66
  className: string;
52
- onClick: (evt: Event) => void;
67
+ onClick: (evt: MouseEvent) => void;
53
68
  }
54
69
 
55
- export default class Node extends Component<NodeProps, NodeState> {
70
+ export default class Node extends Component<Props, State> {
56
71
  innerRef: RefObject<HTMLDivElement>;
57
72
 
58
- constructor(props: NodeProps) {
73
+ constructor(props: Props) {
59
74
  super(props);
60
- this.state = { newName: props.index.node.name };
75
+ this.state = { newName: this.pageName() };
61
76
  this.innerRef = createRef<HTMLDivElement>();
62
77
  }
63
78
 
@@ -136,7 +151,7 @@ export default class Node extends Component<NodeProps, NodeState> {
136
151
  (node.root || this.visibleChildren().length > 0)
137
152
  ) {
138
153
  return this.button("Add page here", {
139
- className: "add add-inline",
154
+ className: "add add-inline transparent",
140
155
  icon: "plus",
141
156
  onClick: handleClick
142
157
  });
@@ -160,7 +175,7 @@ export default class Node extends Component<NodeProps, NodeState> {
160
175
  const { index, tree, dragging, dir, locale } = this.props;
161
176
 
162
177
  if (index.children && index.children.length && !index.node.collapsed) {
163
- const childrenStyles = {};
178
+ const childrenStyles: CSSProperties = {};
164
179
  if (index.node.collapsed) {
165
180
  childrenStyles.display = "none";
166
181
  }
@@ -201,7 +216,7 @@ export default class Node extends Component<NodeProps, NodeState> {
201
216
  return null;
202
217
  }
203
218
 
204
- const handleCollapse = (e: Event) => {
219
+ const handleCollapse = (e: MouseEvent) => {
205
220
  e.stopPropagation();
206
221
  const nodeId = this.props.index.id;
207
222
  if (this.props.onCollapse) {
@@ -211,17 +226,17 @@ export default class Node extends Component<NodeProps, NodeState> {
211
226
 
212
227
  if (this.visibleChildren().length > 0) {
213
228
  const collapsed = index.node.collapsed;
214
- let classnames = "";
229
+ const classnames = ["collapse fa-solid fa-caret-right"];
215
230
 
216
231
  if (collapsed) {
217
- classnames = "collapse fa-solid fa-caret-right";
232
+ classnames.push("collapsed");
218
233
  } else {
219
- classnames = "collapse fa-solid fa-caret-down";
234
+ classnames.push("open");
220
235
  }
221
236
 
222
237
  return (
223
238
  <i
224
- className={classnames}
239
+ className={classnames.join(" ")}
225
240
  onMouseDown={function (e) {
226
241
  e.stopPropagation();
227
242
  }}
@@ -260,22 +275,16 @@ export default class Node extends Component<NodeProps, NodeState> {
260
275
  this.updatePage({ editing: true });
261
276
  }
262
277
 
263
- editUrl(page: PageNode) {
264
- return `/admin/${page.locale}/pages/${page.param}/edit`;
278
+ editUrl(page: Pages.TreeNode) {
279
+ return `/admin/${this.props.locale}/pages/${page.id}/edit`;
265
280
  }
266
281
 
267
- node(): PageNode {
282
+ node(): Pages.TreeNode {
268
283
  return this.props.index.node;
269
284
  }
270
285
 
271
286
  pageName() {
272
- const name = this.node().name || <i className="untitled">Untitled</i>;
273
-
274
- return (
275
- <span dir={this.props.dir} lang={this.props.locale}>
276
- {name}
277
- </span>
278
- );
287
+ return this.node().blocks.name[this.props.locale];
279
288
  }
280
289
 
281
290
  render() {
@@ -291,7 +300,7 @@ export default class Node extends Component<NodeProps, NodeState> {
291
300
  classnames = "node placeholder";
292
301
  }
293
302
 
294
- const handleMouseDown = (e: Event) => {
303
+ const handleMouseDown = (e: MouseEvent) => {
295
304
  if (this.permitted("edit") && !editing && props.onDragStart) {
296
305
  props.onDragStart(props.index.id, this.innerRef.current, e);
297
306
  }
@@ -323,24 +332,25 @@ export default class Node extends Component<NodeProps, NodeState> {
323
332
  this.setState({ newName: event.target.value });
324
333
  };
325
334
 
326
- const performEdit = (event: Event) => {
335
+ const performEdit = (event: FormEvent) => {
327
336
  event.preventDefault();
328
337
  this.updatePage({
329
- name: this.state.newName,
338
+ blocks: { name: { [locale]: this.state.newName } },
330
339
  editing: false
331
340
  });
332
341
  };
333
342
 
334
343
  const cancelEdit = () => {
335
- this.setState({ newName: this.node().name });
344
+ this.setState({ newName: this.pageName() });
336
345
  this.updatePage({ editing: false });
337
346
  };
338
347
 
339
348
  return (
340
349
  <div className="page edit">
341
350
  <i className="fa-regular fa-file icon"></i>
342
- <form onSubmit={performEdit}>
351
+ <form className="edit-name" onSubmit={performEdit}>
343
352
  <input
353
+ className="tight"
344
354
  type="text"
345
355
  value={this.state.newName}
346
356
  dir={dir}
@@ -348,7 +358,7 @@ export default class Node extends Component<NodeProps, NodeState> {
348
358
  autoFocus
349
359
  onChange={handleNameChange}
350
360
  />
351
- <button className="save" type="submit">
361
+ <button className="save primary" type="submit">
352
362
  <i className="fa-solid fa-cloud icon"></i>
353
363
  Save
354
364
  </button>
@@ -366,7 +376,6 @@ export default class Node extends Component<NodeProps, NodeState> {
366
376
  const index = this.props.index;
367
377
  const node = index.node;
368
378
 
369
- let pageName = <span className="name">{this.pageName()}</span>;
370
379
  let className = "page";
371
380
 
372
381
  let iconClass = "fa-regular fa-file icon";
@@ -375,24 +384,21 @@ export default class Node extends Component<NodeProps, NodeState> {
375
384
  className = `page status-${this.node().status}`;
376
385
  }
377
386
 
378
- if (node.id && node.locale && this.permitted("edit")) {
379
- pageName = (
380
- <a href={this.editUrl(node)} className="name">
381
- {this.pageName()}
382
- </a>
383
- );
384
- }
385
-
386
387
  if (node.news_page) {
387
- iconClass = "fa-regular fa-file-lines icon";
388
+ iconClass = "fa-regular fa-file-lines page-icon";
388
389
  } else if (node.pinned) {
389
- iconClass = "fa-regular fa-flag icon";
390
+ iconClass = "fa-regular fa-flag page-icon";
390
391
  }
391
392
 
392
393
  return (
393
394
  <div className={className}>
394
395
  <i className={iconClass}></i>
395
- {pageName}
396
+ <PageName
397
+ name={this.pageName()}
398
+ dir={this.props.dir}
399
+ locale={this.props.locale}
400
+ editUrl={node.id && this.permitted("edit") && this.editUrl(node)}
401
+ />
396
402
  {this.statusLabel()}
397
403
  {this.collapsedLabel()}
398
404
  {this.actions()}
@@ -419,13 +425,13 @@ export default class Node extends Component<NodeProps, NodeState> {
419
425
  }
420
426
  }
421
427
 
422
- updatePage(attributes: Attributes) {
428
+ updatePage(attributes: Partial<Pages.TreeItem>) {
423
429
  if (this.props.updatePage) {
424
430
  return this.props.updatePage(this.props.index, attributes);
425
431
  }
426
432
  }
427
433
 
428
- visibleChildren(): PageNode[] {
434
+ visibleChildren(): Pages.TreeNode[] {
429
435
  if (this.node().children) {
430
436
  return this.node().children.filter((p) => p.status != 4);
431
437
  } else {
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+
3
+ interface Props {
4
+ name: string;
5
+ locale: string;
6
+ dir?: string;
7
+ editUrl?: string;
8
+ }
9
+
10
+ export default function PageName(props: Props) {
11
+ const { name, locale, dir, editUrl } = props;
12
+
13
+ const span = (
14
+ <span dir={dir} lang={locale}>
15
+ {name || <i className="untitled">Untitled</i>}
16
+ </span>
17
+ );
18
+
19
+ if (editUrl) {
20
+ return (
21
+ <a href={editUrl} className="name">
22
+ {span}
23
+ </a>
24
+ );
25
+ } else {
26
+ return <span className="name">{span}</span>;
27
+ }
28
+ }