pages_core 3.12.1 → 3.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/assets/builds/pages_core/admin-dist.js +59 -14
- data/app/assets/builds/pages_core/admin-dist.js.map +7 -0
- data/app/assets/builds/pages_core/admin.css +39 -0
- data/app/assets/stylesheets/pages_core/admin/components/search.css +27 -0
- data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +6 -0
- data/app/controllers/admin/pages_controller.rb +12 -11
- data/app/controllers/concerns/pages_core/rss_controller.rb +17 -1
- data/app/controllers/pages_core/admin_controller.rb +6 -0
- data/app/controllers/pages_core/frontend/pages_controller.rb +9 -5
- data/app/controllers/pages_core/sitemaps_controller.rb +3 -5
- data/app/helpers/admin/pages_helper.rb +32 -0
- data/app/javascript/admin-dist.ts +2 -0
- data/app/javascript/components/Attachments/{Attachment.jsx → Attachment.tsx} +42 -33
- data/app/javascript/components/Attachments/{AttachmentEditor.jsx → AttachmentEditor.tsx} +23 -23
- data/app/javascript/components/{EditableImage.jsx → EditableImage.tsx} +27 -24
- data/app/javascript/components/{FileUploadButton.jsx → FileUploadButton.tsx} +15 -16
- data/app/javascript/components/ImageCropper/FocalPoint.tsx +94 -0
- data/app/javascript/components/ImageCropper/{Image.jsx → Image.tsx} +13 -14
- data/app/javascript/components/ImageCropper/{Toolbar.jsx → Toolbar.tsx} +16 -12
- data/app/javascript/components/ImageCropper/{useCrop.js → useCrop.ts} +80 -37
- data/app/javascript/components/{ImageCropper.jsx → ImageCropper.tsx} +17 -15
- data/app/javascript/components/ImageEditor/{Form.jsx → Form.tsx} +24 -23
- data/app/javascript/components/{ImageEditor.jsx → ImageEditor.tsx} +17 -15
- data/app/javascript/components/ImageGrid/{DragElement.jsx → DragElement.tsx} +12 -10
- data/app/javascript/components/ImageGrid/{GridImage.jsx → GridImage.tsx} +40 -30
- data/app/javascript/components/ImageGrid/{Placeholder.jsx → Placeholder.tsx} +5 -6
- data/app/javascript/components/ImageGrid.jsx +3 -4
- data/app/javascript/components/{ImageUploader.jsx → ImageUploader.tsx} +46 -41
- data/app/javascript/components/Modal.tsx +48 -0
- data/app/javascript/components/PageImages.tsx +28 -0
- data/app/javascript/components/{PageTreeDraggable.jsx → PageTree/Draggable.tsx} +79 -57
- data/app/javascript/components/{PageTreeNode.jsx → PageTree/Node.tsx} +79 -70
- data/app/javascript/components/PageTree/types.ts +15 -0
- data/app/javascript/components/PageTree.tsx +206 -0
- data/app/javascript/components/RichTextToolbarButton.tsx +17 -0
- data/app/javascript/components/TagEditor/{AddTagForm.jsx → AddTagForm.tsx} +9 -10
- data/app/javascript/components/TagEditor/{Tag.jsx → Tag.tsx} +8 -9
- data/app/javascript/components/{TagEditor.jsx → TagEditor.tsx} +12 -13
- data/app/javascript/components/Toast.tsx +61 -0
- data/app/javascript/components/drag/{draggedOrder.js → draggedOrder.ts} +22 -12
- data/app/javascript/components/drag/types.ts +28 -0
- data/app/javascript/components/drag/{useDragCollection.js → useDragCollection.ts} +40 -22
- data/app/javascript/components/drag/{useDragUploader.js → useDragUploader.ts} +34 -25
- data/app/javascript/components/drag/useDraggable.ts +21 -0
- data/app/javascript/components/{drag.js → drag.ts} +1 -0
- data/app/javascript/controllers/{EditPageController.js → EditPageController.ts} +3 -1
- data/app/javascript/controllers/{LoginController.js → LoginController.ts} +7 -3
- data/app/javascript/controllers/{MainController.js → MainController.ts} +19 -14
- data/app/javascript/{index.js → index.ts} +8 -7
- data/app/javascript/lib/{Tree.js → Tree.ts} +106 -85
- data/app/javascript/lib/{copyToClipboard.js → copyToClipboard.ts} +1 -1
- data/app/javascript/lib/{readyHandler.js → readyHandler.ts} +4 -2
- data/app/javascript/lib/{request.js → request.ts} +11 -5
- data/app/javascript/stores/useModalStore.ts +15 -0
- data/app/javascript/stores/useToastStore.ts +26 -0
- data/app/javascript/stores.ts +2 -0
- data/app/javascript/types.ts +30 -0
- data/app/policies/page_policy.rb +4 -0
- data/app/views/admin/calendars/_sidebar.html.erb +3 -0
- data/app/views/admin/news/_sidebar.html.erb +3 -0
- data/app/views/admin/pages/_list_item.html.erb +4 -22
- data/app/views/admin/pages/_search_bar.html.erb +12 -0
- data/app/views/admin/pages/index.html.erb +3 -0
- data/app/views/admin/pages/search.html.erb +54 -0
- data/app/views/feeds/pages.rss.builder +3 -9
- data/config/routes.rb +1 -0
- data/lib/pages_core/configuration/pages.rb +0 -1
- data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +33 -17
- data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +0 -1
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +40 -0
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +68 -0
- data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +17 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.postcss.css +4 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +24 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/layout.css +21 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +5 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +5 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +18 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +6 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +65 -0
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +131 -0
- data/lib/rails/generators/pages_core/install/templates/pages_initializer.rb +0 -3
- metadata +68 -62
- data/app/javascript/admin-dist.js +0 -2
- data/app/javascript/components/ImageCropper/FocalPoint.jsx +0 -93
- data/app/javascript/components/Modal.jsx +0 -59
- data/app/javascript/components/PageImages.jsx +0 -25
- data/app/javascript/components/PageTree.jsx +0 -196
- data/app/javascript/components/RichTextToolbarButton.jsx +0 -20
- data/app/javascript/components/Toast.jsx +0 -72
- data/app/javascript/components/drag/useDraggable.js +0 -17
- data/app/javascript/stores/ModalStore.jsx +0 -12
- data/app/javascript/stores/ToastStore.jsx +0 -14
- data/app/javascript/stores.js +0 -2
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/GridOverlay.js +0 -66
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/ResponsiveEmbeds.js +0 -72
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.sass.scss +0 -15
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.scss +0 -12
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.scss +0 -26
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/breakpoints.scss +0 -42
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/clearfix.scss +0 -7
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/fonts.scss +0 -32
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid.scss +0 -168
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid_overlay.scss +0 -44
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.scss +0 -8
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.scss +0 -90
- data/lib/rails/generators/pages_core/frontend/templates/stylesheets/vendor/normalize.css +0 -349
- /data/app/javascript/components/Attachments/{Placeholder.jsx → Placeholder.tsx} +0 -0
- /data/app/javascript/components/ImageGrid/{FilePlaceholder.jsx → FilePlaceholder.tsx} +0 -0
- /data/app/javascript/{components.js → components.ts} +0 -0
- /data/app/javascript/{hooks.js → hooks.ts} +0 -0
|
@@ -8690,6 +8690,38 @@ body.modal > .wrapper {
|
|
|
8690
8690
|
margin: 20px -2px;
|
|
8691
8691
|
}
|
|
8692
8692
|
|
|
8693
|
+
.search-bar {
|
|
8694
|
+
display: flex;
|
|
8695
|
+
}
|
|
8696
|
+
|
|
8697
|
+
.search-bar input {
|
|
8698
|
+
flex: 1 1 auto;
|
|
8699
|
+
border-right: none;
|
|
8700
|
+
max-width: 600px;
|
|
8701
|
+
min-width: 50px;
|
|
8702
|
+
}
|
|
8703
|
+
|
|
8704
|
+
.search-bar input:focus {
|
|
8705
|
+
z-index: 20;
|
|
8706
|
+
}
|
|
8707
|
+
|
|
8708
|
+
.search-bar button {
|
|
8709
|
+
border-top-left-radius: 0px;
|
|
8710
|
+
border-bottom-left-radius: 0px;
|
|
8711
|
+
white-space: nowrap;
|
|
8712
|
+
}
|
|
8713
|
+
|
|
8714
|
+
main .search-bar {
|
|
8715
|
+
background: #f8f8f8;
|
|
8716
|
+
background: var(--background-disabled-color);
|
|
8717
|
+
padding: 16px 32px;
|
|
8718
|
+
margin: 0px 0px 16px 0px;
|
|
8719
|
+
}
|
|
8720
|
+
|
|
8721
|
+
.sidebar .search-bar {
|
|
8722
|
+
margin-top: 16px;
|
|
8723
|
+
}
|
|
8724
|
+
|
|
8693
8725
|
.sidebar {
|
|
8694
8726
|
background: #f8f8f8;
|
|
8695
8727
|
background: var(--background-disabled-color);
|
|
@@ -9131,6 +9163,13 @@ td.drag-handle {
|
|
|
9131
9163
|
font-weight: normal;
|
|
9132
9164
|
}
|
|
9133
9165
|
|
|
9166
|
+
.news-item-list .autopublish-notice, .calendar-item-list .autopublish-notice {
|
|
9167
|
+
font-size: 0.75rem;
|
|
9168
|
+
font-weight: normal;
|
|
9169
|
+
color: #666;
|
|
9170
|
+
color: var(--text-light-color);
|
|
9171
|
+
}
|
|
9172
|
+
|
|
9134
9173
|
.news-item-list .draft,
|
|
9135
9174
|
.calendar-item-list .draft,
|
|
9136
9175
|
.news-item-list .hidden,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.search-bar {
|
|
2
|
+
display: flex;
|
|
3
|
+
& input {
|
|
4
|
+
flex: 1 1 auto;
|
|
5
|
+
border-right: none;
|
|
6
|
+
max-width: 600px;
|
|
7
|
+
min-width: 50px;
|
|
8
|
+
&:focus {
|
|
9
|
+
z-index: 20;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
& button {
|
|
13
|
+
border-top-left-radius: 0px;
|
|
14
|
+
border-bottom-left-radius: 0px;
|
|
15
|
+
white-space: nowrap;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
main .search-bar {
|
|
20
|
+
background: var(--background-disabled-color);
|
|
21
|
+
padding: 16px 32px;
|
|
22
|
+
margin: 0px 0px 16px 0px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.sidebar .search-bar {
|
|
26
|
+
margin-top: 16px;
|
|
27
|
+
}
|
|
@@ -9,8 +9,6 @@ module Admin
|
|
|
9
9
|
|
|
10
10
|
require_authorization
|
|
11
11
|
|
|
12
|
-
helper_method :page_json
|
|
13
|
-
|
|
14
12
|
def index
|
|
15
13
|
@pages = Page.admin_list(@locale)
|
|
16
14
|
end
|
|
@@ -19,6 +17,15 @@ module Admin
|
|
|
19
17
|
@pages = Page.deleted.by_updated_at.in_locale(@locale)
|
|
20
18
|
end
|
|
21
19
|
|
|
20
|
+
def search
|
|
21
|
+
return if search_query.blank?
|
|
22
|
+
|
|
23
|
+
@search_documents =
|
|
24
|
+
SearchDocument.where(searchable_type: "Page")
|
|
25
|
+
.search(search_query, locale: @locale)
|
|
26
|
+
.paginate(per_page: 50, page: params[:page])
|
|
27
|
+
end
|
|
28
|
+
|
|
22
29
|
def show
|
|
23
30
|
redirect_to edit_admin_page_url(@locale, @page)
|
|
24
31
|
end
|
|
@@ -26,19 +33,14 @@ module Admin
|
|
|
26
33
|
def new
|
|
27
34
|
build_params = params[:page] ? page_params : nil
|
|
28
35
|
@page = build_page(@locale, build_params)
|
|
29
|
-
@page.parent =
|
|
30
|
-
Page.find(params[:parent])
|
|
31
|
-
elsif @news_pages
|
|
32
|
-
@news_pages.first
|
|
33
|
-
end
|
|
36
|
+
@page.parent = Page.find_by(id: params[:parent])
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def edit; end
|
|
37
40
|
|
|
38
41
|
def create
|
|
39
|
-
@page = build_page(@locale, page_params, param_categories)
|
|
42
|
+
@page = build_page(@locale, page_params, param_categories).tap(&:save)
|
|
40
43
|
if @page.valid?
|
|
41
|
-
@page.save
|
|
42
44
|
respond_with_page(@page) do
|
|
43
45
|
redirect_to(edit_admin_page_url(@locale, @page))
|
|
44
46
|
end
|
|
@@ -103,8 +105,7 @@ module Admin
|
|
|
103
105
|
def param_categories
|
|
104
106
|
return [] unless params[:category]
|
|
105
107
|
|
|
106
|
-
params.permit(category: {})[:category]
|
|
107
|
-
.to_hash
|
|
108
|
+
params.permit(category: {})[:category].to_hash
|
|
108
109
|
.map { |id, _| Category.find(id) }
|
|
109
110
|
end
|
|
110
111
|
|
|
@@ -11,10 +11,26 @@ module PagesCore
|
|
|
11
11
|
Page.where(parent_page_id: feeds)
|
|
12
12
|
.order("published_at DESC")
|
|
13
13
|
.published
|
|
14
|
-
.limit(20)
|
|
15
14
|
.localized(locale)
|
|
16
15
|
end
|
|
17
16
|
|
|
17
|
+
def per_page_rss_param(default = 20, max = 1000)
|
|
18
|
+
return default unless params[:per_page].is_a?(String)
|
|
19
|
+
|
|
20
|
+
params[:per_page].to_i.clamp(1, max)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render_page_rss(page, pagination_page = 1)
|
|
24
|
+
if page.feed_enabled?
|
|
25
|
+
render_rss(page.pages.paginate(per_page: per_page_rss_param,
|
|
26
|
+
page: pagination_page)
|
|
27
|
+
.includes(:image, :author),
|
|
28
|
+
title: page.name)
|
|
29
|
+
else
|
|
30
|
+
render_error 404
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
18
34
|
def render_rss(items, title: nil)
|
|
19
35
|
@title = PagesCore.config.site_name
|
|
20
36
|
@title += ": #{title}" if title
|
|
@@ -13,6 +13,8 @@ module PagesCore
|
|
|
13
13
|
|
|
14
14
|
layout "admin"
|
|
15
15
|
|
|
16
|
+
helper_method :search_query
|
|
17
|
+
|
|
16
18
|
class << self
|
|
17
19
|
# Get name of class with in lowercase, with underscores.
|
|
18
20
|
def underscore
|
|
@@ -30,6 +32,10 @@ module PagesCore
|
|
|
30
32
|
|
|
31
33
|
protected
|
|
32
34
|
|
|
35
|
+
def search_query
|
|
36
|
+
params[:q] || ""
|
|
37
|
+
end
|
|
38
|
+
|
|
33
39
|
def set_i18n_locale
|
|
34
40
|
I18n.locale = :en
|
|
35
41
|
end
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
47
|
+
end).compact
|
|
50
48
|
end
|
|
51
49
|
|
|
52
50
|
def page_record_url(record)
|
|
@@ -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")
|
|
@@ -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
|
|
6
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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"];
|
|
@@ -64,7 +84,10 @@ export default function Attachment(props) {
|
|
|
64
84
|
|
|
65
85
|
const icon = uploading ? "cloud-arrow-up" : "paperclip";
|
|
66
86
|
|
|
67
|
-
|
|
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(" ")}
|
|
@@ -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
|
|
5
|
-
import
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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={
|
|
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
|
|
3
|
+
import useModalStore from "../stores/useModalStore";
|
|
5
4
|
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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];
|
|
@@ -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
|
-
};
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import React, { useRef } from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
1
|
+
import React, { ChangeEvent, useRef } from "react";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
interface FileUploadButtonProps {
|
|
4
|
+
callback: (files: File[]) => void,
|
|
5
|
+
type: string,
|
|
6
|
+
multiple: boolean,
|
|
7
|
+
multiline: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function FileUploadButton(props: FileUploadButtonProps) {
|
|
11
|
+
const inputRef = useRef<HTMLInputElement>();
|
|
6
12
|
|
|
7
|
-
const handleChange = (evt) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (
|
|
13
|
+
const handleChange = (evt: ChangeEvent<HTMLInputElement>) => {
|
|
14
|
+
const fileList = evt.target.files;
|
|
15
|
+
const files: File[] = [];
|
|
16
|
+
for (let i = 0; i < fileList.length; i++) {
|
|
11
17
|
files.push(fileList[i]);
|
|
12
18
|
}
|
|
13
19
|
if (files.length > 0) {
|
|
@@ -15,7 +21,7 @@ export default function FileUploadButton(props) {
|
|
|
15
21
|
}
|
|
16
22
|
};
|
|
17
23
|
|
|
18
|
-
const triggerDialog = (evt) => {
|
|
24
|
+
const triggerDialog = (evt: Event) => {
|
|
19
25
|
evt.preventDefault();
|
|
20
26
|
inputRef.current.click();
|
|
21
27
|
};
|
|
@@ -38,10 +44,3 @@ export default function FileUploadButton(props) {
|
|
|
38
44
|
</div>
|
|
39
45
|
);
|
|
40
46
|
}
|
|
41
|
-
|
|
42
|
-
FileUploadButton.propTypes = {
|
|
43
|
-
callback: PropTypes.func,
|
|
44
|
-
type: PropTypes.string,
|
|
45
|
-
multiple: PropTypes.bool,
|
|
46
|
-
multiline: PropTypes.bool
|
|
47
|
-
};
|