pages_core 3.12.0 → 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 (199) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/fonts/661557ef.ttf +0 -0
  4. data/app/assets/builds/fonts/a18fc2d2.woff2 +0 -0
  5. data/app/assets/builds/fonts/b2c7b78f.woff2 +0 -0
  6. data/app/assets/builds/fonts/ceddc204.ttf +0 -0
  7. data/app/assets/builds/pages_core/admin-dist.js +60 -14
  8. data/app/assets/builds/pages_core/admin-dist.js.map +7 -0
  9. data/app/assets/builds/pages_core/admin.css +9272 -0
  10. data/app/assets/images/pages/admin/angle-down-solid.svg +1 -0
  11. data/app/assets/images/pages/admin/icon.svg +1 -0
  12. data/app/assets/stylesheets/pages_core/admin/components/archive.css +6 -0
  13. data/app/assets/stylesheets/{pages/admin/components/attachments.scss → pages_core/admin/components/attachments.css} +35 -28
  14. data/app/assets/stylesheets/{pages/admin.scss → pages_core/admin/components/base.css} +125 -123
  15. data/app/assets/stylesheets/pages_core/admin/components/forms.css +223 -0
  16. data/app/assets/stylesheets/{pages/admin/components/header.scss → pages_core/admin/components/header.css} +76 -46
  17. data/app/assets/stylesheets/{pages/admin/components/image_editor.scss → pages_core/admin/components/image_editor.css} +42 -31
  18. data/app/assets/stylesheets/{pages/admin/components/image_grid.scss → pages_core/admin/components/image_grid.css} +76 -64
  19. data/app/assets/stylesheets/{pages/admin/components/image_uploader.scss → pages_core/admin/components/image_uploader.css} +12 -12
  20. data/app/assets/stylesheets/{pages/admin/components/layout.scss → pages_core/admin/components/layout.css} +13 -9
  21. data/app/assets/stylesheets/pages_core/admin/components/links.css +40 -0
  22. data/app/assets/stylesheets/pages_core/admin/components/list_table.css +66 -0
  23. data/app/assets/stylesheets/{pages/admin/components/login.scss → pages_core/admin/components/login.css} +6 -5
  24. data/app/assets/stylesheets/{pages/admin/components/modal.scss → pages_core/admin/components/modal.css} +10 -12
  25. data/app/assets/stylesheets/{pages/admin/components/page_tree.scss → pages_core/admin/components/page_tree.css} +54 -55
  26. data/app/assets/stylesheets/{pages/admin/components/pagination.scss → pages_core/admin/components/pagination.css} +17 -17
  27. data/app/assets/stylesheets/pages_core/admin/components/search.css +27 -0
  28. data/app/assets/stylesheets/{pages/admin/components/sidebar.scss → pages_core/admin/components/sidebar.css} +8 -7
  29. data/app/assets/stylesheets/{pages/admin/components/tag_editor.scss → pages_core/admin/components/tag_editor.css} +10 -15
  30. data/app/assets/stylesheets/{pages/admin/components/textarea.scss → pages_core/admin/components/textarea.css} +1 -1
  31. data/app/assets/stylesheets/{pages/admin/components/toast.scss → pages_core/admin/components/toast.css} +5 -3
  32. data/app/assets/stylesheets/{pages/admin/components/toolbar.scss → pages_core/admin/components/toolbar.css} +56 -29
  33. data/app/assets/stylesheets/{pages/admin/controllers/pages.scss → pages_core/admin/controllers/pages.css} +69 -52
  34. data/app/assets/stylesheets/pages_core/admin/controllers/users.css +3 -0
  35. data/app/assets/stylesheets/pages_core/admin/vars.css +34 -0
  36. data/app/assets/stylesheets/pages_core/admin.postcss.css +9 -0
  37. data/app/controllers/admin/pages_controller.rb +12 -11
  38. data/app/controllers/concerns/pages_core/rss_controller.rb +17 -1
  39. data/app/controllers/pages_core/admin_controller.rb +6 -0
  40. data/app/controllers/pages_core/frontend/pages_controller.rb +9 -5
  41. data/app/controllers/pages_core/sitemaps_controller.rb +3 -5
  42. data/app/formatters/pages_core/image_embedder.rb +5 -27
  43. data/app/helpers/admin/calendars_helper.rb +8 -0
  44. data/app/helpers/admin/news_helper.rb +13 -0
  45. data/app/helpers/admin/pages_helper.rb +32 -0
  46. data/app/helpers/pages_core/admin/admin_helper.rb +11 -54
  47. data/app/helpers/pages_core/admin/deprecated_admin_helper.rb +40 -0
  48. data/app/helpers/pages_core/images_helper.rb +37 -0
  49. data/app/javascript/admin-dist.ts +2 -0
  50. data/app/javascript/components/Attachments/{Attachment.jsx → Attachment.tsx} +44 -35
  51. data/app/javascript/components/Attachments/{AttachmentEditor.jsx → AttachmentEditor.tsx} +23 -23
  52. data/app/javascript/components/{EditableImage.jsx → EditableImage.tsx} +28 -25
  53. data/app/javascript/components/{FileUploadButton.jsx → FileUploadButton.tsx} +15 -16
  54. data/app/javascript/components/ImageCropper/FocalPoint.tsx +94 -0
  55. data/app/javascript/components/ImageCropper/{Image.jsx → Image.tsx} +13 -14
  56. data/app/javascript/components/ImageCropper/{Toolbar.jsx → Toolbar.tsx} +19 -15
  57. data/app/javascript/components/ImageCropper/{useCrop.js → useCrop.ts} +80 -37
  58. data/app/javascript/components/{ImageCropper.jsx → ImageCropper.tsx} +17 -15
  59. data/app/javascript/components/ImageEditor/{Form.jsx → Form.tsx} +24 -23
  60. data/app/javascript/components/{ImageEditor.jsx → ImageEditor.tsx} +17 -15
  61. data/app/javascript/components/ImageGrid/{DragElement.jsx → DragElement.tsx} +12 -10
  62. data/app/javascript/components/ImageGrid/{GridImage.jsx → GridImage.tsx} +40 -30
  63. data/app/javascript/components/ImageGrid/{Placeholder.jsx → Placeholder.tsx} +5 -6
  64. data/app/javascript/components/ImageGrid.jsx +3 -4
  65. data/app/javascript/components/{ImageUploader.jsx → ImageUploader.tsx} +46 -41
  66. data/app/javascript/components/Modal.tsx +48 -0
  67. data/app/javascript/components/PageImages.tsx +28 -0
  68. data/app/javascript/components/{PageTreeDraggable.jsx → PageTree/Draggable.tsx} +79 -57
  69. data/app/javascript/components/{PageTreeNode.jsx → PageTree/Node.tsx} +86 -77
  70. data/app/javascript/components/PageTree/types.ts +15 -0
  71. data/app/javascript/components/PageTree.tsx +206 -0
  72. data/app/javascript/components/RichTextToolbarButton.tsx +17 -0
  73. data/app/javascript/components/TagEditor/{AddTagForm.jsx → AddTagForm.tsx} +9 -10
  74. data/app/javascript/components/TagEditor/{Tag.jsx → Tag.tsx} +8 -9
  75. data/app/javascript/components/{TagEditor.jsx → TagEditor.tsx} +12 -13
  76. data/app/javascript/components/Toast.tsx +61 -0
  77. data/app/javascript/components/drag/{draggedOrder.js → draggedOrder.ts} +22 -12
  78. data/app/javascript/components/drag/types.ts +28 -0
  79. data/app/javascript/components/drag/{useDragCollection.js → useDragCollection.ts} +40 -22
  80. data/app/javascript/components/drag/{useDragUploader.js → useDragUploader.ts} +34 -25
  81. data/app/javascript/components/drag/useDraggable.ts +21 -0
  82. data/app/javascript/components/{drag.js → drag.ts} +1 -0
  83. data/app/javascript/controllers/{EditPageController.js → EditPageController.ts} +3 -1
  84. data/app/javascript/controllers/{LoginController.js → LoginController.ts} +7 -3
  85. data/app/javascript/controllers/{MainController.js → MainController.ts} +19 -14
  86. data/app/javascript/{index.js → index.ts} +8 -7
  87. data/app/javascript/lib/{Tree.js → Tree.ts} +106 -85
  88. data/app/javascript/lib/{copyToClipboard.js → copyToClipboard.ts} +1 -1
  89. data/app/javascript/lib/{readyHandler.js → readyHandler.ts} +4 -2
  90. data/app/javascript/lib/{request.js → request.ts} +11 -5
  91. data/app/javascript/stores/useModalStore.ts +15 -0
  92. data/app/javascript/stores/useToastStore.ts +26 -0
  93. data/app/javascript/stores.ts +2 -0
  94. data/app/javascript/types.ts +30 -0
  95. data/app/mailers/admin_mailer.rb +1 -0
  96. data/app/models/invite.rb +8 -0
  97. data/app/policies/page_policy.rb +4 -0
  98. data/app/views/admin/calendars/_sidebar.html.erb +50 -0
  99. data/app/views/admin/calendars/show.html.erb +15 -53
  100. data/app/views/admin/invites/new.html.erb +2 -8
  101. data/app/views/admin/invites/show.html.erb +2 -4
  102. data/app/views/admin/news/_sidebar.html.erb +51 -0
  103. data/app/views/admin/news/index.html.erb +21 -56
  104. data/app/views/admin/pages/_list_item.html.erb +4 -22
  105. data/app/views/admin/pages/_search_bar.html.erb +12 -0
  106. data/app/views/admin/pages/deleted.html.erb +10 -8
  107. data/app/views/admin/pages/edit.html.erb +20 -11
  108. data/app/views/admin/pages/index.html.erb +10 -8
  109. data/app/views/admin/pages/new.html.erb +10 -14
  110. data/app/views/admin/pages/search.html.erb +54 -0
  111. data/app/views/admin/password_resets/show.html.erb +3 -5
  112. data/app/views/admin/users/deactivated.html.erb +6 -7
  113. data/app/views/admin/users/edit.html.erb +7 -9
  114. data/app/views/admin/users/index.html.erb +3 -6
  115. data/app/views/admin/users/login.html.erb +4 -5
  116. data/app/views/admin/users/new.html.erb +2 -4
  117. data/app/views/admin/users/new_password.html.erb +4 -5
  118. data/app/views/admin/users/show.html.erb +11 -9
  119. data/app/views/errors/401.html.erb +2 -1
  120. data/app/views/errors/403.html.erb +2 -1
  121. data/app/views/errors/404.html.erb +1 -3
  122. data/app/views/errors/405.html.erb +2 -1
  123. data/app/views/errors/422.html.erb +2 -1
  124. data/app/views/errors/500.html.erb +2 -3
  125. data/app/views/feeds/pages.rss.builder +3 -9
  126. data/app/views/layouts/admin/_header.html.erb +1 -2
  127. data/app/views/layouts/admin/_page_header.html.erb +4 -4
  128. data/app/views/layouts/admin.html.erb +3 -3
  129. data/app/views/layouts/errors.html.erb +127 -4
  130. data/config/routes.rb +1 -0
  131. data/lib/pages_core/configuration/pages.rb +0 -1
  132. data/lib/pages_core/engine.rb +4 -3
  133. data/lib/pages_core.rb +0 -1
  134. data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +33 -17
  135. data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +0 -1
  136. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +40 -0
  137. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +68 -0
  138. data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +17 -0
  139. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.postcss.css +4 -0
  140. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +24 -0
  141. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/layout.css +21 -0
  142. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +5 -0
  143. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +5 -0
  144. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +18 -0
  145. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +6 -0
  146. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +65 -0
  147. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +131 -0
  148. data/lib/rails/generators/pages_core/install/templates/pages_initializer.rb +0 -3
  149. metadata +104 -255
  150. data/app/assets/images/pages/admin/icon.png +0 -0
  151. data/app/assets/images/pages/admin/image-editor-bg.png +0 -0
  152. data/app/assets/images/pages/admin/list-table-pin-blue.gif +0 -0
  153. data/app/assets/images/pages/admin/list-table-pin-disabled.gif +0 -0
  154. data/app/assets/images/pages/admin/list-table-pin-green.gif +0 -0
  155. data/app/assets/images/pages/admin/list-table-pin-red.gif +0 -0
  156. data/app/assets/images/pages/admin/list-table-pin-yellow.gif +0 -0
  157. data/app/assets/images/pages/admin/loading-modal.gif +0 -0
  158. data/app/assets/images/pages/feed-icon-14x14.png +0 -0
  159. data/app/assets/stylesheets/pages/admin/components/archive.scss +0 -6
  160. data/app/assets/stylesheets/pages/admin/components/buttons.scss +0 -23
  161. data/app/assets/stylesheets/pages/admin/components/forms.scss +0 -169
  162. data/app/assets/stylesheets/pages/admin/components/links.scss +0 -43
  163. data/app/assets/stylesheets/pages/admin/components/list_table.scss +0 -61
  164. data/app/assets/stylesheets/pages/admin/controllers/users.scss +0 -3
  165. data/app/assets/stylesheets/pages/admin/mixins/breakpoints.scss +0 -21
  166. data/app/assets/stylesheets/pages/admin/mixins/clearfix.scss +0 -7
  167. data/app/assets/stylesheets/pages/admin/mixins/gradients.scss +0 -7
  168. data/app/assets/stylesheets/pages/admin/vars.scss +0 -30
  169. data/app/assets/stylesheets/pages/errors.css +0 -128
  170. data/app/javascript/admin-dist.js +0 -2
  171. data/app/javascript/components/ImageCropper/FocalPoint.jsx +0 -93
  172. data/app/javascript/components/Modal.jsx +0 -59
  173. data/app/javascript/components/PageImages.jsx +0 -25
  174. data/app/javascript/components/PageTree.jsx +0 -196
  175. data/app/javascript/components/RichTextToolbarButton.jsx +0 -20
  176. data/app/javascript/components/Toast.jsx +0 -72
  177. data/app/javascript/components/drag/useDraggable.js +0 -17
  178. data/app/javascript/stores/ModalStore.jsx +0 -12
  179. data/app/javascript/stores/ToastStore.jsx +0 -14
  180. data/app/javascript/stores.js +0 -2
  181. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/GridOverlay.js +0 -66
  182. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/ResponsiveEmbeds.js +0 -72
  183. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.sass.scss +0 -15
  184. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.scss +0 -12
  185. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.scss +0 -26
  186. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/breakpoints.scss +0 -42
  187. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/clearfix.scss +0 -7
  188. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/fonts.scss +0 -32
  189. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid.scss +0 -168
  190. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid_overlay.scss +0 -44
  191. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.scss +0 -8
  192. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.scss +0 -90
  193. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/vendor/normalize.css +0 -349
  194. data/vendor/assets/stylesheets/ReactCrop.css +0 -167
  195. /data/app/assets/stylesheets/{pages/admin/components/tabs.scss → pages_core/admin/components/tabs.css} +0 -0
  196. /data/app/javascript/components/Attachments/{Placeholder.jsx → Placeholder.tsx} +0 -0
  197. /data/app/javascript/components/ImageGrid/{FilePlaceholder.jsx → FilePlaceholder.tsx} +0 -0
  198. /data/app/javascript/{components.js → components.ts} +0 -0
  199. /data/app/javascript/{hooks.js → hooks.ts} +0 -0
@@ -20,7 +20,10 @@ module PagesCore
20
20
  def index
21
21
  respond_to do |format|
22
22
  format.html { render_published_page(root_pages.try(&:first)) }
23
- format.rss { render_rss(all_feed_items) }
23
+ format.rss do
24
+ render_rss(all_feed_items.paginate(per_page: per_page_rss_param,
25
+ page: page_param))
26
+ end
24
27
  end
25
28
  end
26
29
 
@@ -28,10 +31,7 @@ module PagesCore
28
31
  respond_to do |format|
29
32
  format.html { render_published_page(@page) }
30
33
  format.json { render json: PageResource.new(@page) }
31
- format.rss do
32
- render_rss(@page.pages.limit(20).includes(:image, :author),
33
- title: @page.name)
34
- end
34
+ format.rss { render_page_rss(@page, page_param) }
35
35
  end
36
36
  end
37
37
 
@@ -64,6 +64,10 @@ module PagesCore
64
64
  super
65
65
  end
66
66
 
67
+ def page_param
68
+ params[:page].is_a?(String) ? params[:page] : 1
69
+ end
70
+
67
71
  def page_template(page)
68
72
  if PagesCore::Templates.names.include?(page.template)
69
73
  page.template
@@ -20,10 +20,8 @@ module PagesCore
20
20
  end
21
21
 
22
22
  def format_record(record)
23
- {
24
- loc: record_url(record),
25
- lastmod: format_time(record.updated_at)
26
- }
23
+ { loc: record_url(record),
24
+ lastmod: format_time(record.updated_at) }
27
25
  end
28
26
 
29
27
  def formatted_entries
@@ -46,7 +44,7 @@ module PagesCore
46
44
  ([Page.root.try(:localize, I18n.default_locale)] +
47
45
  locales.flat_map do |locale|
48
46
  Page.published.localized(locale).includes(:parent)
49
- end).compact.uniq
47
+ end).compact
50
48
  end
51
49
 
52
50
  def page_record_url(record)
@@ -21,12 +21,10 @@ module PagesCore
21
21
  end
22
22
 
23
23
  def embed_image(id, size:, class_name:, link:)
24
- image = Image.find(id).localize(I18n.locale)
25
- class_name = ["image", image_class_name(image), class_name].compact
26
- image_tag = dynamic_image_tag(image,
27
- size: size, crop: false, upscale: false)
28
- tag.figure((link ? link_to(image_tag, link) : image_tag) +
29
- image_caption(image), class: class_name)
24
+ image_figure(
25
+ Image.find(id).localize(I18n.locale),
26
+ size: size, class_name: class_name, link: link
27
+ )
30
28
  rescue ActiveRecord::RecordNotFound
31
29
  nil
32
30
  end
@@ -35,30 +33,10 @@ module PagesCore
35
33
  if str =~ /size="(\d*x\d*)"/
36
34
  Regexp.last_match(1)
37
35
  else
38
- "2000x2000"
36
+ default_image_size
39
37
  end
40
38
  end
41
39
 
42
- def image_caption(image)
43
- return unless image.caption?
44
-
45
- tag.figcaption(image.caption)
46
- end
47
-
48
- def image_class_name(image)
49
- if image.size.x == image.size.y
50
- "square"
51
- elsif image.size.x > image.size.y
52
- "landscape"
53
- else
54
- "portrait"
55
- end
56
- end
57
-
58
- def link_to(content, href)
59
- tag.a(content, href: href)
60
- end
61
-
62
40
  def parse_image(str)
63
41
  id = str.match(image_expression)[1]
64
42
  options = str.match(image_expression)[2]
@@ -8,6 +8,14 @@ module Admin
8
8
  ).in_locale(locale)
9
9
  end
10
10
 
11
+ def calendar_page_options(locale)
12
+ options_for_select(
13
+ calendar_pages(locale).map do |p|
14
+ [page_name(p, include_parents: true).gsub("»", "»"), p.id]
15
+ end
16
+ )
17
+ end
18
+
11
19
  def calendar_years_with_count
12
20
  calendar_counts.each_with_object({}) do |entry, obj|
13
21
  obj[entry[:year]] ||= 0
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ module NewsHelper
5
+ def news_page_options(news_pages)
6
+ options_for_select(
7
+ news_pages.map do |p|
8
+ [news_section_name(p, news_pages).gsub("»", "»"), p.id]
9
+ end
10
+ )
11
+ end
12
+ end
13
+ end
@@ -4,6 +4,15 @@ module Admin
4
4
  module PagesHelper
5
5
  include PagesCore::Admin::PageBlocksHelper
6
6
 
7
+ def autopublish_notice(page)
8
+ return unless page.autopublish?
9
+
10
+ tag.div(class: "autopublish-notice") do
11
+ safe_join(["This page will be published",
12
+ tag.b(publish_time(page.published_at))], " ")
13
+ end
14
+ end
15
+
7
16
  def available_templates_for_select
8
17
  PagesCore::Templates.names.collect do |template|
9
18
  if template == "index"
@@ -30,6 +39,14 @@ module Admin
30
39
  ([page.author] + User.activated).uniq
31
40
  end
32
41
 
42
+ def page_list_row(page, &block)
43
+ classes = [page.status_label.downcase]
44
+ classes << "autopublish" if page.autopublish?
45
+ classes << "pinned" if page.pinned?
46
+
47
+ tag.tr(capture(&block), class: classes.join(" "))
48
+ end
49
+
33
50
  def page_name(page, options = {})
34
51
  page_names = if options[:include_parents]
35
52
  page.self_and_ancestors.reverse
@@ -42,6 +59,21 @@ module Admin
42
59
  )
43
60
  end
44
61
 
62
+ def page_published_status(page)
63
+ return page_published_date(page) if page.published?
64
+ return tag.em("Not published") if page.status_label == "Published"
65
+
66
+ tag.em(page.status_label)
67
+ end
68
+
69
+ def page_published_date(page)
70
+ if page.published_at.year == Time.zone.now.year
71
+ l(page.published_at, format: :pages_date)
72
+ else
73
+ l(page.published_at, format: :pages_full)
74
+ end
75
+ end
76
+
45
77
  def publish_time(time)
46
78
  if time.year != Time.zone.now.year
47
79
  time.strftime("on %b %d %Y at %H:%M")
@@ -4,6 +4,7 @@ module PagesCore
4
4
  module Admin
5
5
  module AdminHelper
6
6
  include PagesCore::Admin::ContentTabsHelper
7
+ include PagesCore::Admin::DeprecatedAdminHelper
7
8
  include PagesCore::Admin::DateRangeHelper
8
9
  include PagesCore::Admin::ImageUploadsHelper
9
10
  include PagesCore::Admin::LocalesHelper
@@ -11,20 +12,6 @@ module PagesCore
11
12
  include PagesCore::Admin::LabelledFieldHelper
12
13
  include PagesCore::Admin::TagEditorHelper
13
14
 
14
- attr_writer :page_title, :page_description, :page_description_class,
15
- :page_description_links
16
-
17
- def add_body_class(class_name)
18
- @body_classes ||= []
19
- @body_classes << class_name
20
- end
21
-
22
- def body_classes
23
- classes = @body_classes || []
24
- classes << "with_notice" if flash[:notice]
25
- classes
26
- end
27
-
28
15
  def rich_text_area_tag(name, content = nil, options = {})
29
16
  react_component("RichTextArea",
30
17
  options.merge(id: sanitize_to_id(name),
@@ -32,50 +19,20 @@ module PagesCore
32
19
  value: content))
33
20
  end
34
21
 
35
- def link_separator
36
- safe_join [" ", tag.span("|", class: "separator"), " "]
37
- end
38
-
39
- def deprecate_page_description_args(string = nil, class_name = nil)
40
- if class_name
41
- ActiveSupport::Deprecation.warn("Setting class through " \
42
- "page_description is deprecated, " \
43
- "use page_description_class=")
44
- end
45
- return unless string
22
+ def locale_links(&block)
23
+ return unless PagesCore.config.localizations?
46
24
 
47
- ActiveSupport::Deprecation.warn("Setting description with " \
48
- "page_description is deprecated, " \
49
- "use page_description=")
50
- end
51
-
52
- def page_description(string = nil, class_name = nil)
53
- deprecate_page_description_args(string, class_name)
54
- @page_description_class = class_name if class_name
55
- if string
56
- @page_description = string
57
- else
58
- @page_description
59
- end
60
- end
61
-
62
- def page_description_links(links = nil)
63
- return @page_description_links unless links
64
-
65
- ActiveSupport::Deprecation.warn(
66
- "Setting page description_links with page_description_links " \
67
- "is deprecated, use page_description_links="
25
+ safe_join(
26
+ PagesCore.config.locales.map do |locale, name|
27
+ link_to(name, block.call(locale),
28
+ class: ("current" if locale == params[:locale].to_sym))
29
+ end
68
30
  )
69
- @page_description_links = links
70
31
  end
71
32
 
72
- def page_title(title = nil)
73
- return @page_title unless title
74
-
75
- ActiveSupport::Deprecation.warn(
76
- "Setting page title with page_title is deprecated, use page_title="
77
- )
78
- @page_title = title
33
+ def month_name(month)
34
+ %w[January February March April May June July August September October
35
+ November December][month - 1]
79
36
  end
80
37
  end
81
38
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PagesCore
4
+ module Admin
5
+ module DeprecatedAdminHelper
6
+ def link_separator
7
+ ActiveSupport::Deprecation.warn("link_separator is deprecated")
8
+
9
+ safe_join [" ", tag.span("|", class: "separator"), " "]
10
+ end
11
+
12
+ def page_description=(description)
13
+ ActiveSupport::Deprecation.warn(content_helper_deprecation)
14
+ content_for(:page_description, description.html_safe)
15
+ end
16
+ alias page_description page_description=
17
+
18
+ def page_description_links=(links)
19
+ ActiveSupport::Deprecation.warn(content_helper_deprecation)
20
+ content_for(:page_description_links, links.html_safe)
21
+ end
22
+ alias page_description_links page_description_links=
23
+
24
+ def page_title=(title)
25
+ ActiveSupport::Deprecation.warn(content_helper_deprecation)
26
+ content_for(:page_title, title)
27
+ end
28
+ alias page_title page_title=
29
+
30
+ private
31
+
32
+ def content_helper_deprecation
33
+ name = caller_locations(1, 1)[0].label
34
+ replacement = name.gsub(/=$/, "")
35
+
36
+ "The #{name} helper is deprecated, use content_for(:#{replacement})"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -11,6 +11,25 @@ module PagesCore
11
11
  )
12
12
  end
13
13
 
14
+ def image_caption(image, caption: nil)
15
+ return if caption == false
16
+
17
+ caption = image.caption unless caption.is_a?(String)
18
+ return if caption.blank?
19
+
20
+ tag.figcaption(caption)
21
+ end
22
+
23
+ def image_figure(image, size: nil, class_name: nil, link: nil, caption: nil)
24
+ class_name = ["image", image_class_name(image), class_name].compact
25
+ size ||= default_image_size
26
+ image_tag = dynamic_image_tag(image,
27
+ size: size, crop: false, upscale: false)
28
+ tag.figure((link ? image_link_to(image_tag, link) : image_tag) +
29
+ image_caption(image, caption: caption),
30
+ class: class_name)
31
+ end
32
+
14
33
  def original_dynamic_image_tag(record_or_array, options = {})
15
34
  super(
16
35
  record_or_array,
@@ -27,11 +46,29 @@ module PagesCore
27
46
 
28
47
  private
29
48
 
49
+ def default_image_size
50
+ "2000x2000"
51
+ end
52
+
30
53
  def extract_alt_text(record_or_array)
31
54
  record = extract_dynamic_image_record(record_or_array)
32
55
  return {} unless record.alternative?
33
56
 
34
57
  { alt: record.alternative }
35
58
  end
59
+
60
+ def image_class_name(image)
61
+ if image.size.x == image.size.y
62
+ "square"
63
+ elsif image.size.x > image.size.y
64
+ "landscape"
65
+ else
66
+ "portrait"
67
+ end
68
+ end
69
+
70
+ def image_link_to(content, href)
71
+ tag.a(content, href: href)
72
+ end
36
73
  end
37
74
  end
@@ -0,0 +1,2 @@
1
+ import startPages from "./index";
2
+ startPages();
@@ -1,28 +1,48 @@
1
1
  import React from "react";
2
- import PropTypes from "prop-types";
3
2
  import copyToClipboard from "../../lib/copyToClipboard";
4
3
  import AttachmentEditor from "./AttachmentEditor";
5
- import ModalStore from "../../stores/ModalStore";
6
- import ToastStore from "../../stores/ToastStore";
4
+ import useModalStore from "../../stores/useModalStore";
5
+ import useToastStore from "../../stores/useToastStore";
6
+ import { AttachmentResource, Locale } from "../../types";
7
7
 
8
- import { useDraggable } from "../drag";
8
+ import { useDraggable, Draggable } from "../drag";
9
9
 
10
- export default function Attachment(props) {
10
+ interface Record {
11
+ id: number | null,
12
+ attachment: AttachmentResource,
13
+ uploading: boolean
14
+ }
15
+
16
+ interface AttachmentProps {
17
+ attributeName: string,
18
+ placeholder: boolean,
19
+ draggable: { record: Record },
20
+ locale: string,
21
+ locales: { [index: string]: Locale },
22
+ deleteRecord: () => void,
23
+ showEmbed: boolean,
24
+ position: number,
25
+ onUpdate: (localizations: Record<string, Record<string, string>>) => void,
26
+ startDrag: (evt: Event, draggable: Draggable) => void
27
+ }
28
+
29
+ export default function Attachment(props: AttachmentProps) {
11
30
  const { attributeName, draggable, locales, locale } = props;
12
31
  const { record } = draggable;
13
32
  const { attachment, uploading } = record;
14
33
 
34
+ const openModal = useModalStore((state) => state.open);
35
+ const notice = useToastStore((state) => state.notice);
36
+
15
37
  const listeners = useDraggable(draggable, props.startDrag);
16
38
 
17
- const copyEmbed = (evt) => {
39
+ const copyEmbed = (evt: Event) => {
18
40
  evt.preventDefault();
19
41
  copyToClipboard(`[attachment:${attachment.id}]`);
20
- ToastStore.dispatch({
21
- type: "NOTICE", message: "Embed code copied to clipboard"
22
- });
42
+ notice("Embed code copied to clipboard");
23
43
  };
24
44
 
25
- const deleteRecord = (evt) => {
45
+ const deleteRecord = (evt: Event) => {
26
46
  evt.preventDefault();
27
47
  if (props.deleteRecord) {
28
48
  props.deleteRecord();
@@ -43,15 +63,15 @@ export default function Attachment(props) {
43
63
  return null;
44
64
  };
45
65
 
46
- const editAttachment = (evt) => {
66
+ const editAttachment = (evt: Event) => {
47
67
  evt.preventDefault();
48
- ModalStore.dispatch({
49
- type: "OPEN",
50
- payload: <AttachmentEditor attachment={attachment}
51
- locale={locale}
52
- locales={locales}
53
- onUpdate={props.onUpdate} />
54
- });
68
+ openModal(
69
+ <AttachmentEditor
70
+ attachment={attachment}
71
+ locale={locale}
72
+ locales={locales}
73
+ onUpdate={props.onUpdate} />
74
+ );
55
75
  };
56
76
 
57
77
  const classes = ["attachment"];
@@ -62,9 +82,12 @@ export default function Attachment(props) {
62
82
  classes.push("uploading");
63
83
  }
64
84
 
65
- const icon = uploading ? "cloud-upload" : "paperclip";
85
+ const icon = uploading ? "cloud-arrow-up" : "paperclip";
66
86
 
67
- const localeDir = (locales && locales[locale] && locales[locale].dir) || "ltr";
87
+ let localeDir = "ltr";
88
+ if (locale in locales && locales[locale].dir) {
89
+ localeDir = locales[locale].dir;
90
+ }
68
91
 
69
92
  return (
70
93
  <div className={classes.join(" ")}
@@ -95,7 +118,7 @@ export default function Attachment(props) {
95
118
  {attachment &&
96
119
  <div className="attachment-info">
97
120
  <h3>
98
- <i className={`fa fa-${icon} icon`} />
121
+ <i className={`fa-solid fa-${icon} icon`} />
99
122
  {name() || <em>Untitled</em>}<br />
100
123
  </h3>
101
124
  {!uploading &&
@@ -108,17 +131,3 @@ export default function Attachment(props) {
108
131
  </div>
109
132
  );
110
133
  }
111
-
112
- Attachment.propTypes = {
113
- locale: PropTypes.string,
114
- locales: PropTypes.object,
115
- draggable: PropTypes.object,
116
- deleteRecord: PropTypes.func,
117
- startDrag: PropTypes.func,
118
- showEmbed: PropTypes.bool,
119
- onUpdate: PropTypes.func,
120
- attributeName: PropTypes.string,
121
- placeholder: PropTypes.bool,
122
- position: PropTypes.number,
123
- ref: PropTypes.object
124
- };
@@ -1,11 +1,18 @@
1
- import React, { useState } from "react";
2
- import PropTypes from "prop-types";
1
+ import React, { ChangeEvent, useState } from "react";
3
2
  import copyToClipboard, { copySupported } from "../../lib/copyToClipboard";
4
- import ModalStore from "../../stores/ModalStore";
5
- import ToastStore from "../../stores/ToastStore";
3
+ import useModalStore from "../../stores/useModalStore";
4
+ import useToastStore from "../../stores/useToastStore";
5
+ import { AttachmentResource, Locale } from "../../types";
6
6
  import { putJson } from "../../lib/request";
7
7
 
8
- export default function AttachmentEditor(props) {
8
+ interface AttachmentEditorProps {
9
+ attachment: AttachmentResource,
10
+ locale: string,
11
+ locales: { [index: string]: Locale },
12
+ onUpdate: (localizations: Record<string, Record<string, string>>) => void
13
+ }
14
+
15
+ export default function AttachmentEditor(props: AttachmentEditorProps) {
9
16
  const { attachment, locales } = props;
10
17
 
11
18
  const [locale, setLocale] = useState(props.locale);
@@ -14,7 +21,10 @@ export default function AttachmentEditor(props) {
14
21
  description: attachment.description || {},
15
22
  });
16
23
 
17
- const updateLocalization = (name) => (evt) => {
24
+ const notice = useToastStore((state) => state.notice);
25
+ const closeModal = useModalStore((state) => state.close);
26
+
27
+ const updateLocalization = (name: "name" | "description") => (evt: ChangeEvent<HTMLInputElement>) => {
18
28
  setLocalizations({
19
29
  ...localizations,
20
30
  [name]: { ...localizations[name],
@@ -22,27 +32,24 @@ export default function AttachmentEditor(props) {
22
32
  });
23
33
  };
24
34
 
25
- const copyEmbedCode = (evt) => {
35
+ const copyEmbedCode = (evt: Event) => {
26
36
  evt.preventDefault();
27
37
  copyToClipboard(`[attachment:${attachment.id}]`);
28
- ToastStore.dispatch({
29
- type: "NOTICE", message: "Embed code copied to clipboard"
30
- });
38
+ notice("Embed code copied to clipboard");
31
39
  };
32
40
 
33
- const save = (evt) => {
41
+ const save = (evt: Event) => {
34
42
  evt.preventDefault();
35
43
  evt.stopPropagation();
36
44
 
37
- let data = { ...localizations };
45
+ const data = { ...localizations };
38
46
 
39
- putJson(`/admin/attachments/${attachment.id}`,
40
- { attachment: data });
47
+ void putJson(`/admin/attachments/${attachment.id}`, { attachment: data });
41
48
 
42
49
  if (props.onUpdate) {
43
50
  props.onUpdate(data);
44
51
  }
45
- ModalStore.dispatch({ type: "CLOSE" });
52
+ closeModal();
46
53
  };
47
54
 
48
55
  const inputDir = (locales && locales[locale] && locales[locale].dir) || "ltr";
@@ -106,7 +113,7 @@ export default function AttachmentEditor(props) {
106
113
  <button onClick={save}>
107
114
  Save
108
115
  </button>
109
- <button onClick={() => ModalStore.dispatch({ type: "CLOSE" })}>
116
+ <button onClick={closeModal}>
110
117
  Cancel
111
118
  </button>
112
119
  </div>
@@ -114,10 +121,3 @@ export default function AttachmentEditor(props) {
114
121
  </div>
115
122
  );
116
123
  }
117
-
118
- AttachmentEditor.propTypes = {
119
- attachment: PropTypes.object,
120
- locale: PropTypes.string,
121
- locales: PropTypes.object,
122
- onUpdate: PropTypes.func
123
- };
@@ -1,20 +1,33 @@
1
1
  import React, { useState } from "react";
2
- import PropTypes from "prop-types";
3
2
  import ImageEditor from "./ImageEditor";
4
- import ModalStore from "../stores/ModalStore";
3
+ import useModalStore from "../stores/useModalStore";
5
4
 
6
- export default function EditableImage(props) {
5
+ import { Locale, ImageResource } from "../types";
6
+
7
+ interface EditableImageProps {
8
+ image: ImageResource,
9
+ src: string,
10
+ caption: boolean,
11
+ locale: string,
12
+ locales: Record<string, Locale>,
13
+ width: number,
14
+ onUpdate?: (newImage: ImageResource, src: string) => void
15
+ }
16
+
17
+ export default function EditableImage(props: EditableImageProps) {
7
18
  const [image, setImage] = useState(props.image);
8
19
  const [src, setSrc] = useState(props.src);
9
20
 
21
+ const openModal = useModalStore((state) => state.open);
22
+
10
23
  const height = () => {
11
24
  const width = image.crop_width || image.real_width;
12
25
  const height = image.crop_height || image.real_height;
13
26
  return Math.round((height / width) * props.width);
14
27
  };
15
28
 
16
- const updateImage = (updatedImage, src) => {
17
- let newImage = { ...image, ...updatedImage };
29
+ const updateImage = (updatedImage: ImageResource, src: string) => {
30
+ const newImage = { ...image, ...updatedImage };
18
31
  setSrc(src);
19
32
  setImage(newImage);
20
33
  if (props.onUpdate) {
@@ -22,16 +35,16 @@ export default function EditableImage(props) {
22
35
  }
23
36
  };
24
37
 
25
- const handleClick = (evt) => {
38
+ const handleClick = (evt: Event) => {
26
39
  evt.preventDefault();
27
- ModalStore.dispatch({
28
- type: "OPEN",
29
- payload: <ImageEditor image={image}
30
- caption={props.caption}
31
- locale={props.locale}
32
- locales={props.locales}
33
- onUpdate={updateImage} />
34
- });
40
+ openModal(
41
+ <ImageEditor
42
+ image={image}
43
+ caption={props.caption}
44
+ locale={props.locale}
45
+ locales={props.locales}
46
+ onUpdate={updateImage} />
47
+ );
35
48
  };
36
49
 
37
50
  const altWarning = !image.alternative[props.locale];
@@ -40,7 +53,7 @@ export default function EditableImage(props) {
40
53
  <div className="editable-image">
41
54
  {altWarning &&
42
55
  <span className="alt-warning" title="Alternative text is missing">
43
- <i className="fa fa-exclamation-triangle icon" />
56
+ <i className="fa-solid fa-triangle-exclamation icon" />
44
57
  </span>}
45
58
  <img src={src}
46
59
  width={props.width}
@@ -49,13 +62,3 @@ export default function EditableImage(props) {
49
62
  </div>
50
63
  );
51
64
  }
52
-
53
- EditableImage.propTypes = {
54
- image: PropTypes.object,
55
- src: PropTypes.string,
56
- caption: PropTypes.bool,
57
- locale: PropTypes.string,
58
- locales: PropTypes.object,
59
- width: PropTypes.number,
60
- onUpdate: PropTypes.func
61
- };