alchemy_cms 8.3.10 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +58 -10
- data/app/assets/builds/alchemy/admin/page-select.css +1 -1
- data/app/assets/builds/alchemy/admin/print.css +1 -1
- data/app/assets/builds/alchemy/admin/remote-select.css +1 -0
- data/app/assets/builds/alchemy/admin/tags-autocomplete.css +1 -0
- data/app/assets/builds/alchemy/admin.css +4 -4
- data/app/assets/builds/alchemy/alchemy_admin.min.js +1 -1
- data/app/assets/builds/alchemy/alchemy_admin.min.js.map +1 -1
- data/app/assets/builds/alchemy/dark-theme.css +1 -1
- data/app/assets/builds/alchemy/light-theme.css +1 -1
- data/app/assets/builds/alchemy/preview.min.js +1 -1
- data/app/assets/builds/alchemy/theme.css +1 -1
- data/app/assets/builds/alchemy/welcome.css +1 -1
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
- data/app/assets/builds/tinymce/skins/content/alchemy-dark/content.min.css +1 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy-dark/skin.min.css +1 -1
- data/app/assets/images/alchemy/icons-sprite.svg +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/greeting.html.erb +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/news_reader.html.erb +38 -0
- data/app/components/alchemy/admin/dashboard/widgets/news_reader.rb +54 -0
- data/app/components/alchemy/admin/dashboard/widgets/online_users.html.erb +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/recent_pages.html.erb +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/system_info.html.erb +11 -3
- data/app/components/alchemy/admin/dashboard/widgets/system_info.rb +6 -1
- data/app/components/alchemy/admin/element_editor.html.erb +1 -1
- data/app/components/alchemy/admin/element_select.rb +30 -20
- data/app/components/alchemy/admin/element_status_indicators.html.erb +16 -0
- data/app/components/alchemy/admin/element_status_indicators.rb +31 -0
- data/app/components/alchemy/admin/link_dialog/external_tab.rb +2 -1
- data/app/components/alchemy/admin/main_navigation/entry.rb +61 -0
- data/app/components/alchemy/admin/main_navigation/menu.rb +21 -0
- data/app/components/alchemy/admin/main_navigation/sub_navigation.rb +31 -0
- data/app/components/alchemy/admin/page_node.html.erb +1 -1
- data/app/components/alchemy/admin/page_permitted_roles_select.html.erb +11 -0
- data/app/components/alchemy/admin/page_permitted_roles_select.rb +35 -0
- data/app/components/alchemy/admin/page_select.rb +18 -8
- data/app/components/alchemy/admin/preview_time_select.rb +1 -1
- data/app/components/alchemy/admin/toolbar_button.rb +43 -20
- data/app/components/alchemy/admin/uploader_button.rb +66 -0
- data/app/components/alchemy/ingredients/base_editor.rb +10 -1
- data/app/components/alchemy/ingredients/boolean_editor.rb +1 -0
- data/app/components/alchemy/ingredients/color_editor.rb +6 -3
- data/app/components/alchemy/ingredients/datetime_view.rb +1 -1
- data/app/components/alchemy/ingredients/picture_view.rb +2 -4
- data/app/components/alchemy/menubar.html.erb +157 -0
- data/app/components/alchemy/menubar.rb +15 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +12 -9
- data/app/controllers/alchemy/admin/base_controller.rb +34 -21
- data/app/controllers/alchemy/admin/dashboard_controller.rb +4 -0
- data/app/controllers/alchemy/admin/elements_controller.rb +4 -23
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/nodes_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pictures_controller.rb +6 -3
- data/app/controllers/alchemy/admin/resources_controller.rb +5 -5
- data/app/controllers/alchemy/admin/sites_controller.rb +1 -1
- data/app/controllers/alchemy/base_controller.rb +24 -16
- data/app/controllers/alchemy/pages_controller.rb +29 -8
- data/app/controllers/alchemy/sitemap_controller.rb +58 -0
- data/app/controllers/concerns/alchemy/admin/csp_protection.rb +43 -0
- data/app/controllers/concerns/alchemy/admin/current_language.rb +1 -1
- data/app/controllers/concerns/alchemy/admin/resource_filter.rb +3 -1
- data/{lib → app/controllers/concerns}/alchemy/controller_actions.rb +13 -0
- data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +4 -4
- data/app/helpers/alchemy/admin/base_helper.rb +4 -1
- data/app/helpers/alchemy/admin/form_helper.rb +0 -2
- data/app/helpers/alchemy/admin/navigation_helper.rb +10 -181
- data/app/helpers/alchemy/elements_block_helper.rb +14 -0
- data/app/helpers/alchemy/resources_helper.rb +15 -1
- data/app/javascript/alchemy_admin/components/attachment_select.js +11 -20
- data/app/javascript/alchemy_admin/components/auto_submit.js +2 -4
- data/app/javascript/alchemy_admin/components/button.js +0 -6
- data/app/javascript/alchemy_admin/components/clear_schedule_button.js +21 -0
- data/app/javascript/alchemy_admin/components/color_select.js +50 -45
- data/app/javascript/alchemy_admin/components/conditional_field.js +50 -0
- data/app/javascript/alchemy_admin/components/dirty_guard.js +77 -0
- data/app/javascript/alchemy_admin/components/dom_id_select.js +17 -10
- data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +7 -2
- data/app/javascript/alchemy_admin/components/element_editor.js +29 -56
- data/app/javascript/alchemy_admin/components/element_select.js +26 -66
- data/app/javascript/alchemy_admin/components/icon.js +9 -4
- data/app/javascript/alchemy_admin/{image_cropper.js → components/image_cropper.js} +39 -53
- data/app/javascript/alchemy_admin/components/image_overlay_link.js +14 -0
- data/app/javascript/alchemy_admin/components/index.js +12 -1
- data/app/javascript/alchemy_admin/components/main_navi_entry.js +75 -0
- data/app/javascript/alchemy_admin/components/menu_node.js +73 -0
- data/app/javascript/alchemy_admin/components/news_reader.js +109 -0
- data/app/javascript/alchemy_admin/components/node_folder.js +36 -0
- data/app/javascript/alchemy_admin/components/node_form.js +34 -0
- data/app/javascript/alchemy_admin/components/node_select.js +25 -30
- data/app/javascript/alchemy_admin/components/node_tree.js +56 -0
- data/app/javascript/alchemy_admin/components/page_node.js +2 -1
- data/app/javascript/alchemy_admin/components/page_select.js +16 -34
- data/app/javascript/alchemy_admin/components/picture_archive.js +92 -0
- data/app/javascript/alchemy_admin/components/picture_editor.js +2 -1
- data/app/javascript/alchemy_admin/components/preview_window.js +11 -3
- data/app/javascript/alchemy_admin/components/remote_select.js +461 -81
- data/app/javascript/alchemy_admin/components/select.js +26 -88
- data/app/javascript/alchemy_admin/components/sitemap.js +2 -1
- data/app/javascript/alchemy_admin/components/sortable_elements.js +2 -1
- data/app/javascript/alchemy_admin/components/tags_autocomplete.js +63 -48
- data/app/javascript/alchemy_admin/components/tinymce.js +2 -1
- data/app/javascript/alchemy_admin/components/uploader/file_upload.js +3 -2
- data/app/javascript/alchemy_admin/components/uploader.js +19 -3
- data/app/javascript/alchemy_admin/confirm_dialog.js +7 -1
- data/app/javascript/alchemy_admin/dialog.js +182 -110
- data/app/javascript/alchemy_admin/hotkeys.js +0 -5
- data/app/javascript/alchemy_admin/i18n.js +3 -9
- data/app/javascript/alchemy_admin/image_overlay.js +58 -42
- data/app/javascript/alchemy_admin/initializer.js +2 -2
- data/app/javascript/alchemy_admin/link_dialog.js +15 -2
- data/app/javascript/alchemy_admin/routes.js +18 -0
- data/app/javascript/alchemy_admin/turbo_stream_actions.js +88 -0
- data/app/javascript/alchemy_admin/utils/ajax.js +14 -3
- data/app/javascript/alchemy_admin/utils/events.js +5 -0
- data/app/javascript/alchemy_admin/utils/json_script.js +12 -0
- data/app/javascript/alchemy_admin/utils/tom_select.js +171 -0
- data/app/javascript/alchemy_admin.js +2 -19
- data/app/javascript/preview.js +131 -113
- data/app/jobs/alchemy/invalidate_elements_cache_job.rb +1 -1
- data/app/models/alchemy/admin/content_security_policy.rb +143 -0
- data/app/models/alchemy/admin/filters/base.rb +7 -0
- data/app/models/alchemy/admin/filters/datepicker.rb +1 -1
- data/app/models/alchemy/admin/filters/select.rb +20 -7
- data/app/models/alchemy/attachment.rb +14 -7
- data/app/models/alchemy/cache_control.rb +104 -0
- data/app/models/alchemy/current.rb +1 -1
- data/app/models/alchemy/eager_loading.rb +0 -2
- data/app/models/alchemy/element.rb +23 -8
- data/app/models/alchemy/element_definition.rb +1 -0
- data/app/models/alchemy/elements_repository.rb +11 -0
- data/app/models/alchemy/folded_page.rb +5 -1
- data/app/models/alchemy/ingredient.rb +26 -0
- data/app/models/alchemy/ingredients/datetime.rb +1 -1
- data/app/models/alchemy/ingredients/link.rb +2 -0
- data/app/models/alchemy/ingredients/picture.rb +3 -0
- data/app/models/alchemy/ingredients/text.rb +2 -0
- data/app/models/alchemy/language.rb +19 -7
- data/app/models/alchemy/legacy_page_url.rb +6 -1
- data/app/models/alchemy/node.rb +51 -0
- data/app/models/alchemy/page/page_natures.rb +41 -8
- data/app/models/alchemy/page/page_scopes.rb +23 -0
- data/app/models/alchemy/page/sitemap_cache_key.rb +56 -0
- data/app/models/alchemy/page.rb +88 -32
- data/app/models/alchemy/page_definition.rb +1 -1
- data/app/models/alchemy/page_mutex.rb +16 -0
- data/app/models/alchemy/page_version.rb +24 -1
- data/app/models/alchemy/permissions.rb +6 -5
- data/app/models/alchemy/picture.rb +15 -7
- data/app/models/alchemy/picture_description.rb +22 -0
- data/app/models/alchemy/picture_thumb.rb +18 -0
- data/app/models/alchemy/publishable/timestamp_resolver.rb +70 -0
- data/app/models/alchemy/resource.rb +1 -1
- data/app/models/alchemy/safe_url_validator.rb +34 -0
- data/app/models/alchemy/site.rb +8 -3
- data/app/models/alchemy/storage_adapter/active_storage/preprocessor.rb +7 -3
- data/app/models/alchemy/storage_adapter/active_storage.rb +4 -1
- data/app/models/alchemy/tag.rb +10 -4
- data/{lib → app/models/concerns}/alchemy/filetypes.rb +5 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +61 -6
- data/app/models/concerns/alchemy/publishable.rb +14 -29
- data/app/models/concerns/alchemy/taggable.rb +92 -0
- data/app/models/concerns/alchemy/user_methods.rb +95 -0
- data/app/services/alchemy/duplicate_element.rb +5 -2
- data/app/services/alchemy/element_preloader.rb +1 -2
- data/app/stylesheets/alchemy/_custom-properties.scss +1 -0
- data/app/stylesheets/alchemy/_extends.scss +0 -8
- data/app/stylesheets/alchemy/_themes.scss +10 -9
- data/app/stylesheets/alchemy/admin/_empty-state.scss +29 -0
- data/app/stylesheets/alchemy/admin/_help.scss +10 -0
- data/app/stylesheets/alchemy/admin/_tom-select.scss +104 -15
- data/app/stylesheets/alchemy/admin/archive.scss +56 -11
- data/app/stylesheets/alchemy/admin/base.scss +4 -5
- data/app/stylesheets/alchemy/admin/buttons.scss +12 -1
- data/app/stylesheets/alchemy/admin/dashboard.scss +150 -20
- data/app/stylesheets/alchemy/admin/dialogs.scss +27 -36
- data/app/stylesheets/alchemy/admin/element-select.scss +1 -1
- data/app/stylesheets/alchemy/admin/elements.scss +8 -24
- data/app/stylesheets/alchemy/admin/flash.scss +2 -1
- data/app/stylesheets/alchemy/admin/forms.scss +60 -24
- data/app/stylesheets/alchemy/admin/frame.scss +40 -4
- data/app/stylesheets/alchemy/admin/image_library.scss +9 -12
- data/app/stylesheets/alchemy/admin/navigation.scss +17 -14
- data/app/stylesheets/alchemy/admin/node-select.scss +13 -37
- data/app/stylesheets/alchemy/admin/nodes.scss +1 -1
- data/app/stylesheets/alchemy/admin/page-select.scss +6 -97
- data/app/stylesheets/alchemy/admin/print.scss +0 -4
- data/app/stylesheets/alchemy/admin/remote-select.scss +170 -0
- data/app/stylesheets/alchemy/admin/search.scss +2 -3
- data/app/stylesheets/alchemy/admin/selects.scss +1 -294
- data/app/stylesheets/alchemy/admin/sitemap.scss +2 -1
- data/app/stylesheets/alchemy/admin/tables.scss +7 -1
- data/app/stylesheets/alchemy/admin/tags-autocomplete.scss +17 -0
- data/app/stylesheets/alchemy/admin/tags.scss +0 -13
- data/app/stylesheets/alchemy/admin/toolbar.scss +9 -6
- data/app/stylesheets/alchemy/admin/upload.scss +61 -1
- data/app/stylesheets/alchemy/admin.scss +4 -1
- data/app/stylesheets/tinymce/skins/ui/alchemy/skin.scss +19 -0
- data/app/stylesheets/tinymce/skins/ui/alchemy-dark/skin.scss +19 -0
- data/app/types/alchemy/cache_control_type.rb +84 -0
- data/app/views/alchemy/_edit_mode.html.erb +1 -1
- data/app/views/alchemy/_menubar.html.erb +2 -154
- data/app/views/alchemy/_preview_mode_code.html.erb +0 -3
- data/app/views/alchemy/admin/_left_menu.html.erb +18 -5
- data/app/views/alchemy/admin/_top_menu.html.erb +1 -1
- data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +5 -4
- data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +3 -3
- data/app/views/alchemy/admin/attachments/_files_list.html.erb +71 -83
- data/app/views/alchemy/admin/attachments/assign.turbo_stream.erb +8 -0
- data/app/views/alchemy/admin/attachments/index.html.erb +32 -7
- data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/crop.html.erb +20 -20
- data/app/views/alchemy/admin/dashboard/_footer.html.erb +0 -3
- data/app/views/alchemy/admin/dashboard/_top.html.erb +1 -0
- data/app/views/alchemy/admin/dashboard/help.html.erb +84 -57
- data/app/views/alchemy/admin/dashboard/license.html.erb +6 -0
- data/app/views/alchemy/admin/elements/_footer.html.erb +0 -1
- data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_header.html.erb +2 -19
- data/app/views/alchemy/admin/elements/_preview_text_quote.html.erb +3 -0
- data/app/views/alchemy/admin/elements/_schedule.html.erb +7 -7
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +1 -1
- data/app/views/alchemy/admin/elements/create.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/elements/index.html.erb +1 -1
- data/app/views/alchemy/admin/elements/publish.turbo_stream.erb +2 -2
- data/app/views/alchemy/admin/elements/update.turbo_stream.erb +31 -0
- data/app/views/alchemy/admin/ingredients/update.turbo_stream.erb +6 -3
- data/app/views/alchemy/admin/languages/_table.html.erb +0 -2
- data/app/views/alchemy/admin/languages/index.html.erb +25 -29
- data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +1 -3
- data/app/views/alchemy/admin/layoutpages/edit.html.erb +1 -1
- data/app/views/alchemy/admin/layoutpages/index.html.erb +26 -7
- data/app/views/alchemy/admin/nodes/_form.html.erb +3 -21
- data/app/views/alchemy/admin/nodes/_node.html.erb +93 -88
- data/app/views/alchemy/admin/nodes/index.html.erb +15 -23
- data/app/views/alchemy/admin/pages/_create_language_form.html.erb +2 -6
- data/app/views/alchemy/admin/pages/_form.html.erb +2 -1
- data/app/views/alchemy/admin/pages/_publication_fields.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_table.html.erb +2 -4
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +2 -1
- data/app/views/alchemy/admin/pages/edit.html.erb +10 -21
- data/app/views/alchemy/admin/pages/flush.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/pages/index.html.erb +26 -31
- data/app/views/alchemy/admin/pages/info.html.erb +2 -2
- data/app/views/alchemy/admin/pages/update.turbo_stream.erb +2 -2
- data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
- data/app/views/alchemy/admin/partials/_routes.html.erb +24 -48
- data/app/views/alchemy/admin/pictures/_archive.html.erb +3 -17
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +5 -3
- data/app/views/alchemy/admin/pictures/_form.html.erb +0 -1
- data/app/views/alchemy/admin/pictures/_infos.html.erb +5 -1
- data/app/views/alchemy/admin/pictures/_picture.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +3 -4
- data/app/views/alchemy/admin/pictures/assign.turbo_stream.erb +7 -0
- data/app/views/alchemy/admin/pictures/index.html.erb +38 -30
- data/app/views/alchemy/admin/pictures/show.html.erb +41 -41
- data/app/views/alchemy/admin/resources/_empty_state.html.erb +27 -0
- data/app/views/alchemy/admin/resources/_pagination.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_per_page_select.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_resource_table.html.erb +0 -2
- data/app/views/alchemy/admin/resources/_resource_usage_info.html.erb +3 -2
- data/app/views/alchemy/admin/resources/index.html.erb +32 -13
- data/app/views/alchemy/admin/sites/index.html.erb +24 -22
- data/app/views/alchemy/admin/styleguide/index.html.erb +35 -12
- data/app/views/alchemy/admin/tags/index.html.erb +27 -25
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +6 -8
- data/app/views/alchemy/admin/translations/_en.js +3 -0
- data/app/views/alchemy/admin/uploader/_button.html.erb +11 -17
- data/app/views/alchemy/admin/uploader/_setup.html.erb +3 -3
- data/app/views/alchemy/pages/sitemap.xml.erb +2 -0
- data/app/views/kaminari/alchemy/_first_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_gap.html.erb +0 -1
- data/app/views/kaminari/alchemy/_last_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_next_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_paginator.html.erb +0 -1
- data/app/views/kaminari/alchemy/_prev_page.html.erb +1 -2
- data/app/views/layouts/alchemy/admin/dialog.html.erb +3 -0
- data/app/views/layouts/alchemy/admin.html.erb +5 -6
- data/config/brakeman.ignore +23 -1
- data/config/importmap.rb +10 -8
- data/config/initializers/dragonfly.rb +21 -17
- data/config/locales/alchemy.en.yml +53 -12
- data/config/routes.rb +3 -3
- data/db/migrate/20260727120000_add_permitted_roles_to_alchemy_pages.rb +23 -0
- data/db/migrate/20260820120000_add_cached_tag_list_to_taggables.rb +57 -0
- data/lib/alchemy/configuration/number_option.rb +13 -0
- data/lib/alchemy/configuration/proc_option.rb +13 -0
- data/lib/alchemy/configuration.rb +2 -0
- data/lib/alchemy/configurations/admin_components.rb +30 -0
- data/lib/alchemy/configurations/format_matchers.rb +7 -1
- data/lib/alchemy/configurations/main.rb +103 -4
- data/lib/alchemy/configurations/menu.rb +91 -0
- data/lib/alchemy/configurations/menu_item.rb +123 -0
- data/lib/alchemy/configurations/sitemap.rb +1 -0
- data/lib/alchemy/configurations/uploader.rb +15 -1
- data/lib/alchemy/engine.rb +0 -6
- data/lib/alchemy/i18n.rb +26 -0
- data/lib/alchemy/modules.rb +52 -50
- data/lib/alchemy/tasks/generate_thumbnails.rb +99 -0
- data/lib/alchemy/tasks/tidy.rb +1 -1
- data/lib/alchemy/test_support/capybara_helpers.rb +48 -37
- data/lib/alchemy/test_support/current_language_shared_examples.rb +1 -1
- data/lib/alchemy/test_support/factories/dummy_user_factory.rb +4 -1
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +132 -0
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +52 -0
- data/lib/alchemy/test_support/shared_publishable_examples.rb +39 -4
- data/lib/alchemy/upgrader/eight_four.rb +237 -0
- data/lib/alchemy/upgrader/register_module_rewriter.rb +177 -0
- data/lib/alchemy/upgrader.rb +2 -1
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy.rb +0 -1
- data/lib/alchemy_cms.rb +0 -13
- data/lib/generators/alchemy/elements/templates/view.html.erb +1 -1
- data/lib/generators/alchemy/elements/templates/view.html.haml +1 -1
- data/lib/generators/alchemy/elements/templates/view.html.slim +1 -1
- data/lib/generators/alchemy/install/install_generator.rb +2 -13
- data/lib/generators/alchemy/install/templates/alchemy.rb.tt +24 -9
- data/lib/generators/alchemy/module/module_generator.rb +1 -0
- data/lib/generators/alchemy/module/templates/ability.rb.tt +1 -1
- data/lib/generators/alchemy/module/templates/module_config.rb.tt +16 -18
- data/lib/generators/alchemy/user_columns_migration/templates/migration.rb.tt +19 -0
- data/lib/generators/alchemy/user_columns_migration/user_columns_migration_generator.rb +30 -0
- data/lib/tasks/alchemy/thumbnails.rake +16 -29
- data/lib/tasks/alchemy/upgrade.rake +45 -3
- data/vendor/javascript/cropperjs.min.js +3 -3
- data/vendor/javascript/floating-ui.min.js +1 -1
- data/vendor/javascript/tinymce.min.js +5 -5
- data/vendor/javascript/tom-select.min.js +1 -1
- metadata +85 -65
- data/app/javascript/alchemy_admin/components/action.js +0 -50
- data/app/javascript/alchemy_admin/dirty.js +0 -55
- data/app/javascript/alchemy_admin/node_tree.js +0 -82
- data/app/javascript/alchemy_admin/picture_selector.js +0 -67
- data/app/javascript/alchemy_admin/templates/compiled.js +0 -1
- data/app/javascript/alchemy_admin/templates/node_folder.hbs +0 -3
- data/app/stylesheets/alchemy/admin/attachment-select.scss +0 -19
- data/app/views/alchemy/admin/_main_navi.html.erb +0 -6
- data/app/views/alchemy/admin/attachments/assign.js.erb +0 -15
- data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +0 -30
- data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +0 -20
- data/app/views/alchemy/admin/partials/_sub_navigation.html.erb +0 -11
- data/app/views/alchemy/admin/pictures/assign.js.erb +0 -13
- data/app/views/alchemy/base/error_notice.js.erb +0 -1
- data/app/views/alchemy/base/permission_denied.js.erb +0 -3
- data/app/views/alchemy/base/redirect.js.erb +0 -11
- data/config/alchemy/config.yml +0 -217
- data/config/alchemy/modules.yml +0 -82
- data/config/initializers/rails_live_reload.rb +0 -13
- data/lib/alchemy/dev_support/live_reload_watcher.rb +0 -5
- data/lib/alchemy/taggable.rb +0 -49
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +0 -34
- data/vendor/assets/stylesheets/alchemy_admin/select2.css +0 -1
- data/vendor/javascript/handlebars.min.js +0 -29
- data/vendor/javascript/jquery.min.js +0 -2
- data/vendor/javascript/rails-ujs.min.js +0 -1
- data/vendor/javascript/select2.min.js +0 -23
- /data/{lib → app/controllers/concerns}/alchemy/ability_helper.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/admin/locale.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/admin/preview_time.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/admin/timezone.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/configuration_methods.rb +0 -0
- /data/{lib → app/models}/alchemy/admin/preview_url.rb +0 -0
- /data/{lib → app/models/concerns}/alchemy/hints.rb +0 -0
- /data/{lib → app/models/concerns}/alchemy/name_conversions.rb +0 -0
- /data/{lib → app/services}/alchemy/elements_finder.rb +0 -0
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import TomSelect from "tom-select"
|
|
2
|
+
import "alchemy_admin/components/icon"
|
|
2
3
|
import { translate } from "alchemy_admin/i18n"
|
|
3
4
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const DROPDOWN_MIN_HEIGHT = 120
|
|
13
|
-
|
|
14
|
-
class Select extends HTMLSelectElement {
|
|
5
|
+
createDropdownPositioning,
|
|
6
|
+
dropdownMessages,
|
|
7
|
+
focusTomSelect,
|
|
8
|
+
removeButton,
|
|
9
|
+
teardownTomSelect
|
|
10
|
+
} from "alchemy_admin/utils/tom_select"
|
|
11
|
+
|
|
12
|
+
export class Select extends HTMLSelectElement {
|
|
15
13
|
#tomSelect = null
|
|
16
14
|
|
|
17
15
|
connectedCallback() {
|
|
@@ -33,6 +31,10 @@ class Select extends HTMLSelectElement {
|
|
|
33
31
|
this.#tomSelect?.disable()
|
|
34
32
|
}
|
|
35
33
|
|
|
34
|
+
focus() {
|
|
35
|
+
focusTomSelect(this.#tomSelect, () => super.focus())
|
|
36
|
+
}
|
|
37
|
+
|
|
36
38
|
setOptions(data, prompt = undefined) {
|
|
37
39
|
const selectedValue = this.value
|
|
38
40
|
|
|
@@ -62,21 +64,22 @@ class Select extends HTMLSelectElement {
|
|
|
62
64
|
return this.getAttribute("placeholder")
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
// Subclasses may return extra Tom Select render functions (e.g. custom
|
|
68
|
+
// `option`/`item` templates) to merge on top of the defaults below.
|
|
69
|
+
get renderers() {
|
|
70
|
+
return {}
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
#initTomSelect() {
|
|
66
74
|
const plugins = {}
|
|
67
75
|
const hasPlaceholder = !!this.placeholder
|
|
68
76
|
// Capture this before Tom Select initializes, since it rewrites the
|
|
69
77
|
// select's selected option during setup.
|
|
70
78
|
const hasSelectedOption = !!this.querySelector("option[selected]")
|
|
71
|
-
const
|
|
72
|
-
dropdownMask.className = "ts-dropdown-mask"
|
|
73
|
-
|
|
74
|
-
let removeAutoUpdater = () => {}
|
|
79
|
+
const { onDropdownOpen, onDropdownClose } = createDropdownPositioning()
|
|
75
80
|
|
|
76
81
|
if (this.multiple) {
|
|
77
|
-
plugins.remove_button =
|
|
78
|
-
title: translate("Remove")
|
|
79
|
-
}
|
|
82
|
+
plugins.remove_button = removeButton()
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
if (this.allowClear) {
|
|
@@ -111,63 +114,8 @@ class Select extends HTMLSelectElement {
|
|
|
111
114
|
},
|
|
112
115
|
// remove the transition after selection of option.
|
|
113
116
|
refreshThrottle: 0,
|
|
114
|
-
onDropdownOpen
|
|
115
|
-
|
|
116
|
-
const styles = {
|
|
117
|
-
minWidth: `${this.control.offsetWidth}px`
|
|
118
|
-
}
|
|
119
|
-
// If the select is inside a dialog, we need to ensure the dropdown appears above it.
|
|
120
|
-
if (this.control.closest(".alchemy-dialog-body, .alchemy-popover")) {
|
|
121
|
-
styles.zIndex = "101"
|
|
122
|
-
}
|
|
123
|
-
Object.assign(this.dropdown.style, styles)
|
|
124
|
-
// Append the dropdown to the body to avoid overflow issues, especially in dialogs.
|
|
125
|
-
document.body.append(dropdownMask)
|
|
126
|
-
document.body.append(this.dropdown)
|
|
127
|
-
// Use Floating UI to position the dropdown relative to the control.
|
|
128
|
-
const updatePosition = async () => {
|
|
129
|
-
// Use Floating UI to calculate the dropdown position
|
|
130
|
-
const { x, y } = await computePosition(this.control, this.dropdown, {
|
|
131
|
-
middleware: [
|
|
132
|
-
// Flip to the opposite side if there’s not enough space
|
|
133
|
-
flip(),
|
|
134
|
-
// Make some space between the control and the dropdown to prevent overlap
|
|
135
|
-
offset(2),
|
|
136
|
-
// Ensure the dropdown fits within the viewport
|
|
137
|
-
size({
|
|
138
|
-
apply({ availableHeight, elements }) {
|
|
139
|
-
Object.assign(
|
|
140
|
-
elements.floating.querySelector(".ts-dropdown-content")
|
|
141
|
-
.style,
|
|
142
|
-
{
|
|
143
|
-
maxHeight: `${Math.max(DROPDOWN_MIN_HEIGHT, availableHeight - DROPDOWN_WINDOW_MARGIN)}px`
|
|
144
|
-
}
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
]
|
|
149
|
-
})
|
|
150
|
-
// Position the dropdown
|
|
151
|
-
Object.assign(this.dropdown.style, {
|
|
152
|
-
left: `${x}px`,
|
|
153
|
-
top: `${y}px`
|
|
154
|
-
})
|
|
155
|
-
}
|
|
156
|
-
// Update the dropdown position whenever the window resizes or scrolls.
|
|
157
|
-
removeAutoUpdater = autoUpdate(
|
|
158
|
-
this.control,
|
|
159
|
-
this.dropdown,
|
|
160
|
-
updatePosition
|
|
161
|
-
)
|
|
162
|
-
},
|
|
163
|
-
onDropdownClose: function () {
|
|
164
|
-
this.control_input.classList.remove("has-value")
|
|
165
|
-
// Remove the dropdown from DOM when closed.
|
|
166
|
-
this.dropdown.remove()
|
|
167
|
-
dropdownMask.remove()
|
|
168
|
-
// Cleanup the position auto-update when the dropdown is closed.
|
|
169
|
-
removeAutoUpdater()
|
|
170
|
-
},
|
|
117
|
+
onDropdownOpen,
|
|
118
|
+
onDropdownClose,
|
|
171
119
|
allowEmptyOption: true,
|
|
172
120
|
openOnFocus: false,
|
|
173
121
|
// Keep options in their original order instead of sorting by value.
|
|
@@ -176,14 +124,8 @@ class Select extends HTMLSelectElement {
|
|
|
176
124
|
maxOptions: null,
|
|
177
125
|
// Customize the "create" and "no results" dropdown messages with i18n.
|
|
178
126
|
render: {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
${translate("Add")}<strong>${escape(data.input)}</strong>…
|
|
182
|
-
</div>`
|
|
183
|
-
},
|
|
184
|
-
no_results() {
|
|
185
|
-
return `<div class="no-results">${translate("No results found")}</div>`
|
|
186
|
-
}
|
|
127
|
+
...dropdownMessages,
|
|
128
|
+
...this.renderers
|
|
187
129
|
}
|
|
188
130
|
}
|
|
189
131
|
|
|
@@ -197,11 +139,7 @@ class Select extends HTMLSelectElement {
|
|
|
197
139
|
}
|
|
198
140
|
|
|
199
141
|
#destroyTomSelect() {
|
|
200
|
-
|
|
201
|
-
// the onDropdownClose handler does not run and the full window dropdown
|
|
202
|
-
// mask would be left behind, swallowing every click.
|
|
203
|
-
this.#tomSelect?.close(false)
|
|
204
|
-
this.#tomSelect?.destroy()
|
|
142
|
+
teardownTomSelect(this.#tomSelect)
|
|
205
143
|
this.#tomSelect = null
|
|
206
144
|
}
|
|
207
145
|
}
|
|
@@ -3,6 +3,7 @@ import { growl } from "alchemy_admin/growler"
|
|
|
3
3
|
import { patch } from "alchemy_admin/utils/ajax"
|
|
4
4
|
import { translate } from "alchemy_admin/i18n"
|
|
5
5
|
import pleaseWaitOverlay from "alchemy_admin/please_wait_overlay"
|
|
6
|
+
import { routes } from "alchemy_admin/routes"
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Custom element for the sitemap container
|
|
@@ -160,7 +161,7 @@ export class AlchemySitemap extends HTMLElement {
|
|
|
160
161
|
// evt.item is the <alchemy-page-node> element being dragged
|
|
161
162
|
const pageNode = evt.item
|
|
162
163
|
const pageId = pageNode.pageId
|
|
163
|
-
const url =
|
|
164
|
+
const url = routes.move_admin_page_path(pageId)
|
|
164
165
|
const data = {
|
|
165
166
|
target_parent_id: evt.to.dataset.parentId,
|
|
166
167
|
new_position: evt.newIndex
|
|
@@ -3,6 +3,7 @@ import { growl } from "alchemy_admin/growler"
|
|
|
3
3
|
import { post } from "alchemy_admin/utils/ajax"
|
|
4
4
|
import { reloadPreview } from "alchemy_admin/components/preview_window"
|
|
5
5
|
import { dispatchPageDirtyEvent } from "alchemy_admin/utils/dispatch_page_dirty_event"
|
|
6
|
+
import { routes } from "alchemy_admin/routes"
|
|
6
7
|
|
|
7
8
|
const SORTABLE_OPTIONS = {
|
|
8
9
|
draggable: ".element-editor",
|
|
@@ -36,7 +37,7 @@ function onSort(event) {
|
|
|
36
37
|
// or sorted in the same list. Not on the old list in order to avoid incrementing
|
|
37
38
|
// the position of the other elements.
|
|
38
39
|
if (event.target === event.to) {
|
|
39
|
-
post(
|
|
40
|
+
post(routes.order_admin_elements_path, params).then((response) => {
|
|
40
41
|
const data = response.data
|
|
41
42
|
growl(data.message)
|
|
42
43
|
if (data.pageHasUnpublishedChanges) {
|
|
@@ -1,69 +1,84 @@
|
|
|
1
|
-
import
|
|
1
|
+
import TomSelect from "tom-select"
|
|
2
|
+
import "alchemy_admin/components/icon"
|
|
3
|
+
import { get } from "alchemy_admin/utils/ajax"
|
|
4
|
+
import { growl } from "alchemy_admin/growler"
|
|
5
|
+
import {
|
|
6
|
+
createDropdownPositioning,
|
|
7
|
+
dropdownMessages,
|
|
8
|
+
focusTomSelect,
|
|
9
|
+
removeButton,
|
|
10
|
+
teardownTomSelect
|
|
11
|
+
} from "alchemy_admin/utils/tom_select"
|
|
2
12
|
|
|
3
13
|
export class TagsAutocomplete extends HTMLElement {
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
async connectedCallback() {
|
|
7
|
-
await setupSelectLocale()
|
|
8
|
-
if (!this.isConnected) return
|
|
14
|
+
#tomSelect = null
|
|
9
15
|
|
|
16
|
+
connectedCallback() {
|
|
10
17
|
this.classList.add("autocomplete_tag_list")
|
|
11
|
-
|
|
18
|
+
// Reuse the shared Tom Select styling by tagging the source input, whose
|
|
19
|
+
// class Tom Select copies onto the generated wrapper.
|
|
20
|
+
this.input.classList.add("alchemy_selectbox")
|
|
21
|
+
this.#tomSelect = new TomSelect(this.input, this.settings)
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
disconnectedCallback() {
|
|
15
|
-
this.#
|
|
16
|
-
this.#
|
|
25
|
+
teardownTomSelect(this.#tomSelect)
|
|
26
|
+
this.#tomSelect = null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
focus() {
|
|
30
|
+
focusTomSelect(this.#tomSelect, () => super.focus())
|
|
17
31
|
}
|
|
18
32
|
|
|
19
33
|
get input() {
|
|
20
34
|
return this.getElementsByTagName("input")[0]
|
|
21
35
|
}
|
|
22
36
|
|
|
23
|
-
get
|
|
37
|
+
get settings() {
|
|
24
38
|
return {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
openOnEnter: false,
|
|
28
|
-
minimumInputLength: 1,
|
|
29
|
-
createSearchChoice: this.#createSearchChoice,
|
|
30
|
-
ajax: {
|
|
31
|
-
url: this.getAttribute("url"),
|
|
32
|
-
dataType: "json",
|
|
33
|
-
data: (term) => {
|
|
34
|
-
return { term }
|
|
35
|
-
},
|
|
36
|
-
results: (data) => {
|
|
37
|
-
return { results: data }
|
|
38
|
-
}
|
|
39
|
+
plugins: {
|
|
40
|
+
remove_button: removeButton()
|
|
39
41
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
// The autocomplete endpoint returns each tag as `{ id, text }`.
|
|
43
|
+
valueField: "id",
|
|
44
|
+
// Tags are entered and stored as a comma separated list.
|
|
45
|
+
delimiter: ",",
|
|
46
|
+
// Allow free tagging.
|
|
47
|
+
create: true,
|
|
48
|
+
// Hide the "Add …" create option while suggestions are still loading, so
|
|
49
|
+
// it doesn't flash and then disappear once a matching tag arrives from
|
|
50
|
+
// the server. When idle, keep Tom Select's default of not offering to
|
|
51
|
+
// create a tag that already exists.
|
|
52
|
+
createFilter(input) {
|
|
53
|
+
if (this.loading) return false
|
|
54
|
+
return this.settings.duplicates || !this.options[input]
|
|
55
|
+
},
|
|
56
|
+
// Do not keep a removed tag around as a lingering suggestion.
|
|
57
|
+
persist: false,
|
|
58
|
+
// Clear the typed text once a tag is added, so it doesn't linger in the
|
|
59
|
+
// input after selecting an existing tag.
|
|
60
|
+
clearAfterSelect: true,
|
|
61
|
+
// Show every matching tag returned by the server.
|
|
62
|
+
maxOptions: null,
|
|
63
|
+
openOnFocus: false,
|
|
64
|
+
// Apply the placeholder set on the wrapper element.
|
|
65
|
+
placeholder: this.getAttribute("placeholder"),
|
|
66
|
+
// Load matching tags from the server as the user types.
|
|
67
|
+
load: (query, callback) => {
|
|
68
|
+
get(this.getAttribute("url"), { term: query })
|
|
69
|
+
.then((response) => callback(response.data))
|
|
70
|
+
.catch((error) => {
|
|
71
|
+
growl(error.message || error, "error")
|
|
72
|
+
// Tom Select stays in loading state until the callback runs.
|
|
73
|
+
callback()
|
|
74
|
+
})
|
|
75
|
+
},
|
|
76
|
+
...createDropdownPositioning(),
|
|
77
|
+
render: {
|
|
78
|
+
...dropdownMessages
|
|
53
79
|
}
|
|
54
80
|
}
|
|
55
81
|
}
|
|
56
|
-
|
|
57
|
-
#initSelection(element, callback) {
|
|
58
|
-
const data = []
|
|
59
|
-
$(element.val().split(",")).each(function () {
|
|
60
|
-
data.push({
|
|
61
|
-
id: this.trim(),
|
|
62
|
-
text: this
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
callback(data)
|
|
66
|
-
}
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
customElements.define("alchemy-tags-autocomplete", TagsAutocomplete)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "tinymce"
|
|
2
2
|
import { currentLocale } from "alchemy_admin/i18n"
|
|
3
|
+
import { readJSONScript } from "alchemy_admin/utils/json_script"
|
|
3
4
|
|
|
4
5
|
const DARK_THEME = "alchemy-dark"
|
|
5
6
|
const LIGHT_THEME = "alchemy"
|
|
@@ -172,7 +173,7 @@ export class Tinymce extends HTMLElement {
|
|
|
172
173
|
|
|
173
174
|
const config = {
|
|
174
175
|
content_css: this.preferredTheme,
|
|
175
|
-
...
|
|
176
|
+
...readJSONScript("alchemy_tinymce_defaults"),
|
|
176
177
|
...customConfig,
|
|
177
178
|
language: currentLocale(),
|
|
178
179
|
selector: `#${this.editorId}`,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { formatFileSize } from "alchemy_admin/utils/format"
|
|
2
2
|
import { translate } from "alchemy_admin/i18n"
|
|
3
3
|
import { growl } from "alchemy_admin/growler"
|
|
4
|
+
import { readJSONScript } from "alchemy_admin/utils/json_script"
|
|
4
5
|
|
|
5
6
|
export class FileUpload extends HTMLElement {
|
|
6
7
|
// public — used by callers (Uploader, Progress, tests)
|
|
@@ -78,10 +79,10 @@ export class FileUpload extends HTMLElement {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
/**
|
|
81
|
-
* validate given file with the
|
|
82
|
+
* validate given file with the uploader configuration
|
|
82
83
|
*/
|
|
83
84
|
#validateFile() {
|
|
84
|
-
const config =
|
|
85
|
+
const config = readJSONScript("alchemy_uploader_defaults")
|
|
85
86
|
const maxFileSize = config.file_size_limit * Math.pow(1024, 2) // in Byte
|
|
86
87
|
let errorMessage = undefined
|
|
87
88
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { Progress } from "alchemy_admin/components/uploader/progress"
|
|
7
7
|
import { FileUpload } from "alchemy_admin/components/uploader/file_upload"
|
|
8
8
|
import { translate } from "alchemy_admin/i18n"
|
|
9
|
+
import { readJSONScript } from "alchemy_admin/utils/json_script"
|
|
9
10
|
import { getToken } from "alchemy_admin/utils/ajax"
|
|
10
11
|
|
|
11
12
|
export class Uploader extends HTMLElement {
|
|
@@ -110,13 +111,14 @@ export class Uploader extends HTMLElement {
|
|
|
110
111
|
uploadFiles(files) {
|
|
111
112
|
// prepare file progress bars and server request
|
|
112
113
|
let fileUploadCount = 0
|
|
114
|
+
const config = readJSONScript("alchemy_uploader_defaults")
|
|
113
115
|
|
|
114
116
|
const fileUploads = files.map((file) => {
|
|
115
117
|
const request = new XMLHttpRequest()
|
|
116
118
|
const fileUpload = new FileUpload()
|
|
117
119
|
fileUpload.initialize(file, request)
|
|
118
120
|
|
|
119
|
-
if (
|
|
121
|
+
if (config.upload_limit - 1 < fileUploadCount) {
|
|
120
122
|
fileUpload.valid = false
|
|
121
123
|
fileUpload.errorMessage = translate("Maximum number of files exceeded")
|
|
122
124
|
} else if (fileUpload.valid) {
|
|
@@ -154,23 +156,37 @@ export class Uploader extends HTMLElement {
|
|
|
154
156
|
#createProgress(fileUploads) {
|
|
155
157
|
if (this.uploadProgress) {
|
|
156
158
|
this.uploadProgress.cancel()
|
|
157
|
-
|
|
159
|
+
this.uploadProgress.remove()
|
|
158
160
|
}
|
|
159
161
|
this.uploadProgress = new Progress()
|
|
160
162
|
this.uploadProgress.initialize(fileUploads)
|
|
163
|
+
this.uploadProgress.setAttribute("layout", this.layout)
|
|
161
164
|
this.uploadProgress.onComplete = (status) => {
|
|
162
165
|
this.dispatchEvent(
|
|
163
166
|
new CustomEvent(`Alchemy.upload.${status}`, { bubbles: true })
|
|
164
167
|
)
|
|
165
168
|
}
|
|
166
169
|
|
|
167
|
-
|
|
170
|
+
this.#progressContainer.append(this.uploadProgress)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// The dropzone doubles as the progress container so the upload indicators
|
|
174
|
+
// render inside the archive (and inside dialogs). Falls back to the body for
|
|
175
|
+
// uploaders without a dropzone (e.g. the attachment replace button).
|
|
176
|
+
get #progressContainer() {
|
|
177
|
+
return this.#dropzoneElement ?? document.body
|
|
168
178
|
}
|
|
169
179
|
|
|
170
180
|
get dropzone() {
|
|
171
181
|
return this.getAttribute("dropzone")
|
|
172
182
|
}
|
|
173
183
|
|
|
184
|
+
// Shape of the upload indicators. Defaults to "row" so uploads fit naturally
|
|
185
|
+
// into resource table views; set layout="card" for the thumbnail grid.
|
|
186
|
+
get layout() {
|
|
187
|
+
return this.getAttribute("layout") || "row"
|
|
188
|
+
}
|
|
189
|
+
|
|
174
190
|
/**
|
|
175
191
|
* @returns {HTMLInputElement}
|
|
176
192
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHtmlElement } from "alchemy_admin/utils/dom_helpers"
|
|
2
|
+
import { currentDialog } from "alchemy_admin/dialog"
|
|
2
3
|
import { translate } from "alchemy_admin/i18n"
|
|
3
4
|
|
|
4
5
|
const getDefaults = () => ({
|
|
@@ -37,7 +38,12 @@ class ConfirmDialog {
|
|
|
37
38
|
</button>
|
|
38
39
|
</sl-dialog>
|
|
39
40
|
`)
|
|
40
|
-
|
|
41
|
+
// A native <dialog> opened with showModal() sits in the top layer and makes
|
|
42
|
+
// everything outside its subtree inert. Append the confirm into the open
|
|
43
|
+
// dialog (if any) so it stays visible and interactable above it, instead of
|
|
44
|
+
// rendering below and unclickable on the body.
|
|
45
|
+
const container = currentDialog()?.dialog_container ?? document.body
|
|
46
|
+
container.append(this.dialog)
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
#bindEvents() {
|