alchemy_cms 7.0.8 → 7.1.0.pre.b1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/backport.yml +36 -0
- data/.github/workflows/test.yml +3 -2
- data/.gitignore +1 -0
- data/.standard.yml +1 -1
- data/CHANGELOG.md +145 -0
- data/Gemfile +8 -10
- data/README.md +10 -8
- data/alchemy_cms.gemspec +3 -2
- data/app/assets/config/alchemy_manifest.js +0 -1
- data/app/assets/javascripts/alchemy/admin.js +1 -19
- data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +2 -3
- data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +19 -34
- data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +38 -13
- data/app/assets/javascripts/alchemy/alchemy.file_progress.js.coffee +1 -1
- data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +32 -25
- data/app/assets/javascripts/alchemy/alchemy.growler.js.coffee +1 -1
- data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +3 -5
- data/app/assets/javascripts/alchemy/alchemy.initializer.js.coffee +0 -57
- data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +22 -63
- data/app/assets/javascripts/alchemy/alchemy.list_filter.js.coffee +2 -2
- data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +5 -4
- data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +5 -5
- data/app/assets/javascripts/alchemy/templates/index.js +0 -2
- data/app/assets/javascripts/alchemy/templates/node_folder.hbs +1 -1
- data/app/assets/javascripts/alchemy/templates/page.hbs +1 -1
- data/app/assets/javascripts/alchemy/templates/page_folder.hbs +2 -2
- data/app/assets/stylesheets/alchemy/_custom-properties.scss +82 -0
- data/app/assets/stylesheets/alchemy/_mixins.scss +38 -30
- data/app/assets/stylesheets/alchemy/_variables.scss +12 -5
- data/app/assets/stylesheets/alchemy/admin.scss +3 -4
- data/app/assets/stylesheets/alchemy/archive.scss +107 -50
- data/app/assets/stylesheets/alchemy/attachments.scss +5 -4
- data/app/assets/stylesheets/alchemy/buttons.scss +38 -164
- data/app/assets/stylesheets/alchemy/dashboard.scss +31 -6
- data/app/assets/stylesheets/alchemy/dialogs.scss +12 -28
- data/app/assets/stylesheets/alchemy/elements.scss +273 -282
- data/app/assets/stylesheets/alchemy/flash.scss +20 -12
- data/app/assets/stylesheets/alchemy/forms.scss +21 -34
- data/app/assets/stylesheets/alchemy/frame.scss +11 -32
- data/app/assets/stylesheets/alchemy/hints.scss +4 -62
- data/app/assets/stylesheets/alchemy/image_library.scss +36 -33
- data/app/assets/stylesheets/alchemy/menubar.scss +7 -6
- data/app/assets/stylesheets/alchemy/navigation.scss +27 -15
- data/app/assets/stylesheets/alchemy/nodes.scss +11 -7
- data/app/assets/stylesheets/alchemy/notices.scss +16 -4
- data/app/assets/stylesheets/alchemy/page-select.scss +10 -2
- data/app/assets/stylesheets/alchemy/pagination.scss +22 -13
- data/app/assets/stylesheets/alchemy/resource_info.scss +7 -5
- data/app/assets/stylesheets/alchemy/selects.scss +49 -42
- data/app/assets/stylesheets/alchemy/shoelace.scss +345 -0
- data/app/assets/stylesheets/alchemy/sitemap.scss +24 -14
- data/app/assets/stylesheets/alchemy/spinner.scss +9 -19
- data/app/assets/stylesheets/alchemy/tables.scss +16 -24
- data/app/assets/stylesheets/alchemy/tags.scss +4 -0
- data/app/assets/stylesheets/alchemy/toolbar.scss +29 -25
- data/app/assets/stylesheets/alchemy/upload.scss +140 -89
- data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +80 -108
- data/app/components/alchemy/admin/node_select.rb +39 -0
- data/app/components/alchemy/admin/page_select.rb +42 -0
- data/app/controllers/alchemy/admin/base_controller.rb +5 -6
- data/app/controllers/alchemy/admin/elements_controller.rb +63 -35
- data/app/controllers/alchemy/base_controller.rb +4 -2
- data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +1 -1
- data/app/controllers/concerns/alchemy/site_redirects.rb +1 -1
- data/app/decorators/alchemy/element_editor.rb +0 -2
- data/app/helpers/alchemy/admin/attachments_helper.rb +6 -5
- data/app/helpers/alchemy/admin/base_helper.rb +17 -12
- data/app/helpers/alchemy/admin/ingredients_helper.rb +4 -1
- data/app/helpers/alchemy/admin/pages_helper.rb +5 -11
- data/app/helpers/alchemy/base_helper.rb +47 -13
- data/app/javascript/alchemy_admin/components/alchemy_html_element.js +129 -0
- data/app/javascript/alchemy_admin/components/button.js +59 -0
- data/app/javascript/alchemy_admin/components/char_counter.js +40 -0
- data/app/javascript/alchemy_admin/components/datepicker.js +39 -0
- data/app/javascript/alchemy_admin/components/dialog_link.js +45 -0
- data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +36 -0
- data/app/javascript/alchemy_admin/components/element_editor.js +553 -0
- data/app/javascript/alchemy_admin/components/ingredient_group.js +54 -0
- data/app/javascript/alchemy_admin/components/link_buttons/link_button.js +48 -0
- data/app/javascript/alchemy_admin/components/link_buttons/unlink_button.js +38 -0
- data/app/javascript/alchemy_admin/components/link_buttons.js +79 -0
- data/app/javascript/alchemy_admin/components/node_select.js +45 -0
- data/app/javascript/alchemy_admin/components/overlay.js +18 -0
- data/app/javascript/alchemy_admin/components/page_select.js +63 -0
- data/app/javascript/alchemy_admin/components/remote_select.js +134 -0
- data/app/javascript/alchemy_admin/components/select.js +12 -0
- data/app/javascript/alchemy_admin/components/spinner.js +31 -0
- data/app/javascript/alchemy_admin/components/tinymce.js +146 -0
- data/app/javascript/alchemy_admin/components/uploader/file_upload.js +266 -0
- data/app/javascript/alchemy_admin/components/uploader/progress.js +258 -0
- data/app/javascript/alchemy_admin/components/uploader.js +132 -0
- data/app/javascript/alchemy_admin/dirty.js +49 -0
- data/app/javascript/alchemy_admin/file_editors.js +1 -1
- data/app/javascript/alchemy_admin/gui.js +14 -0
- data/app/javascript/alchemy_admin/i18n.js +12 -8
- data/app/javascript/alchemy_admin/image_cropper.js +6 -3
- data/app/javascript/alchemy_admin/image_loader.js +7 -15
- data/app/javascript/alchemy_admin/ingredient_anchor_link.js +2 -5
- data/app/javascript/alchemy_admin/initializer.js +65 -0
- data/app/javascript/alchemy_admin/locales/en.js +31 -0
- data/app/javascript/alchemy_admin/picture_editors.js +2 -2
- data/app/javascript/alchemy_admin/picture_selector.js +38 -0
- data/app/javascript/alchemy_admin/please_wait_overlay.js +8 -0
- data/app/javascript/alchemy_admin/sortable_elements.js +78 -0
- data/app/javascript/alchemy_admin/spinner.js +36 -0
- data/app/javascript/alchemy_admin/tags_autocomplete.js +46 -0
- data/app/javascript/alchemy_admin/utils/ajax.js +6 -5
- data/app/javascript/alchemy_admin/utils/dom_helpers.js +20 -0
- data/app/javascript/alchemy_admin/utils/format.js +11 -0
- data/app/javascript/alchemy_admin/utils/string_conversions.js +10 -0
- data/app/javascript/alchemy_admin.js +70 -13
- data/app/javascript/menubar.js +10 -0
- data/app/models/alchemy/attachment.rb +9 -11
- data/app/models/alchemy/element.rb +11 -0
- data/app/models/alchemy/ingredients/richtext.rb +1 -10
- data/app/models/alchemy/node.rb +4 -0
- data/app/models/alchemy/page/page_natures.rb +10 -2
- data/app/models/alchemy/page.rb +9 -49
- data/app/models/alchemy/picture/url.rb +1 -9
- data/app/serializers/alchemy/page_tree_serializer.rb +2 -1
- data/app/services/alchemy/copy_page.rb +98 -0
- data/app/views/alchemy/_menubar.html.erb +17 -13
- data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +14 -10
- data/app/views/alchemy/admin/attachments/_attachment.html.erb +44 -36
- data/app/views/alchemy/admin/attachments/_replace_button.html.erb +15 -21
- data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +0 -1
- data/app/views/alchemy/admin/attachments/assign.js.erb +1 -1
- data/app/views/alchemy/admin/attachments/index.html.erb +6 -4
- data/app/views/alchemy/admin/attachments/show.html.erb +8 -8
- data/app/views/alchemy/admin/clipboard/clear.js.erb +1 -1
- data/app/views/alchemy/admin/clipboard/index.html.erb +3 -7
- data/app/views/alchemy/admin/clipboard/insert.js.erb +1 -1
- data/app/views/alchemy/admin/crop.html.erb +1 -1
- data/app/views/alchemy/admin/dashboard/_locked_pages.html.erb +1 -1
- data/app/views/alchemy/admin/dashboard/index.html.erb +13 -11
- data/app/views/alchemy/admin/dashboard/info.html.erb +7 -7
- data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +21 -23
- data/app/views/alchemy/admin/elements/_element.html.erb +52 -44
- data/app/views/alchemy/admin/elements/_footer.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_header.html.erb +11 -12
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +33 -45
- data/app/views/alchemy/admin/elements/create.js.erb +7 -15
- data/app/views/alchemy/admin/elements/destroy.js.erb +0 -2
- data/app/views/alchemy/admin/elements/index.html.erb +27 -24
- data/app/views/alchemy/admin/elements/new.html.erb +9 -11
- data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +2 -2
- data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +3 -3
- data/app/views/alchemy/admin/ingredients/_video_fields.html.erb +1 -2
- data/app/views/alchemy/admin/languages/_form.html.erb +2 -3
- data/app/views/alchemy/admin/languages/_language.html.erb +15 -8
- data/app/views/alchemy/admin/languages/_table.html.erb +1 -0
- data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +28 -16
- data/app/views/alchemy/admin/layoutpages/index.html.erb +2 -2
- data/app/views/alchemy/admin/legacy_page_urls/_legacy_page_url.html.erb +12 -8
- data/app/views/alchemy/admin/legacy_page_urls/_new.html.erb +1 -1
- data/app/views/alchemy/admin/nodes/_form.html.erb +20 -21
- data/app/views/alchemy/admin/nodes/_node.html.erb +39 -34
- data/app/views/alchemy/admin/nodes/index.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_anchor_link.html.erb +4 -4
- data/app/views/alchemy/admin/pages/_create_language_form.html.erb +2 -2
- data/app/views/alchemy/admin/pages/_current_page.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_external_link.html.erb +4 -4
- data/app/views/alchemy/admin/pages/_file_link.html.erb +5 -5
- data/app/views/alchemy/admin/pages/_form.html.erb +10 -21
- data/app/views/alchemy/admin/pages/_internal_link.html.erb +4 -4
- data/app/views/alchemy/admin/pages/_locked_page.html.erb +2 -2
- data/app/views/alchemy/admin/pages/_new_page_form.html.erb +4 -17
- data/app/views/alchemy/admin/pages/_page.html.erb +76 -72
- data/app/views/alchemy/admin/pages/_page_infos.html.erb +23 -7
- data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +2 -1
- data/app/views/alchemy/admin/pages/_page_status.html.erb +11 -21
- data/app/views/alchemy/admin/pages/_publication_fields.html.erb +2 -5
- data/app/views/alchemy/admin/pages/_table.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_table_row.html.erb +43 -39
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +43 -38
- data/app/views/alchemy/admin/pages/configure.html.erb +12 -14
- data/app/views/alchemy/admin/pages/edit.html.erb +80 -103
- data/app/views/alchemy/admin/pages/info.html.erb +20 -11
- data/app/views/alchemy/admin/pages/link.html.erb +22 -16
- data/app/views/alchemy/admin/pages/new.html.erb +9 -11
- data/app/views/alchemy/admin/pages/unlock.js.erb +10 -3
- data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +15 -13
- data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +3 -5
- data/app/views/alchemy/admin/partials/_routes.html.erb +10 -2
- data/app/views/alchemy/admin/partials/_site_select.html.erb +6 -5
- data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +28 -23
- data/app/views/alchemy/admin/pictures/_archive.html.erb +5 -5
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +21 -23
- data/app/views/alchemy/admin/pictures/_infos.html.erb +2 -6
- data/app/views/alchemy/admin/pictures/_picture.html.erb +15 -17
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +17 -16
- data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +1 -1
- data/app/views/alchemy/admin/pictures/assign.js.erb +1 -1
- data/app/views/alchemy/admin/pictures/index.html.erb +34 -30
- data/app/views/alchemy/admin/pictures/show.html.erb +3 -3
- data/app/views/alchemy/admin/resources/_filter.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_form.html.erb +2 -2
- data/app/views/alchemy/admin/resources/_resource.html.erb +16 -9
- data/app/views/alchemy/admin/resources/_table.html.erb +4 -1
- data/app/views/alchemy/admin/resources/index.html.erb +22 -19
- data/app/views/alchemy/admin/sites/index.html.erb +2 -1
- data/app/views/alchemy/admin/styleguide/index.html.erb +54 -28
- data/app/views/alchemy/admin/tags/_tag.html.erb +16 -14
- data/app/views/alchemy/admin/tags/index.html.erb +15 -12
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +29 -0
- data/app/views/alchemy/admin/uploader/_button.html.erb +23 -29
- data/app/views/alchemy/admin/uploader/_setup.html.erb +3 -8
- data/app/views/alchemy/base/500.html.erb +1 -1
- data/app/views/alchemy/base/error_notice.js.erb +0 -1
- data/app/views/alchemy/ingredients/_boolean_editor.html.erb +1 -1
- data/app/views/alchemy/ingredients/_datetime_editor.html.erb +1 -1
- data/app/views/alchemy/ingredients/_file_editor.html.erb +5 -5
- data/app/views/alchemy/ingredients/_link_editor.html.erb +1 -1
- data/app/views/alchemy/ingredients/_node_editor.html.erb +6 -19
- data/app/views/alchemy/ingredients/_page_editor.html.erb +7 -19
- data/app/views/alchemy/ingredients/_picture_editor.html.erb +2 -2
- data/app/views/alchemy/ingredients/_richtext_editor.html.erb +6 -15
- data/app/views/alchemy/ingredients/_select_editor.html.erb +2 -1
- data/app/views/alchemy/ingredients/_text_editor.html.erb +1 -1
- data/app/views/alchemy/ingredients/shared/_anchor.html.erb +1 -1
- data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +10 -20
- data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +42 -49
- data/app/views/kaminari/alchemy/_first_page.html.erb +4 -2
- data/app/views/kaminari/alchemy/_gap.html.erb +1 -1
- data/app/views/kaminari/alchemy/_last_page.html.erb +4 -2
- data/app/views/kaminari/alchemy/_next_page.html.erb +4 -2
- data/app/views/kaminari/alchemy/_prev_page.html.erb +4 -2
- data/app/views/layouts/alchemy/admin.html.erb +10 -29
- data/config/alchemy/modules.yml +30 -30
- data/config/importmap.rb +10 -1
- data/config/initializers/rails_live_reload.rb +13 -0
- data/config/locales/alchemy.en.yml +23 -9
- data/config/routes.rb +2 -1
- data/lib/alchemy/auth_accessors.rb +6 -1
- data/lib/alchemy/dev_support/live_reload_watcher.rb +5 -0
- data/lib/alchemy/engine.rb +8 -2
- data/lib/alchemy/forms/builder.rb +18 -12
- data/lib/alchemy/resources_helper.rb +3 -3
- data/lib/alchemy/test_support/capybara_helpers.rb +8 -5
- data/lib/alchemy/test_support/rspec_matchers.rb +14 -0
- data/lib/alchemy/test_support/shared_uploader_examples.rb +1 -1
- data/lib/alchemy/tinymce.rb +8 -3
- data/lib/alchemy/version.rb +1 -1
- data/package.json +14 -5
- data/vendor/assets/fonts/remixicon.eot +0 -0
- data/vendor/assets/fonts/remixicon.svg +7816 -0
- data/vendor/assets/fonts/remixicon.ttf +0 -0
- data/vendor/assets/fonts/remixicon.woff +0 -0
- data/vendor/assets/fonts/remixicon.woff2 +0 -0
- data/vendor/assets/stylesheets/remixicon.scss +10480 -0
- metadata +83 -88
- data/app/assets/javascripts/alchemy/alchemy.autocomplete.js.coffee +0 -30
- data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +0 -53
- data/app/assets/javascripts/alchemy/alchemy.buttons.js.coffee +0 -45
- data/app/assets/javascripts/alchemy/alchemy.char_counter.js.coffee +0 -19
- data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +0 -59
- data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +0 -79
- data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +0 -267
- data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +0 -27
- data/app/assets/javascripts/alchemy/alchemy.spinner.js +0 -32
- data/app/assets/javascripts/alchemy/alchemy.tooltips.coffee +0 -10
- data/app/assets/javascripts/alchemy/alchemy.uploader.js.coffee +0 -131
- data/app/assets/javascripts/alchemy/menubar.js.coffee +0 -8
- data/app/assets/javascripts/alchemy/node_select.js +0 -39
- data/app/assets/javascripts/alchemy/page_select.js +0 -46
- data/app/assets/javascripts/alchemy/templates/node.hbs +0 -16
- data/app/assets/javascripts/alchemy/templates/spinner.hbs +0 -7
- data/app/assets/stylesheets/alchemy/jquery-ui.scss +0 -435
- data/app/javascript/alchemy_admin/datepicker.js +0 -33
- data/app/javascript/alchemy_admin/tinymce.js +0 -146
- data/app/javascript/alchemy_admin/translations.js +0 -32
- data/app/views/alchemy/admin/elements/fold.js.erb +0 -33
- data/app/views/alchemy/admin/elements/order.js.erb +0 -11
- data/app/views/alchemy/admin/elements/publish.js.erb +0 -21
- data/app/views/alchemy/admin/elements/update.js.erb +0 -27
- data/vendor/assets/fonts/fa-regular-400.eot +0 -0
- data/vendor/assets/fonts/fa-regular-400.svg +0 -803
- data/vendor/assets/fonts/fa-regular-400.ttf +0 -0
- data/vendor/assets/fonts/fa-regular-400.woff +0 -0
- data/vendor/assets/fonts/fa-regular-400.woff2 +0 -0
- data/vendor/assets/fonts/fa-solid-900.eot +0 -0
- data/vendor/assets/fonts/fa-solid-900.svg +0 -4938
- data/vendor/assets/fonts/fa-solid-900.ttf +0 -0
- data/vendor/assets/fonts/fa-solid-900.woff +0 -0
- data/vendor/assets/fonts/fa-solid-900.woff2 +0 -0
- data/vendor/assets/javascripts/fileupload/jquery.fileupload-process.js +0 -178
- data/vendor/assets/javascripts/fileupload/jquery.fileupload-validate.js +0 -125
- data/vendor/assets/javascripts/fileupload/jquery.fileupload.js +0 -1502
- data/vendor/assets/javascripts/fileupload/jquery.iframe-transport.js +0 -224
- data/vendor/assets/javascripts/jquery-ui/data.js +0 -45
- data/vendor/assets/javascripts/jquery-ui/ie.js +0 -20
- data/vendor/assets/javascripts/jquery-ui/keycode.js +0 -51
- data/vendor/assets/javascripts/jquery-ui/plugin.js +0 -49
- data/vendor/assets/javascripts/jquery-ui/safe-active-element.js +0 -46
- data/vendor/assets/javascripts/jquery-ui/safe-blur.js +0 -27
- data/vendor/assets/javascripts/jquery-ui/scroll-parent.js +0 -50
- data/vendor/assets/javascripts/jquery-ui/unique-id.js +0 -54
- data/vendor/assets/javascripts/jquery-ui/version.js +0 -20
- data/vendor/assets/javascripts/jquery-ui/widget.js +0 -754
- data/vendor/assets/javascripts/jquery-ui/widgets/draggable.js +0 -1268
- data/vendor/assets/javascripts/jquery-ui/widgets/mouse.js +0 -241
- data/vendor/assets/javascripts/jquery-ui/widgets/sortable.js +0 -1623
- data/vendor/assets/javascripts/jquery-ui/widgets/tabs.js +0 -931
- data/vendor/assets/javascripts/jquery_plugins/jquery.scrollTo.min.js +0 -7
- data/vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js +0 -296
- data/vendor/assets/stylesheets/fontawesome/_animated.scss +0 -20
- data/vendor/assets/stylesheets/fontawesome/_bordered-pulled.scss +0 -20
- data/vendor/assets/stylesheets/fontawesome/_core.scss +0 -21
- data/vendor/assets/stylesheets/fontawesome/_fixed-width.scss +0 -6
- data/vendor/assets/stylesheets/fontawesome/_icons.scss +0 -1441
- data/vendor/assets/stylesheets/fontawesome/_larger.scss +0 -23
- data/vendor/assets/stylesheets/fontawesome/_list.scss +0 -18
- data/vendor/assets/stylesheets/fontawesome/_mixins.scss +0 -56
- data/vendor/assets/stylesheets/fontawesome/_rotated-flipped.scss +0 -24
- data/vendor/assets/stylesheets/fontawesome/_screen-reader.scss +0 -5
- data/vendor/assets/stylesheets/fontawesome/_stacked.scss +0 -31
- data/vendor/assets/stylesheets/fontawesome/_variables.scss +0 -1458
- data/vendor/assets/stylesheets/fontawesome/fontawesome.scss +0 -16
- data/vendor/assets/stylesheets/fontawesome/regular.scss +0 -23
- data/vendor/assets/stylesheets/fontawesome/solid.scss +0 -24
@@ -3,7 +3,7 @@
|
|
3
3
|
module Alchemy
|
4
4
|
module Admin
|
5
5
|
class ElementsController < Alchemy::Admin::BaseController
|
6
|
-
before_action :load_element, only: [:update, :destroy, :
|
6
|
+
before_action :load_element, only: [:update, :destroy, :collapse, :expand, :publish]
|
7
7
|
authorize_resource class: Alchemy::Element
|
8
8
|
|
9
9
|
def index
|
@@ -53,13 +53,25 @@ module Alchemy
|
|
53
53
|
# Updates the element and all ingredients in the element.
|
54
54
|
#
|
55
55
|
def update
|
56
|
-
@page = @element.page
|
57
|
-
|
58
56
|
if @element.update(element_params)
|
59
|
-
|
57
|
+
render json: {
|
58
|
+
notice: Alchemy.t(:element_saved),
|
59
|
+
previewText: Rails::Html::SafeListSanitizer.new.sanitize(@element.preview_text),
|
60
|
+
ingredientAnchors: @element.ingredients.select { |i| i.settings[:anchor] }.map do |ingredient|
|
61
|
+
{
|
62
|
+
ingredientId: ingredient.id,
|
63
|
+
active: ingredient.dom_id.present?
|
64
|
+
}
|
65
|
+
end
|
66
|
+
}
|
60
67
|
else
|
61
|
-
|
62
|
-
|
68
|
+
@warning = Alchemy.t("Validation failed")
|
69
|
+
render json: {
|
70
|
+
warning: @warning,
|
71
|
+
errorMessage: Alchemy.t(:ingredient_validations_headline),
|
72
|
+
ingredientsWithErrors: @element.ingredients_with_errors.map(&:id),
|
73
|
+
errors: @element.ingredient_error_messages
|
74
|
+
}
|
63
75
|
end
|
64
76
|
end
|
65
77
|
|
@@ -70,37 +82,59 @@ module Alchemy
|
|
70
82
|
end
|
71
83
|
|
72
84
|
def publish
|
73
|
-
@element.
|
85
|
+
@element.public = !@element.public?
|
86
|
+
@element.save(validate: false)
|
87
|
+
render json: {
|
88
|
+
public: @element.public?,
|
89
|
+
label: @element.public? ? Alchemy.t(:hide_element) : Alchemy.t(:show_element)
|
90
|
+
}
|
74
91
|
end
|
75
92
|
|
76
93
|
def order
|
77
|
-
@
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
position: position
|
85
|
-
)
|
86
|
-
end
|
87
|
-
# Need to manually touch the parent because Rails does not do it
|
88
|
-
# with the update_columns above
|
89
|
-
@parent_element&.touch
|
94
|
+
@element = Element.find(params[:element_id])
|
95
|
+
@element.update(
|
96
|
+
parent_element_id: params[:parent_element_id],
|
97
|
+
position: params[:position]
|
98
|
+
)
|
99
|
+
if params[:parent_element_id].present?
|
100
|
+
@parent_element = Element.find_by(id: params[:parent_element_id])
|
90
101
|
end
|
102
|
+
|
103
|
+
render json: {
|
104
|
+
message: Alchemy.t(:successfully_saved_element_position),
|
105
|
+
preview_text: @element.preview_text
|
106
|
+
}
|
91
107
|
end
|
92
108
|
|
93
|
-
#
|
109
|
+
# Collapses the element, all nested elements and persists the state in the db
|
94
110
|
#
|
95
|
-
def
|
96
|
-
@page = @element.page
|
111
|
+
def collapse
|
97
112
|
# We do not want to trigger the touch callback or any validations
|
98
|
-
@element.update_columns(folded:
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
113
|
+
@element.update_columns(folded: true)
|
114
|
+
# Collapse all nested elements
|
115
|
+
nested_elements_ids = collapse_nested_elements_ids(@element)
|
116
|
+
Alchemy::Element.where(id: nested_elements_ids).update_all(folded: true)
|
117
|
+
|
118
|
+
render json: {
|
119
|
+
nestedElementIds: nested_elements_ids,
|
120
|
+
title: Alchemy.t(@element.folded? ? :show_element_content : :hide_element_content)
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
# Expands the element, all parents and persists the state in the db
|
125
|
+
#
|
126
|
+
def expand
|
127
|
+
# We do not want to trigger the touch callback or any validations
|
128
|
+
@element.update_columns(folded: false)
|
129
|
+
# We want to expand the upper most parent first in order to prevent
|
130
|
+
# re-painting issues in the browser
|
131
|
+
parent_element_ids = @element.parent_element_ids.reverse
|
132
|
+
Alchemy::Element.where(id: parent_element_ids).update_all(folded: false)
|
133
|
+
|
134
|
+
render json: {
|
135
|
+
parentElementIds: parent_element_ids,
|
136
|
+
title: Alchemy.t(@element.folded? ? :show_element_content : :hide_element_content)
|
137
|
+
}
|
104
138
|
end
|
105
139
|
|
106
140
|
private
|
@@ -171,12 +205,6 @@ module Alchemy
|
|
171
205
|
def create_element_params
|
172
206
|
params.require(:element).permit(:name, :page_version_id, :parent_element_id)
|
173
207
|
end
|
174
|
-
|
175
|
-
def element_update_error
|
176
|
-
@element_validated = false
|
177
|
-
@notice = Alchemy.t("Validation failed")
|
178
|
-
@error_message = "<h2>#{@notice}</h2><p>#{Alchemy.t(:ingredient_validations_headline)}</p>".html_safe
|
179
|
-
end
|
180
208
|
end
|
181
209
|
end
|
182
210
|
end
|
@@ -53,7 +53,9 @@ module Alchemy
|
|
53
53
|
#{current_alchemy_user.inspect}
|
54
54
|
WARN
|
55
55
|
end
|
56
|
-
if
|
56
|
+
if request.format.json?
|
57
|
+
render json: {message: Alchemy.t("You are not authorized")}, status: :unauthorized
|
58
|
+
elsif current_alchemy_user
|
57
59
|
handle_redirect_for_user
|
58
60
|
else
|
59
61
|
handle_redirect_for_guest
|
@@ -65,7 +67,7 @@ module Alchemy
|
|
65
67
|
if can?(:index, :alchemy_admin_dashboard)
|
66
68
|
redirect_or_render_notice
|
67
69
|
else
|
68
|
-
redirect_to
|
70
|
+
redirect_to Alchemy.unauthorized_path
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
@@ -12,7 +12,7 @@ module Alchemy
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def enforce_primary_host_for_site
|
15
|
-
redirect_to url_for(host: current_alchemy_site.host), status: :moved_permanently
|
15
|
+
redirect_to url_for(host: current_alchemy_site.host), status: :moved_permanently, allow_other_host: true
|
16
16
|
end
|
17
17
|
|
18
18
|
def needs_redirect_to_primary_host?
|
@@ -4,17 +4,18 @@ module Alchemy
|
|
4
4
|
module Admin
|
5
5
|
module AttachmentsHelper
|
6
6
|
include Alchemy::Admin::BaseHelper
|
7
|
+
include Alchemy::Filetypes
|
7
8
|
|
8
9
|
def mime_to_human(mime)
|
9
10
|
Alchemy.t(mime, scope: "mime_types", default: Alchemy.t(:document))
|
10
11
|
end
|
11
12
|
|
12
13
|
def attachment_preview_size(attachment)
|
13
|
-
case attachment.
|
14
|
-
when
|
15
|
-
when
|
16
|
-
when
|
17
|
-
when "pdf" then "
|
14
|
+
case attachment.file_mime_type
|
15
|
+
when *IMAGE_FILE_TYPES then "600x475"
|
16
|
+
when *AUDIO_FILE_TYPES then "600x190"
|
17
|
+
when *VIDEO_FILE_TYPES then "600x485"
|
18
|
+
when "application/pdf" then "600x600"
|
18
19
|
else
|
19
20
|
"600x145"
|
20
21
|
end
|
@@ -55,7 +55,10 @@ module Alchemy
|
|
55
55
|
default_options = {modal: true}
|
56
56
|
options = default_options.merge(options)
|
57
57
|
link_to content, url,
|
58
|
-
html_options.merge(
|
58
|
+
html_options.merge(
|
59
|
+
"data-dialog-options" => options.to_json,
|
60
|
+
:is => "alchemy-dialog-link"
|
61
|
+
)
|
59
62
|
end
|
60
63
|
|
61
64
|
# Used for translations selector in Alchemy cockpit user settings.
|
@@ -189,7 +192,7 @@ module Alchemy
|
|
189
192
|
options = {
|
190
193
|
title: Alchemy.t("Delete"),
|
191
194
|
message: Alchemy.t("Are you sure?"),
|
192
|
-
icon:
|
195
|
+
icon: "delete-bin-2"
|
193
196
|
}.merge(options)
|
194
197
|
button_with_confirm(
|
195
198
|
render_icon(options[:icon]),
|
@@ -322,18 +325,20 @@ module Alchemy
|
|
322
325
|
date = Time.zone.parse(date) if date.is_a?(String)
|
323
326
|
value = date&.iso8601
|
324
327
|
|
325
|
-
text_field object.class.name.demodulize.underscore.to_sym,
|
326
|
-
method.to_sym, {:
|
328
|
+
input_field = text_field object.class.name.demodulize.underscore.to_sym,
|
329
|
+
method.to_sym, {type: "text", class: type, value: value}.merge(html_options)
|
330
|
+
|
331
|
+
content_tag("alchemy-datepicker", input_field, "input-type" => type)
|
327
332
|
end
|
328
333
|
|
329
334
|
# Render a hint icon with tooltip for given object.
|
330
335
|
# The model class needs to include the hints module
|
331
|
-
def render_hint_for(element)
|
336
|
+
def render_hint_for(element, icon_options = {})
|
332
337
|
return unless element.has_hint?
|
333
338
|
|
334
|
-
content_tag
|
335
|
-
render_icon("question
|
336
|
-
content_tag(:span, element.hint.html_safe,
|
339
|
+
content_tag "sl-tooltip", class: "like-hint-tooltip", placement: "bottom-start" do
|
340
|
+
render_icon("question", icon_options) +
|
341
|
+
content_tag(:span, element.hint.html_safe, slot: "content")
|
337
342
|
end
|
338
343
|
end
|
339
344
|
|
@@ -372,12 +377,12 @@ module Alchemy
|
|
372
377
|
# <%= hint_with_tooltip('Page layout is missing', icon: 'info') %>
|
373
378
|
#
|
374
379
|
# @param text [String] - The text displayed in the tooltip
|
375
|
-
# @param icon: '
|
380
|
+
# @param icon: 'alert' [String] - Icon name
|
376
381
|
#
|
377
382
|
# @return [String]
|
378
|
-
def hint_with_tooltip(text, icon: "
|
379
|
-
content_tag :
|
380
|
-
render_icon(icon)
|
383
|
+
def hint_with_tooltip(text, icon: "alert")
|
384
|
+
content_tag :"sl-tooltip", class: "like-hint-tooltip", content: text, placement: "bottom" do
|
385
|
+
render_icon(icon)
|
381
386
|
end
|
382
387
|
end
|
383
388
|
|
@@ -34,7 +34,10 @@ module Alchemy
|
|
34
34
|
# Renders the label and hint for a ingredient.
|
35
35
|
def ingredient_label(ingredient, column = :value, html_options = {})
|
36
36
|
label_tag ingredient.form_field_id(column), html_options do
|
37
|
-
[
|
37
|
+
[
|
38
|
+
render_ingredient_role(ingredient),
|
39
|
+
render_hint_for(ingredient, size: "lg", fixed_width: false)
|
40
|
+
].compact.join(" ").html_safe
|
38
41
|
end
|
39
42
|
end
|
40
43
|
end
|
@@ -36,21 +36,15 @@ module Alchemy
|
|
36
36
|
|
37
37
|
def page_status_checkbox(page, attribute)
|
38
38
|
label = page.class.human_attribute_name(attribute)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
hint = content_tag(:span, class: "hint-bubble") do
|
43
|
-
Alchemy.t(:attribute_fixed, attribute: attribute)
|
44
|
-
end
|
45
|
-
content = content_tag(:span, class: "with-hint") do
|
46
|
-
"#{checkbox}\n#{label}\n#{hint}".html_safe
|
39
|
+
checkbox = if page.attribute_fixed?(attribute)
|
40
|
+
content_tag("sl-tooltip", class: "like-hint-tooltip", content: Alchemy.t(:attribute_fixed, attribute: attribute), placement: "bottom-start") do
|
41
|
+
check_box_tag("page[#{attribute}]", "1", page.send(attribute), disabled: true)
|
47
42
|
end
|
48
43
|
else
|
49
|
-
|
50
|
-
content = "#{checkbox}\n#{label}".html_safe
|
44
|
+
check_box(:page, attribute)
|
51
45
|
end
|
52
46
|
|
53
|
-
content_tag(:label, class: "checkbox") {
|
47
|
+
content_tag(:label, class: "checkbox") { "#{checkbox}\n#{label}".html_safe }
|
54
48
|
end
|
55
49
|
end
|
56
50
|
end
|
@@ -19,21 +19,21 @@ module Alchemy
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
# Render a
|
22
|
+
# Render a Remix icon
|
23
23
|
#
|
24
|
-
# @param
|
25
|
-
# @
|
26
|
-
# @
|
24
|
+
# @param icon_name [String] icon name
|
25
|
+
# @option options - style: nil [String] icon style. line or fill
|
26
|
+
# @option options - size: nil [String] icon size
|
27
27
|
#
|
28
28
|
# @return [String]
|
29
|
-
def render_icon(
|
30
|
-
options = {style: "
|
29
|
+
def render_icon(icon_name, options = {})
|
30
|
+
options = {style: "line", fixed_width: true}.merge(options)
|
31
|
+
style = options[:style] && "-#{options[:style]}"
|
31
32
|
classes = [
|
32
|
-
"icon
|
33
|
-
"
|
34
|
-
"
|
35
|
-
options[:
|
36
|
-
options[:transform] ? "fa-#{options[:transform]}" : nil,
|
33
|
+
"icon",
|
34
|
+
"ri-#{ri_icon(icon_name)}#{style}",
|
35
|
+
options[:size] ? "ri-#{options[:size]}" : nil,
|
36
|
+
options[:fixed_width] ? "ri-fw" : nil,
|
37
37
|
options[:class]
|
38
38
|
].compact
|
39
39
|
content_tag("i", nil, class: classes)
|
@@ -87,18 +87,52 @@ module Alchemy
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
# Returns the
|
90
|
+
# Returns the icon name for given message type
|
91
91
|
#
|
92
92
|
# @param message_type [String] The message type. One of +warning+, +info+, +notice+, +error+
|
93
|
-
# @return [String] The
|
93
|
+
# @return [String] The icon name
|
94
94
|
def message_icon_class(message_type)
|
95
95
|
case message_type.to_s
|
96
96
|
when "warning", "warn", "alert" then "exclamation"
|
97
97
|
when "notice" then "check"
|
98
98
|
when "error" then "bug"
|
99
|
+
when "hint" then "info"
|
99
100
|
else
|
100
101
|
message_type
|
101
102
|
end
|
102
103
|
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Returns the Remix icon name for given icon name
|
108
|
+
#
|
109
|
+
# @param icon_name [String] The icon name.
|
110
|
+
# @return [String] The Remix icon class
|
111
|
+
def ri_icon(icon_name)
|
112
|
+
case icon_name.to_s
|
113
|
+
when "minus", "remove", "delete"
|
114
|
+
"delete-bin-2"
|
115
|
+
when "plus"
|
116
|
+
"add"
|
117
|
+
when "copy"
|
118
|
+
"file-copy"
|
119
|
+
when "download"
|
120
|
+
"download-2"
|
121
|
+
when "upload"
|
122
|
+
"upload-2"
|
123
|
+
when "exclamation"
|
124
|
+
"alert"
|
125
|
+
when "info-circle", "info"
|
126
|
+
"information"
|
127
|
+
when "times"
|
128
|
+
"close"
|
129
|
+
when "tag"
|
130
|
+
"price-tag-3"
|
131
|
+
when "cog"
|
132
|
+
"settings-3"
|
133
|
+
else
|
134
|
+
icon_name
|
135
|
+
end
|
136
|
+
end
|
103
137
|
end
|
104
138
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import { toCamelCase } from "alchemy_admin/utils/string_conversions"
|
2
|
+
|
3
|
+
export class AlchemyHTMLElement extends HTMLElement {
|
4
|
+
static properties = {}
|
5
|
+
|
6
|
+
/**
|
7
|
+
* create the list of observed attributes
|
8
|
+
* this function is a requirement for the `attributeChangedCallback` - method
|
9
|
+
* @returns {string[]}
|
10
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference
|
11
|
+
*/
|
12
|
+
static get observedAttributes() {
|
13
|
+
return Object.keys(this.properties)
|
14
|
+
}
|
15
|
+
|
16
|
+
constructor(options = {}) {
|
17
|
+
super()
|
18
|
+
|
19
|
+
this.options = options
|
20
|
+
this.changeComponent = true
|
21
|
+
this.initialContent = this.innerHTML // store the inner content of the component
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* run when the component will be initialized by the Browser
|
26
|
+
* this is a default function
|
27
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference
|
28
|
+
*/
|
29
|
+
connectedCallback() {
|
30
|
+
// parse the properties object and register property with the default values
|
31
|
+
Object.keys(this.constructor.properties).forEach((name) => {
|
32
|
+
// if the options was given via the constructor, they should be prefer (e.g. new <WebComponentName>({title: "Foo"}))
|
33
|
+
this[name] =
|
34
|
+
this.options[name] ?? this.constructor.properties[name].default
|
35
|
+
})
|
36
|
+
|
37
|
+
// then process the attributes
|
38
|
+
this.getAttributeNames().forEach((name) => this._updateFromAttribute(name))
|
39
|
+
|
40
|
+
// render the component
|
41
|
+
this._updateComponent()
|
42
|
+
this.connected()
|
43
|
+
}
|
44
|
+
|
45
|
+
/**
|
46
|
+
* disconnected callback if the component is removed from the DOM
|
47
|
+
* this is currently only a Proxy to the disconnected - callback to use the same callback structure
|
48
|
+
* as for the connected - callback
|
49
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference
|
50
|
+
*/
|
51
|
+
disconnectedCallback() {
|
52
|
+
this.disconnected()
|
53
|
+
}
|
54
|
+
|
55
|
+
/**
|
56
|
+
* triggered by the browser, if one of the observed attributes is changing
|
57
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Components#reference
|
58
|
+
*/
|
59
|
+
attributeChangedCallback(name) {
|
60
|
+
this._updateFromAttribute(name)
|
61
|
+
this._updateComponent()
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* a connected method to make it easier to overwrite the connection callback
|
66
|
+
*/
|
67
|
+
connected() {}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* a disconnected method to make it easier to overwrite the disconnection callback
|
71
|
+
*/
|
72
|
+
disconnected() {}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* empty method container to allow the child component to put the rendered string into this method
|
76
|
+
* @returns {String}
|
77
|
+
*/
|
78
|
+
render() {
|
79
|
+
return this.initialContent
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* after render callback
|
84
|
+
* the function will be triggered after the DOM was updated
|
85
|
+
*/
|
86
|
+
afterRender() {}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Dispatches a custom event with given name
|
90
|
+
* @param {string} name The name of the custom event
|
91
|
+
* @param {object} detail Optional event details
|
92
|
+
*/
|
93
|
+
dispatchCustomEvent(name, detail = {}) {
|
94
|
+
const event = new CustomEvent(`Alchemy.${name}`, { bubbles: true, detail })
|
95
|
+
this.dispatchEvent(event)
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* (re)render the component content inside the component container
|
100
|
+
* @private
|
101
|
+
*/
|
102
|
+
_updateComponent() {
|
103
|
+
if (this.changeComponent) {
|
104
|
+
this.innerHTML = this.render()
|
105
|
+
this.changeComponent = false
|
106
|
+
this.afterRender()
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* update the value from the given attribute
|
112
|
+
*
|
113
|
+
* @param {string} name
|
114
|
+
* @private
|
115
|
+
*/
|
116
|
+
_updateFromAttribute(name) {
|
117
|
+
const attributeValue = this.getAttribute(name)
|
118
|
+
const propertyName = toCamelCase(name)
|
119
|
+
const isBooleanValue =
|
120
|
+
attributeValue.length === 0 || attributeValue === "true"
|
121
|
+
|
122
|
+
const value = isBooleanValue ? true : attributeValue
|
123
|
+
|
124
|
+
if (this[propertyName] !== value) {
|
125
|
+
this[propertyName] = value
|
126
|
+
this.changeComponent = true
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import Spinner from "../spinner"
|
2
|
+
|
3
|
+
class Button extends HTMLButtonElement {
|
4
|
+
connectedCallback() {
|
5
|
+
if (this.form) {
|
6
|
+
this.form.addEventListener("submit", this)
|
7
|
+
|
8
|
+
if (this.form.dataset.remote == "true") {
|
9
|
+
this.form.addEventListener("ajax:complete", this)
|
10
|
+
}
|
11
|
+
} else {
|
12
|
+
console.warn("No form for button found!", this)
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
handleEvent(event) {
|
17
|
+
switch (event.type) {
|
18
|
+
case "submit":
|
19
|
+
const isDisabled = this.getAttribute("disabled") === "disabled"
|
20
|
+
|
21
|
+
if (isDisabled) {
|
22
|
+
event.preventDefault()
|
23
|
+
event.stopPropagation()
|
24
|
+
} else {
|
25
|
+
this.disable()
|
26
|
+
}
|
27
|
+
break
|
28
|
+
case "ajax:complete":
|
29
|
+
this.enable()
|
30
|
+
break
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
disable() {
|
35
|
+
const spinner = new Spinner("small")
|
36
|
+
const rect = this.getBoundingClientRect()
|
37
|
+
|
38
|
+
this.dataset.initialButtonText = this.innerHTML
|
39
|
+
this.setAttribute("disabled", "disabled")
|
40
|
+
this.setAttribute("tabindex", "-1")
|
41
|
+
this.classList.add("disabled")
|
42
|
+
this.style.width = `${rect.width}px`
|
43
|
+
this.style.height = `${rect.height}px`
|
44
|
+
this.innerHTML = " "
|
45
|
+
|
46
|
+
spinner.spin(this)
|
47
|
+
}
|
48
|
+
|
49
|
+
enable() {
|
50
|
+
this.classList.remove("disabled")
|
51
|
+
this.removeAttribute("disabled")
|
52
|
+
this.removeAttribute("tabindex")
|
53
|
+
this.style.width = null
|
54
|
+
this.style.height = null
|
55
|
+
this.innerHTML = this.dataset.initialButtonText
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
customElements.define("alchemy-button", Button, { extends: "button" })
|
@@ -0,0 +1,40 @@
|
|
1
|
+
/**
|
2
|
+
* Show the character counter below input fields and textareas
|
3
|
+
*/
|
4
|
+
import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
|
5
|
+
import { translate } from "alchemy_admin/i18n"
|
6
|
+
|
7
|
+
class CharCounter extends AlchemyHTMLElement {
|
8
|
+
static properties = {
|
9
|
+
maxChars: { default: 60 }
|
10
|
+
}
|
11
|
+
connected() {
|
12
|
+
this.translation = translate("allowed_chars", this.maxChars)
|
13
|
+
this.formField = this.getFormField()
|
14
|
+
|
15
|
+
if (this.formField) {
|
16
|
+
this.createDisplayElement()
|
17
|
+
this.countCharacters()
|
18
|
+
this.formField.addEventListener("keyup", () => this.countCharacters()) // add arrow function to get a implicit this - binding
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
getFormField() {
|
23
|
+
const formFields = this.querySelectorAll("input, textarea")
|
24
|
+
return formFields.length > 0 ? formFields[0] : undefined
|
25
|
+
}
|
26
|
+
|
27
|
+
createDisplayElement() {
|
28
|
+
this.display = document.createElement("small")
|
29
|
+
this.display.className = "alchemy-char-counter"
|
30
|
+
this.formField.after(this.display)
|
31
|
+
}
|
32
|
+
|
33
|
+
countCharacters() {
|
34
|
+
const charLength = this.formField.value.length
|
35
|
+
this.display.textContent = `${charLength} ${this.translation}`
|
36
|
+
this.display.classList.toggle("too-long", charLength > this.maxChars)
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
customElements.define("alchemy-char-counter", CharCounter)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
|
2
|
+
import { translate, currentLocale } from "alchemy_admin/i18n"
|
3
|
+
import flatpickr from "flatpickr"
|
4
|
+
|
5
|
+
class Datepicker extends AlchemyHTMLElement {
|
6
|
+
static properties = {
|
7
|
+
inputType: { default: "date" }
|
8
|
+
}
|
9
|
+
|
10
|
+
constructor() {
|
11
|
+
super()
|
12
|
+
this.flatpickr = undefined
|
13
|
+
}
|
14
|
+
|
15
|
+
afterRender() {
|
16
|
+
const options = {
|
17
|
+
// alchemy_i18n supports `zh_CN` etc., but flatpickr only has two-letter codes (`zh`)
|
18
|
+
locale: currentLocale().slice(0, 2),
|
19
|
+
altInput: true,
|
20
|
+
altFormat: translate(`formats.${this.inputType}`),
|
21
|
+
altInputClass: "flatpickr-input",
|
22
|
+
dateFormat: "Z",
|
23
|
+
enableTime: /time/.test(this.inputType),
|
24
|
+
noCalendar: this.inputType === "time",
|
25
|
+
time_24hr: translate("formats.time_24hr"),
|
26
|
+
onValueUpdate(_selectedDates, _dateStr, instance) {
|
27
|
+
instance.element.closest("alchemy-element-editor")?.setDirty()
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
this.flatpickr = flatpickr(this.getElementsByTagName("input")[0], options)
|
32
|
+
}
|
33
|
+
|
34
|
+
disconnected() {
|
35
|
+
this.flatpickr.destroy()
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
customElements.define("alchemy-datepicker", Datepicker)
|