maglevcms 1.8.0 → 2.0.0.beta1

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/.yarn/install-state.gz +0 -0
  3. data/README.md +4 -4
  4. data/Rakefile +2 -1
  5. data/app/components/maglev/block_component.rb +1 -1
  6. data/app/components/maglev/content/base.rb +1 -1
  7. data/app/components/maglev/content/link.rb +4 -0
  8. data/app/components/maglev/section_component.rb +2 -2
  9. data/app/controllers/concerns/maglev/fetchers_concern.rb +3 -1
  10. data/app/controllers/concerns/maglev/resource_id_concern.rb +10 -0
  11. data/app/controllers/concerns/maglev/standalone_sections_concern.rb +6 -0
  12. data/app/controllers/concerns/maglev/{ui_locale_concern.rb → user_interface_locale_concern.rb} +1 -1
  13. data/app/controllers/maglev/api/collection_items_controller.rb +7 -0
  14. data/app/controllers/maglev/api/page_clones_controller.rb +1 -2
  15. data/app/controllers/maglev/api_controller.rb +1 -1
  16. data/app/controllers/maglev/application_controller.rb +1 -5
  17. data/app/controllers/maglev/assets/active_storage_proxy_controller.rb +17 -0
  18. data/app/controllers/maglev/assets/proxy_controller.rb +21 -0
  19. data/app/controllers/maglev/editor_controller.rb +1 -1
  20. data/app/controllers/maglev/page_preview_controller.rb +3 -3
  21. data/app/frontend/admin/controllers/screenshot_controller.js +29 -1
  22. data/app/frontend/editor/assets/remixicons/clipboard-line.svg +1 -0
  23. data/app/frontend/editor/assets/remixicons/ri-draggable.svg +1 -0
  24. data/app/frontend/editor/assets/remixicons/ri-folders-line.svg +1 -0
  25. data/app/frontend/editor/assets/remixicons/ri-more-fill.svg +1 -0
  26. data/app/frontend/editor/assets/remixicons/ri-shadow-line.svg +1 -0
  27. data/app/frontend/editor/components/errors/forbidden.vue +24 -0
  28. data/app/frontend/editor/components/errors/stale-record.vue +2 -2
  29. data/app/frontend/editor/components/header-nav/device-toggler.vue +6 -3
  30. data/app/frontend/editor/components/header-nav/index.vue +13 -42
  31. data/app/frontend/editor/components/header-nav/locale-toggler/index.vue +1 -1
  32. data/app/frontend/editor/components/header-nav/page-info.vue +41 -0
  33. data/app/frontend/editor/components/header-nav/preview-button.vue +6 -1
  34. data/app/frontend/editor/components/header-nav/separator.vue +1 -1
  35. data/app/frontend/editor/components/image-library/index.vue +26 -29
  36. data/app/frontend/editor/components/kit/collection-item-input.vue +9 -0
  37. data/app/frontend/editor/components/kit/copy-paste-button.vue +26 -0
  38. data/app/frontend/editor/components/kit/dropdown.vue +5 -0
  39. data/app/frontend/editor/components/kit/icon-button.vue +24 -0
  40. data/app/frontend/editor/components/kit/icon.vue +2 -3
  41. data/app/frontend/editor/components/kit/index.js +4 -2
  42. data/app/frontend/editor/components/kit/link-input.vue +1 -1
  43. data/app/frontend/editor/components/kit/modal.vue +1 -1
  44. data/app/frontend/editor/components/kit/page-icon.vue +1 -1
  45. data/app/frontend/editor/components/kit/rich-text-input/link-buttons.vue +1 -1
  46. data/app/frontend/editor/components/kit/select-input.vue +4 -2
  47. data/app/frontend/editor/components/link-picker/actions.vue +1 -1
  48. data/app/frontend/editor/components/link-picker/index.vue +1 -1
  49. data/app/frontend/editor/components/link-picker/page.vue +2 -0
  50. data/app/frontend/editor/components/page/form/main.vue +1 -1
  51. data/app/frontend/editor/components/page/list/actions-button.vue +159 -0
  52. data/app/frontend/editor/components/page/list/index.vue +15 -24
  53. data/app/frontend/editor/components/page/list/list-item.vue +10 -61
  54. data/app/frontend/editor/components/section-highlighter/index.vue +47 -46
  55. data/app/frontend/editor/components/section-list/add-button.vue +39 -0
  56. data/app/frontend/editor/components/section-list/index.vue +30 -15
  57. data/app/frontend/editor/components/section-list/list-item.vue +23 -8
  58. data/app/frontend/editor/components/section-pane/block-list/index.vue +1 -0
  59. data/app/frontend/editor/components/section-pane/block-list/list-item.vue +9 -6
  60. data/app/frontend/editor/components/section-pane/block-tree/new-nested-block-button.vue +2 -2
  61. data/app/frontend/editor/components/section-pane/block-tree/tree-node.vue +2 -2
  62. data/app/frontend/editor/components/sidebar-nav/index.vue +102 -0
  63. data/app/frontend/editor/components/sidebar-nav/link.vue +60 -0
  64. data/app/frontend/editor/components/theme-section-list/category.vue +47 -0
  65. data/app/frontend/editor/components/theme-section-list/index.vue +7 -31
  66. data/app/frontend/editor/components/theme-section-list/list-item.vue +10 -3
  67. data/app/frontend/editor/design/components/modal.scss +1 -1
  68. data/app/frontend/editor/design/components/tooltip.scss +6 -0
  69. data/app/frontend/editor/design/transitions.scss +1 -1
  70. data/app/frontend/editor/layouts/app.vue +1 -1
  71. data/app/frontend/editor/layouts/default.vue +2 -2
  72. data/app/frontend/editor/layouts/slide-pane.vue +1 -1
  73. data/app/frontend/editor/locales/editor.ar.json +265 -0
  74. data/app/frontend/editor/locales/editor.en.json +23 -15
  75. data/app/frontend/editor/locales/editor.es.json +18 -10
  76. data/app/frontend/editor/locales/editor.fr.json +28 -13
  77. data/app/frontend/editor/locales/editor.pt-BR.json +18 -10
  78. data/app/frontend/editor/locales/index.js +3 -0
  79. data/app/frontend/editor/mixins/error-modal.js +7 -2
  80. data/app/frontend/editor/mixins/preview-transformation.js +0 -1
  81. data/app/frontend/editor/plugins/i18n.js +2 -17
  82. data/app/frontend/editor/services/__tests__/section.spec.js +6 -0
  83. data/app/frontend/editor/services/collection-item.js +8 -0
  84. data/app/frontend/editor/services/page.js +1 -1
  85. data/app/frontend/editor/services/section.js +30 -0
  86. data/app/frontend/editor/services/site.js +4 -4
  87. data/app/frontend/editor/services/theme.js +1 -0
  88. data/app/frontend/editor/spec/__mocks__/page.js +3 -1
  89. data/app/frontend/editor/spec/__mocks__/services.js +1 -0
  90. data/app/frontend/editor/store/__tests__/getters.spec.js +56 -1
  91. data/app/frontend/editor/store/actions/index.js +1 -0
  92. data/app/frontend/editor/store/actions/site.js +8 -4
  93. data/app/frontend/editor/store/getters.js +11 -0
  94. data/app/frontend/editor/views/page-preview.vue +2 -1
  95. data/app/frontend/images/favicon.svg +11 -0
  96. data/app/frontend/images/logo.svg +14 -0
  97. data/app/frontend/live-preview-client/iframe-decorator.js +17 -15
  98. data/app/frontend/live-preview-client/rails.js +1 -1
  99. data/app/helpers/maglev/application_helper.rb +7 -2
  100. data/app/helpers/maglev/editor_helper.rb +12 -5
  101. data/app/models/concerns/maglev/sections_concern.rb +4 -0
  102. data/app/models/maglev/asset.rb +13 -14
  103. data/app/models/maglev/page/path_concern.rb +23 -8
  104. data/app/models/maglev/page.rb +17 -18
  105. data/app/models/maglev/page_path.rb +18 -18
  106. data/app/models/maglev/section/setting.rb +1 -1
  107. data/app/models/maglev/section.rb +18 -9
  108. data/app/models/maglev/setting_types/link.rb +1 -1
  109. data/app/models/maglev/site.rb +13 -24
  110. data/app/services/maglev/app_container.rb +2 -2
  111. data/app/services/maglev/extract_locale.rb +3 -2
  112. data/app/services/maglev/fetch_page.rb +2 -0
  113. data/app/services/maglev/fetch_theme_layout.rb +2 -0
  114. data/app/services/maglev/get_page_fullpath.rb +1 -2
  115. data/app/services/maglev/remove_section_type.rb +50 -0
  116. data/app/services/maglev/rename_section_type.rb +57 -0
  117. data/app/services/maglev/search_pages.rb +2 -1
  118. data/app/views/layouts/maglev/admin/application.html.erb +1 -1
  119. data/app/views/maglev/admin/sections/previews/show.html.erb +16 -1
  120. data/app/views/maglev/api/collection_items/show.json.jbuilder +7 -0
  121. data/app/views/maglev/api/page_clones/create.json.jbuilder +3 -0
  122. data/app/views/maglev/editor/show.html.erb +6 -5
  123. data/config/locales/activerecord.ar.yml +13 -0
  124. data/config/locales/activerecord.en.yml +8 -2
  125. data/config/locales/activerecord.es.yml +4 -0
  126. data/config/locales/activerecord.fr.yml +6 -2
  127. data/config/locales/activerecord.pt-BR.yml +13 -0
  128. data/config/routes.rb +3 -4
  129. data/db/migrate/20200831101942_create_maglev_section_content.rb +10 -2
  130. data/db/migrate/20210819092740_switch_to_localized_page_fields.rb +13 -3
  131. data/db/migrate/20211008064437_add_locales_to_sites.rb +7 -1
  132. data/db/migrate/20211013210954_translate_section_content.rb +16 -2
  133. data/db/migrate/20211203224112_add_open_graph_tags_to_pages.rb +9 -3
  134. data/db/migrate/20220612092235_add_style_to_sites.rb +5 -1
  135. data/lib/commands/maglev/change_site_locales_command.rb +61 -0
  136. data/lib/commands/maglev/create_site_command.rb +28 -0
  137. data/lib/commands/maglev/sections/remove_command.rb +48 -0
  138. data/lib/commands/maglev/sections/rename_command.rb +49 -0
  139. data/lib/generators/maglev/templates/install/config/initializers/maglev.rb +4 -1
  140. data/lib/generators/maglev/templates/theme/app/views/theme/layout.html.erb.tt +8 -2
  141. data/lib/maglev/errors.rb +1 -0
  142. data/lib/maglev/preview_constraint.rb +5 -1
  143. data/lib/maglev/theme_filesystem_loader.rb +7 -0
  144. data/lib/maglev/version.rb +1 -1
  145. data/lib/maglev.rb +13 -3
  146. data/lib/tasks/maglev/icons.rake +123 -0
  147. data/lib/tasks/maglev/vite.rake +62 -0
  148. data/lib/tasks/maglev_tasks.rake +9 -107
  149. data/tailwind.config.js +1 -0
  150. metadata +48 -33
  151. data/app/controllers/maglev/assets_controller.rb +0 -10
  152. data/app/frontend/editor/components/kit/list-item-button.vue +0 -16
  153. data/app/frontend/editor/components/sidebar-nav.vue +0 -125
  154. data/app/frontend/editor/plugins/maglev_dummy.js +0 -2
  155. data/app/frontend/images/favicon.png +0 -0
  156. data/app/frontend/images/logo.png +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5f367edf49a70fcab89dd3906037a4d7e1b4711c50fd9a5b255f526f78262ec
4
- data.tar.gz: e041ca5f4be377ba43ab62b8ac86835e6c2cb4079e77274a50966b231a41098d
3
+ metadata.gz: 82bc9958b22dec5a92e3f9e3fab438418f666479d868e9196c6c96fe350d082c
4
+ data.tar.gz: 975cf8a2f8b11ce2c2f3cfad7c38ba7db2c7ab351d1e5a83a1f563e903a9def2
5
5
  SHA512:
6
- metadata.gz: 12e159b397754772159e3dfa9531d3cb147b7b73d2f0006452acce3f34aec256a9f66cbbcc5d054c2518dc6bcc476af5f382965835806255a4769426bfe4d1fe
7
- data.tar.gz: 6837950bdf76d44df4a2ede9a4c417010714631a03b7cab000edb5ff5e8c194f913c82281915361c20680ed6bc84c1ab9799fb02efb6f137a5e734980cb8264d
6
+ metadata.gz: 8a5ec523921178fdbecbec37ff7ddccab1fc9f1e26fd6d1fd270b7bf231ab28af1b111a7ecfc71b132abdce46f39ff785bc6630d6c98925c106bc266c6432037
7
+ data.tar.gz: 52b7d06da150eb80b06ec8006fa5960f8e77543cd7d054786a19d4875fed86b50946e2820567b402455df561b25fb1302e5e3a7be1adfa7b8f4b8b8d75dad417
Binary file
data/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  [![Build Status](https://github.com/maglevhq/maglev-core/actions/workflows/verify.yml/badge.svg)](https://github.com/maglevhq/maglev-core/actions/workflows/verify.yml)
4
4
 
5
- MaglevCMS is a website builder / CMS that you can plug in any Ruby on Rails 6+ application.
5
+ MaglevCMS is a website builder/CMS that you can integrate into any Ruby on Rails application from version 7 up to version 8.
6
6
 
7
7
  ## Demo
8
8
 
9
- Check out our [MaglevCMS PRO demo](https://demo-pro.maglev.dev) to see MaglevCMS in action.\
10
- This demo showcases the full capabilities of MaglevCMS, including advanced features available in the PRO version.
9
+ Check out our [MaglevCMS Saas Edition demo](https://demo-pro.maglev.dev) to see MaglevCMS in action.\
10
+ This demo showcases the full capabilities of MaglevCMS, including advanced features available in the SaaS version.
11
11
 
12
12
 
13
13
  ## Getting started / Documentation
@@ -17,7 +17,7 @@ This demo showcases the full capabilities of MaglevCMS, including advanced featu
17
17
 
18
18
  ## Interested in more power for your Rails SaaS?
19
19
 
20
- If you're a Rails SaaS owner looking to offer advanced page-building and customization features to your clients, check out [MaglevCMS PRO](https://pro.maglev.dev). With MaglevCMS PRO, you get premium tools, enhanced customization options, and dedicated support to take your platform to the next level.\
20
+ If you're a Rails SaaS owner looking to offer advanced page-building and customization features to your clients, check out [MaglevCMS Saas Edition](https://www.maglev.dev/saas-edition). With MaglevCMS SaaS Edition, you get premium tools, enhanced customization options, and dedicated support to take your platform to the next level.\
21
21
  Explore the full potential of MaglevCMS and see how it can transform your SaaS business.
22
22
 
23
23
  ## Testing
data/Rakefile CHANGED
@@ -20,8 +20,9 @@ require 'vite_ruby'
20
20
  ViteRuby.install_tasks
21
21
  ViteRuby.config.root # Ensure the engine is set as the root.
22
22
 
23
- rakefile_path = Rails::VERSION::MAJOR >= 8 ? 'spec/dummy/Rakefile' : 'spec/legacy_dummy/Rakefile'
23
+ rakefile_path = !defined?(Rails) || Rails::VERSION::MAJOR >= 8 ? 'spec/dummy/Rakefile' : 'spec/legacy_dummy/Rakefile'
24
24
  APP_RAKEFILE = File.expand_path(rakefile_path, __dir__)
25
+
25
26
  load 'rails/tasks/engine.rake'
26
27
  load 'rails/tasks/statistics.rake'
27
28
 
@@ -5,7 +5,7 @@ module Maglev
5
5
  include TagHelper
6
6
 
7
7
  extend Forwardable
8
- def_delegators :section, :site, :config
8
+ def_delegators :section, :site, :page, :config
9
9
 
10
10
  attr_reader :section, :id, :name, :type, :settings, :attributes, :definition
11
11
  attr_accessor :children
@@ -6,7 +6,7 @@ module Maglev
6
6
  include ::Maglev::Inspector
7
7
 
8
8
  extend Forwardable
9
- def_delegators :scope, :site, :config
9
+ def_delegators :scope, :site, :config, :page
10
10
 
11
11
  attr_accessor :scope, :content, :setting
12
12
 
@@ -23,6 +23,10 @@ module Maglev
23
23
  open_new_window? ? '_blank' : nil
24
24
  end
25
25
 
26
+ def active?
27
+ link[:link_type] == 'page' && link[:link_id] == page.id
28
+ end
29
+
26
30
  def to_s
27
31
  href
28
32
  end
@@ -5,7 +5,7 @@ module Maglev
5
5
  include TagHelper
6
6
 
7
7
  extend Forwardable
8
- def_delegators :parent, :site, :config
8
+ def_delegators :parent, :site, :page, :config
9
9
 
10
10
  attr_reader :parent, :id, :type, :settings, :attributes, :definition, :templates_root_path, :rendering_mode
11
11
 
@@ -97,7 +97,7 @@ module Maglev
97
97
  end
98
98
 
99
99
  def handle_error(exception)
100
- throw exception if %i[live section].include?(rendering_mode) || Rails.env.test?
100
+ raise exception if %i[live section].include?(rendering_mode) || Rails.env.test?
101
101
 
102
102
  Rails.logger.error [
103
103
  "⚠️ [Maglev] Error when rendering a \"#{type}\" type section ⚠️",
@@ -48,7 +48,9 @@ module Maglev
48
48
  end
49
49
 
50
50
  def fetch_maglev_theme_layout
51
- @fetch_maglev_theme_layout ||= maglev_services.fetch_theme_layout.call
51
+ @fetch_maglev_theme_layout ||= maglev_services.fetch_theme_layout.call(
52
+ page: fetch_maglev_page
53
+ )
52
54
  end
53
55
 
54
56
  def fetch_maglev_sections_path
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maglev
4
+ module ResourceIdConcern
5
+ def resource_id
6
+ # A standard UUID code contains 32 hex digits along with 4 "-"" symbols
7
+ Maglev.uuid_as_primary_key? && params[:id] ? params[:id][0..35] : params[:id]
8
+ end
9
+ end
10
+ end
@@ -10,6 +10,12 @@ module Maglev
10
10
  include Maglev::ContentLocaleConcern
11
11
 
12
12
  helper Maglev::PagePreviewHelper
13
+
14
+ private
15
+
16
+ def maglev_rendering_mode
17
+ params[:rendering_mode] || super
18
+ end
13
19
  end
14
20
 
15
21
  private
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Maglev
4
- module UiLocaleConcern
4
+ module UserInterfaceLocaleConcern
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
@@ -9,6 +9,13 @@ module Maglev
9
9
  keyword: params[:q]
10
10
  )
11
11
  end
12
+
13
+ def show
14
+ @item = services.fetch_collection_items.call(
15
+ collection_id: params[:collection_id],
16
+ id: params[:id]
17
+ )
18
+ end
12
19
  end
13
20
  end
14
21
  end
@@ -4,8 +4,7 @@ module Maglev
4
4
  module Api
5
5
  class PageClonesController < ::Maglev::ApiController
6
6
  def create
7
- page = clone_page(resources.find(params[:page_id]))
8
- head :created, location: api_page_path(page)
7
+ @page = clone_page(resources.find(params[:page_id]))
9
8
  end
10
9
 
11
10
  private
@@ -3,7 +3,7 @@
3
3
  module Maglev
4
4
  class ApiController < ::Maglev::ApplicationController
5
5
  include Maglev::JsonConcern
6
- include Maglev::UiLocaleConcern
6
+ include Maglev::UserInterfaceLocaleConcern
7
7
  include Maglev::ContentLocaleConcern
8
8
 
9
9
  before_action :authenticate
@@ -3,6 +3,7 @@
3
3
  module Maglev
4
4
  class ApplicationController < ::ApplicationController
5
5
  include Maglev::ServicesConcern
6
+ include Maglev::ResourceIdConcern
6
7
 
7
8
  protect_from_forgery with: :exception
8
9
 
@@ -13,10 +14,5 @@ module Maglev
13
14
  def use_engine_vite?
14
15
  true
15
16
  end
16
-
17
- def resource_id
18
- # A standard UUID code contains 32 hex digits along with 4 "-"" symbols
19
- Maglev.uuid_as_primary_key? && params[:id] ? params[:id][0..35] : params[:id]
20
- end
21
17
  end
22
18
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maglev
4
+ module Assets
5
+ class ActiveStorageProxyController < ::ActiveStorage::Blobs::ProxyController
6
+ include Maglev::ResourceIdConcern
7
+
8
+ private
9
+
10
+ def set_blob
11
+ @blob = Maglev::Asset.find(resource_id).file
12
+ rescue ActiveRecord::RecordNotFound
13
+ head :not_found
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maglev
4
+ module Assets
5
+ class ProxyController < ApplicationController
6
+ def show
7
+ @asset = Maglev::Asset.find(resource_id)
8
+ send_data @asset.download, filename: @asset.filename, type: @asset.content_type
9
+ response.headers['Cache-Control'] = cache_control_header
10
+ end
11
+
12
+ private
13
+
14
+ def cache_control_header
15
+ # Rails 8+ uses :'cache-control' and Rails 7.x uses :'Cache-Control'
16
+ Rails.configuration.public_file_server.headers[:'cache-control'] ||
17
+ Rails.configuration.public_file_server.headers[:'Cache-Control']
18
+ end
19
+ end
20
+ end
21
+ end
@@ -5,7 +5,7 @@ module Maglev
5
5
  include Maglev::AuthenticationConcern
6
6
  include Maglev::FetchersConcern
7
7
  include Maglev::BackActionConcern
8
- include Maglev::UiLocaleConcern
8
+ include Maglev::UserInterfaceLocaleConcern
9
9
  include Maglev::ContentLocaleConcern
10
10
 
11
11
  before_action :fetch_maglev_site, only: :show
@@ -7,7 +7,7 @@ module Maglev
7
7
  include Maglev::ContentLocaleConcern
8
8
 
9
9
  before_action :fetch_maglev_site
10
- before_action :extract_content_locale
10
+ around_action :extract_content_locale
11
11
 
12
12
  def index
13
13
  render_maglev_page
@@ -43,9 +43,9 @@ module Maglev
43
43
  false
44
44
  end
45
45
 
46
- def extract_content_locale
46
+ def extract_content_locale(&block)
47
47
  _, locale = maglev_services.extract_locale.call(params: params, locales: maglev_site.locale_prefixes)
48
- ::I18n.locale = locale
48
+ ::I18n.with_locale(locale, &block)
49
49
  end
50
50
 
51
51
  def fallback_to_default_locale
@@ -2,7 +2,7 @@ import { Controller } from 'stimulus'
2
2
  import axios from '../utils/axios'
3
3
 
4
4
  export default class extends Controller {
5
- static targets = ['source', 'output']
5
+ static targets = ['source', 'output', 'fileInput']
6
6
  static values = { url: String }
7
7
 
8
8
  connect() {
@@ -38,4 +38,32 @@ export default class extends Controller {
38
38
  .catch((error) => console.log('ERROR!', error))
39
39
  })
40
40
  }
41
+
42
+ triggerFileInput() {
43
+ this.fileInputTarget.click()
44
+ }
45
+
46
+ handleFileUpload(event) {
47
+ const file = event.target.files[0]
48
+ if (!file) return
49
+
50
+ const reader = new FileReader()
51
+ reader.onload = () => {
52
+ const base64Image = reader.result
53
+ this.outputTarget.src = base64Image
54
+
55
+ axios
56
+ .post(this.urlValue, {
57
+ screenshot: { base64_image: base64Image },
58
+ })
59
+ .then(() => {
60
+ alert('Screenshot uploaded!')
61
+ })
62
+ .catch((error) => console.log('ERROR!', error))
63
+ }
64
+ reader.readAsDataURL(file)
65
+
66
+ // Clear the input so the same file can be selected again
67
+ event.target.value = ''
68
+ }
41
69
  }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M7 4V2H17V4H20.0066C20.5552 4 21 4.44495 21 4.9934V21.0066C21 21.5552 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5551 3 21.0066V4.9934C3 4.44476 3.44495 4 3.9934 4H7ZM7 6H5V20H19V6H17V8H7V6ZM9 4V6H15V4H9Z"></path></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M8.5 7C9.32843 7 10 6.32843 10 5.5C10 4.67157 9.32843 4 8.5 4C7.67157 4 7 4.67157 7 5.5C7 6.32843 7.67157 7 8.5 7ZM8.5 13.5C9.32843 13.5 10 12.8284 10 12C10 11.1716 9.32843 10.5 8.5 10.5C7.67157 10.5 7 11.1716 7 12C7 12.8284 7.67157 13.5 8.5 13.5ZM10 18.5C10 19.3284 9.32843 20 8.5 20C7.67157 20 7 19.3284 7 18.5C7 17.6716 7.67157 17 8.5 17C9.32843 17 10 17.6716 10 18.5ZM15.5 7C16.3284 7 17 6.32843 17 5.5C17 4.67157 16.3284 4 15.5 4C14.6716 4 14 4.67157 14 5.5C14 6.32843 14.6716 7 15.5 7ZM17 12C17 12.8284 16.3284 13.5 15.5 13.5C14.6716 13.5 14 12.8284 14 12C14 11.1716 14.6716 10.5 15.5 10.5C16.3284 10.5 17 11.1716 17 12ZM15.5 20C16.3284 20 17 19.3284 17 18.5C17 17.6716 16.3284 17 15.5 17C14.6716 17 14 17.6716 14 18.5C14 19.3284 14.6716 20 15.5 20Z"></path></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6 7V4C6 3.44772 6.44772 3 7 3H13.4142L15.4142 5H21C21.5523 5 22 5.44772 22 6V16C22 16.5523 21.5523 17 21 17H18V20C18 20.5523 17.5523 21 17 21H3C2.44772 21 2 20.5523 2 20V8C2 7.44772 2.44772 7 3 7H6ZM6 9H4V19H16V17H6V9ZM8 5V15H20V7H14.5858L12.5858 5H8Z"></path></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 10C3.9 10 3 10.9 3 12C3 13.1 3.9 14 5 14C6.1 14 7 13.1 7 12C7 10.9 6.1 10 5 10ZM19 10C17.9 10 17 10.9 17 12C17 13.1 17.9 14 19 14C20.1 14 21 13.1 21 12C21 10.9 20.1 10 19 10ZM12 10C10.9 10 10 10.9 10 12C10 13.1 10.9 14 12 14C13.1 14 14 13.1 14 12C14 10.9 13.1 10 12 10Z"></path></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 2C2.44772 2 2 2.44772 2 3V17C2 17.5523 2.44772 18 3 18H6V21C6 21.5523 6.44772 22 7 22H21C21.5523 22 22 21.5523 22 21V7C22 6.44772 21.5523 6 21 6H18V3C18 2.44772 17.5523 2 17 2H3ZM18 11.5607L20 13.5607V16.4393L18 14.4393V11.5607ZM18 9.43934V8H20V11.4393L18 9.43934ZM18 17V16.5607L20 18.5607V20H18.5607L16.5607 18H17C17.5523 18 18 17.5523 18 17ZM14.4393 18L16.4393 20H13.5607L11.5607 18H14.4393ZM9.43934 18L11.4393 20H8V18H9.43934ZM4 16V4H16V16H4Z"></path></svg>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div class="pt-8 pb-4">
3
+ <p class="text-gray-600 text-justify">
4
+ {{ $t('errorModals.forbidden.message') }}
5
+ </p>
6
+
7
+ <div class="mt-4 text-center">
8
+ <button class="big-submit-button bg-editor-primary" @click="close">
9
+ {{ $t('errorModals.forbidden.button') }}
10
+ </button>
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script>
16
+ export default {
17
+ name: 'ForbiddenError',
18
+ methods: {
19
+ close() {
20
+ this.closeModal()
21
+ },
22
+ },
23
+ }
24
+ </script>
@@ -1,10 +1,10 @@
1
1
  <template>
2
- <div class="pt-8 pb-4">
2
+ <div class="pt-8 pb-4 space-y-4">
3
3
  <p class="text-gray-600 text-justify">
4
4
  {{ $t('errorModals.staleRecord.message') }}
5
5
  </p>
6
6
 
7
- <div class="mt-8 text-center">
7
+ <div class="text-center">
8
8
  <button class="big-submit-button bg-editor-primary" @click="reloadPage">
9
9
  {{ $t('errorModals.staleRecord.button') }}
10
10
  </button>
@@ -5,9 +5,12 @@
5
5
  :key="icon.device"
6
6
  :name="icon.name"
7
7
  :color="icon.color"
8
- class="cursor-pointer"
9
- :class="{ 'mx-2': index === 1 }"
10
- size="1.5rem"
8
+ class="cursor-pointer hover:bg-editor-primary hover:bg-opacity-5 px-2"
9
+ :class="{
10
+ 'mx-1': index === 1,
11
+ 'bg-editor-primary bg-opacity-5': device === icon.device
12
+ }"
13
+ size="2.5rem"
11
14
  @click.native="setDevice(icon.device)"
12
15
  />
13
16
  </div>
@@ -7,54 +7,26 @@
7
7
  <div class="h-6 bg-gray-200 rounded w-1/4 mx-6"></div>
8
8
  <div class="h-6 bg-gray-200 rounded w-1/4 mx-6"></div>
9
9
  </div>
10
- <div class="flex justify-between h-full w-full" v-else>
11
- <div class="flex">
12
- <router-link
13
- :to="{ name: 'listPages' }"
14
- class="flex items-center py-4 px-6 flex-row hover:bg-editor-primary hover:bg-opacity-5 transition-colors duration-200"
15
- :class="{
16
- 'bg-white': !isListPagesActive,
17
- 'bg-editor-primary bg-opacity-5': isListPagesActive,
18
- }"
19
- >
20
- <span>{{ $t('headerNav.pages') }}</span>
21
- <uikit-page-icon class="ml-4" :page="currentPage" />
22
- <span class="ml-2">{{ currentPage.title }}</span>
23
- <uikit-icon name="arrow-down-s-line" class="ml-3" />
24
- </router-link>
25
-
26
- <separator />
27
-
28
- <router-link
29
- :to="{ name: 'editPageSettings' }"
30
- class="flex items-center py-4 px-6 flex-row hover:bg-editor-primary hover:bg-opacity-5 transition-colors duration-200"
31
- :class="{
32
- 'bg-white': !isEditPageActive,
33
- 'bg-editor-primary bg-opacity-5': isEditPageActive,
34
- }"
35
- >
36
- <uikit-icon name="settings-4-line" size="1.25rem" />
37
- <span class="ml-2">{{ $t('headerNav.pageSettings') }}</span>
38
- </router-link>
39
-
40
- <separator />
10
+ <div class="grid grid-cols-5 h-full w-full" v-else>
11
+ <div class="col-span-3 lg:col-span-2">
12
+ <page-info />
41
13
  </div>
42
14
 
43
- <div class="flex">
44
- <div class="flex py-4 px-6">
45
- <device-toggler />
46
- </div>
47
- <separator />
15
+ <div class="col-span-1 hidden lg:flex justify-center">
16
+ <device-toggler />
17
+ </div>
48
18
 
19
+ <div class="col-span-2 flex justify-end h-full">
49
20
  <div class="flex h-full relative" v-if="hasMultipleLocales">
50
21
  <locale-toggler />
51
22
  </div>
52
23
  <separator v-if="hasMultipleLocales" />
53
24
 
54
25
  <preview-toggler v-if="isSitePublishable" />
55
- <preview-button v-else />
26
+ <preview-button v-else class="hidden lg:flex" />
56
27
 
57
28
  <separator v-if="isSitePublishable" />
29
+
58
30
  <div
59
31
  class="flex items-center h-full relative space-x-1 pr-4"
60
32
  v-if="isSitePublishable"
@@ -64,7 +36,7 @@
64
36
  </div>
65
37
 
66
38
  <div v-else>
67
- <save-button :big="true" />
39
+ <save-button big />
68
40
  </div>
69
41
  </div>
70
42
  </div>
@@ -79,6 +51,7 @@ import PreviewToggler from './preview-toggler.vue'
79
51
  import PublishButton from './publish-button.vue'
80
52
  import SaveButton from './save-button.vue'
81
53
  import Separator from './separator.vue'
54
+ import PageInfo from './page-info.vue'
82
55
 
83
56
  export default {
84
57
  name: 'HeaderNav',
@@ -90,6 +63,7 @@ export default {
90
63
  SaveButton,
91
64
  Separator,
92
65
  PreviewToggler,
66
+ PageInfo,
93
67
  },
94
68
  computed: {
95
69
  isLoading() {
@@ -97,10 +71,7 @@ export default {
97
71
  },
98
72
  isListPagesActive() {
99
73
  return this.$route.name === 'listPages'
100
- },
101
- isEditPageActive() {
102
- return this.$route.name === 'editPageSettings'
103
- },
74
+ },
104
75
  },
105
76
  }
106
77
  </script>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <uikit-dropdown placement="bottom" ref="dropdown" class="h-full flex">
3
+ <uikit-dropdown placement="bottom" ref="dropdown" class="h-full flex" popoverClass="tooltip-menu">
4
4
  <template v-slot:button>
5
5
  <button
6
6
  class="h-full w-full px-6 hover:bg-editor-primary hover:bg-opacity-5 transition-colors duration-200 flex items-center focus:outline-none focus:none"
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <div class="flex flex-row items-center h-full w-full px-4 overflow-hidden">
3
+ <div class="flex flex-col leading-none overflow-hidden">
4
+ <div class="flex items-center">
5
+ <uikit-page-icon :page="currentPage" size="1.1rem" class="shrink-0 text-gray-900" />
6
+ <div class="text-base font-semibold truncate ml-1 mr-3">{{ currentPage.title }}</div>
7
+
8
+ <page-actions-button
9
+ :page="currentPage"
10
+ compact
11
+ @on-clone="goToClonedPage"
12
+ />
13
+ </div>
14
+ <div class="text-xs text-gray-500 flex items-center space-x-1">
15
+ <p class="truncate text-gray-700">
16
+ <span class="text-gray-900 font-semibold">{{ currentPagePath[0] }}</span>{{ currentPagePath.slice(1) }}
17
+ </p>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script>
24
+ import { mapGetters } from 'vuex'
25
+ import PageActionsButton from '../page/list/actions-button.vue'
26
+
27
+ export default {
28
+ name: 'PageInfo',
29
+ components: {
30
+ PageActionsButton,
31
+ },
32
+ computed: {
33
+ ...mapGetters(['currentPagePath', 'currentPageUrl']),
34
+ },
35
+ methods: {
36
+ goToClonedPage(page) {
37
+ this.$router.push({ name: 'editPage', params: { pageId: page.path } })
38
+ },
39
+ },
40
+ }
41
+ </script>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <a
3
- :href="currentPage.previewUrl"
3
+ :href="currentPageUrl"
4
4
  target="_blank"
5
5
  class="px-6 flex items-center hover:bg-editor-primary/5 transition-colors duration-200"
6
6
  >
@@ -11,5 +11,10 @@
11
11
  <script>
12
12
  export default {
13
13
  name: 'PreviewButton',
14
+ computed: {
15
+ currentPageUrl() {
16
+ return this.currentPage.liveUrl
17
+ },
18
+ },
14
19
  }
15
20
  </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="h-full py-4 w-px">
2
+ <div class="h-full py-4 w-px shrink-0">
3
3
  <div class="h-full w-full bg-gray-300" />
4
4
  </div>
5
5
  </template>
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div>
3
- <div class="flex items-center" v-if="!hasNoImagesYet">
2
+ <div class="h-156 flex flex-col space-y-5">
3
+ <div class="flex items-center h-10" v-if="!hasNoImagesYet">
4
4
  <image-uploader
5
5
  :multiple="true"
6
6
  @uploaded="refresh"
@@ -12,34 +12,31 @@
12
12
  @search="search"
13
13
  />
14
14
  </div>
15
- <div class="mt-5">
16
- <div
17
- class="overflow-y-auto h-128"
18
- :class="{ invisible: images === null }"
19
- >
20
- <div class="mt-10 flex flex-col items-center" v-if="hasNoImagesYet">
21
- <p class="text-center">{{ $t('imageLibrary.none') }}</p>
22
- <image-uploader @uploaded="refresh" :multiple="true" class="mt-4" />
23
- </div>
24
- <transition :name="slideDirection" mode="out-in" v-else>
25
- <image-list
26
- :images="images"
27
- :key="activePage"
28
- :pickerMode="pickerMode"
29
- v-on="$listeners"
30
- @destroy="destroyImage"
31
- />
32
- </transition>
15
+ <div class="grow overflow-y-auto">
16
+ <div class="mt-10 flex flex-col items-center" v-if="hasNoImagesYet">
17
+ <p class="text-center">{{ $t('imageLibrary.none') }}</p>
18
+ <image-uploader @uploaded="refresh" :multiple="true" class="mt-4" />
33
19
  </div>
34
- <uikit-pagination
35
- labelI18nKey="imageLibrary.pagination.label"
36
- noItemsI18nKey="imageLibrary.pagination.noItems"
37
- :activePage="activePage"
38
- :totalItems="totalItems"
39
- :perPage="perPage"
40
- @change="(page) => (this.activePage = page)"
41
- />
20
+ <transition :name="slideDirection" mode="out-in" v-else>
21
+ <image-list
22
+ :images="images"
23
+ :key="activePage"
24
+ :pickerMode="pickerMode"
25
+ v-on="$listeners"
26
+ @destroy="destroyImage"
27
+ />
28
+ </transition>
42
29
  </div>
30
+ <uikit-pagination
31
+ class="shrink-0"
32
+ labelI18nKey="imageLibrary.pagination.label"
33
+ noItemsI18nKey=""
34
+ :activePage="activePage"
35
+ :totalItems="totalItems"
36
+ :perPage="perPage"
37
+ @change="(page) => (this.activePage = page)"
38
+ v-if="!hasNoImagesYet"
39
+ />
43
40
  </div>
44
41
  </template>
45
42
 
@@ -70,7 +67,7 @@ export default {
70
67
  },
71
68
  computed: {
72
69
  hasNoImagesYet() {
73
- return this.isBlank(this.images) && this.query === null && !this.loading
70
+ return this.isBlank(this.images) && this.query === null
74
71
  },
75
72
  },
76
73
  methods: {