alchemy_cms 8.0.7 → 8.1.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/README.md +14 -10
- data/app/assets/builds/alchemy/admin.css +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/{views/alchemy/admin/elements/_element.html.erb → components/alchemy/admin/element_editor.html.erb} +34 -29
- data/app/components/alchemy/admin/element_editor.rb +115 -0
- data/app/components/alchemy/admin/element_select.rb +12 -9
- data/app/components/alchemy/admin/ingredient_editor.rb +54 -0
- data/app/components/alchemy/admin/list_filter.rb +16 -5
- data/app/components/alchemy/admin/page_node.html.erb +214 -0
- data/app/components/alchemy/admin/page_node.rb +70 -0
- data/app/components/alchemy/admin/picture_thumbnail.rb +36 -0
- data/app/components/alchemy/admin/publish_page_button.html.erb +15 -0
- data/app/components/alchemy/admin/publish_page_button.rb +54 -0
- data/app/{helpers/alchemy/admin/tags_helper.rb → components/alchemy/admin/tags_list.rb} +19 -11
- data/app/components/alchemy/admin/toolbar_button.rb +16 -12
- data/app/components/alchemy/ingredients/audio_editor.rb +8 -0
- data/app/components/alchemy/ingredients/base_editor.rb +222 -0
- data/app/components/alchemy/ingredients/boolean_editor.rb +21 -0
- data/app/components/alchemy/ingredients/color_editor.rb +80 -0
- data/app/components/alchemy/ingredients/color_view.rb +13 -0
- data/app/components/alchemy/ingredients/datetime_editor.rb +28 -0
- data/app/components/alchemy/ingredients/file_editor.rb +69 -0
- data/app/components/alchemy/ingredients/headline_editor.rb +88 -0
- data/app/components/alchemy/ingredients/html_editor.rb +11 -0
- data/app/components/alchemy/ingredients/link_editor.rb +29 -0
- data/app/components/alchemy/ingredients/node_editor.rb +23 -0
- data/app/components/alchemy/ingredients/number_editor.rb +28 -0
- data/app/components/alchemy/ingredients/page_editor.rb +19 -0
- data/app/components/alchemy/ingredients/picture_editor.rb +81 -0
- data/app/components/alchemy/ingredients/richtext_editor.rb +31 -0
- data/app/components/alchemy/ingredients/select_editor.rb +37 -0
- data/app/components/alchemy/ingredients/select_view.rb +7 -0
- data/app/components/alchemy/ingredients/text_editor.rb +41 -0
- data/app/components/alchemy/ingredients/video_editor.rb +8 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +8 -6
- data/app/controllers/alchemy/admin/base_controller.rb +7 -18
- data/app/controllers/alchemy/admin/clipboard_controller.rb +15 -11
- data/app/controllers/alchemy/admin/dashboard_controller.rb +2 -2
- data/app/controllers/alchemy/admin/elements_controller.rb +34 -32
- data/app/controllers/alchemy/admin/ingredients_controller.rb +1 -0
- data/app/controllers/alchemy/admin/layoutpages_controller.rb +2 -1
- data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +1 -1
- data/app/controllers/alchemy/admin/nodes_controller.rb +24 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +31 -41
- data/app/controllers/alchemy/admin/pictures_controller.rb +2 -5
- data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
- data/app/controllers/alchemy/api/ingredients_controller.rb +1 -1
- data/app/controllers/alchemy/api/pages_controller.rb +5 -3
- data/app/controllers/alchemy/base_controller.rb +6 -6
- data/app/controllers/alchemy/pages_controller.rb +12 -6
- data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +0 -1
- data/app/controllers/concerns/alchemy/admin/clipboard.rb +57 -0
- data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +2 -2
- data/app/controllers/concerns/alchemy/site_redirects.rb +1 -1
- data/app/decorators/alchemy/ingredient_editor.rb +37 -4
- data/app/helpers/alchemy/admin/base_helper.rb +10 -6
- data/app/helpers/alchemy/admin/ingredients_helper.rb +6 -3
- data/app/helpers/alchemy/base_helper.rb +1 -1
- data/app/helpers/alchemy/pages_helper.rb +1 -1
- data/app/javascript/alchemy_admin/components/action.js +5 -1
- data/app/javascript/alchemy_admin/components/color_select.js +73 -0
- data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +11 -3
- data/app/javascript/alchemy_admin/components/element_editor/publish_element_button.js +7 -2
- data/app/javascript/alchemy_admin/components/element_editor.js +11 -12
- data/app/javascript/alchemy_admin/components/element_select.js +39 -17
- data/app/javascript/alchemy_admin/components/elements_window.js +0 -2
- data/app/javascript/alchemy_admin/components/file_editor.js +26 -0
- data/app/javascript/alchemy_admin/components/index.js +9 -0
- data/app/javascript/alchemy_admin/components/list_filter.js +57 -8
- data/app/javascript/alchemy_admin/components/message.js +9 -3
- data/app/javascript/alchemy_admin/components/page_node.js +119 -0
- data/app/javascript/alchemy_admin/{page_publication_fields.js → components/page_publication_fields.js} +9 -8
- data/app/javascript/alchemy_admin/{picture_editors.js → components/picture_editor.js} +30 -45
- data/app/javascript/alchemy_admin/components/picture_thumbnail.js +107 -0
- data/app/javascript/alchemy_admin/components/publish_page_button.js +41 -0
- data/app/javascript/alchemy_admin/components/select.js +3 -1
- data/app/javascript/alchemy_admin/components/sitemap.js +210 -0
- data/app/javascript/alchemy_admin/{sortable_elements.js → components/sortable_elements.js} +22 -25
- data/app/javascript/alchemy_admin/components/tinymce.js +10 -5
- data/app/javascript/alchemy_admin/components/update_check.js +1 -1
- data/app/javascript/alchemy_admin/components/uploader.js +30 -0
- data/app/javascript/alchemy_admin/image_overlay.js +0 -2
- data/app/javascript/alchemy_admin/initializer.js +0 -3
- data/app/javascript/alchemy_admin/templates/compiled.js +1 -1
- data/app/javascript/alchemy_admin/utils/ajax.js +15 -3
- data/app/javascript/alchemy_admin.js +0 -6
- data/app/models/alchemy/attachment.rb +4 -4
- data/app/models/alchemy/element/definitions.rb +1 -2
- data/app/models/alchemy/element/element_ingredients.rb +6 -2
- data/app/models/alchemy/element.rb +54 -13
- data/app/models/alchemy/element_definition.rb +4 -1
- data/app/models/alchemy/elements_repository.rb +6 -0
- data/app/models/alchemy/folded_page.rb +2 -2
- data/app/models/alchemy/ingredient.rb +38 -1
- data/app/models/alchemy/ingredient_definition.rb +4 -1
- data/app/models/alchemy/ingredient_validator.rb +6 -2
- data/app/models/alchemy/ingredients/color.rb +10 -0
- data/app/models/alchemy/ingredients/headline.rb +2 -17
- data/app/models/alchemy/ingredients/picture.rb +4 -4
- data/app/models/alchemy/ingredients/select.rb +19 -0
- data/app/models/alchemy/node.rb +28 -1
- data/app/models/alchemy/page/page_naming.rb +0 -7
- data/app/models/alchemy/page/page_natures.rb +7 -3
- data/app/models/alchemy/page/page_scopes.rb +1 -1
- data/app/models/alchemy/page/publisher.rb +14 -2
- data/app/models/alchemy/page.rb +102 -23
- data/app/models/alchemy/page_definition.rb +4 -1
- data/app/models/alchemy/page_version.rb +22 -6
- data/app/models/alchemy/picture.rb +10 -11
- data/app/models/alchemy/picture_variant.rb +1 -3
- data/app/models/alchemy/resource.rb +1 -1
- data/app/models/alchemy/storage_adapter/active_storage.rb +14 -2
- data/app/models/alchemy/storage_adapter/dragonfly.rb +12 -0
- data/app/models/alchemy/storage_adapter.rb +2 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +4 -4
- data/app/models/concerns/alchemy/publishable.rb +54 -0
- data/app/serializers/alchemy/page_tree_serializer.rb +11 -31
- data/app/services/alchemy/copy_page.rb +17 -0
- data/app/services/alchemy/duplicate_element.rb +1 -1
- data/app/services/alchemy/page_tree_preloader.rb +105 -0
- data/app/stylesheets/alchemy/_extends.scss +3 -9
- data/app/stylesheets/alchemy/_mixins.scss +3 -1
- data/app/stylesheets/alchemy/_themes.scss +19 -10
- data/app/stylesheets/alchemy/admin/archive.scss +1 -0
- data/app/stylesheets/alchemy/admin/base.scss +5 -2
- data/app/stylesheets/alchemy/admin/buttons.scss +3 -3
- data/app/stylesheets/alchemy/admin/element-select.scss +18 -0
- data/app/stylesheets/alchemy/admin/elements.scss +123 -23
- data/app/stylesheets/alchemy/admin/errors.scss +1 -1
- data/app/stylesheets/alchemy/admin/flash.scss +6 -4
- data/app/stylesheets/alchemy/admin/images.scss +9 -5
- data/app/stylesheets/alchemy/admin/list_filter.scss +4 -4
- data/app/stylesheets/alchemy/admin/notices.scss +1 -2
- data/app/stylesheets/alchemy/admin/selects.scss +36 -21
- data/app/stylesheets/alchemy/admin/shoelace.scss +14 -1
- data/app/stylesheets/alchemy/admin/sitemap.scss +11 -3
- data/app/stylesheets/alchemy/admin/tags.scss +3 -1
- data/app/stylesheets/alchemy/admin/toolbar.scss +1 -1
- data/app/views/alchemy/_edit_mode.html.erb +1 -1
- data/app/views/alchemy/_menubar.html.erb +1 -1
- data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +35 -31
- data/app/views/alchemy/admin/attachments/_library_sidebar.html.erb +6 -0
- data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
- data/app/views/alchemy/admin/attachments/_replace_button.html.erb +1 -8
- data/app/views/alchemy/admin/attachments/_sorting_select.html.erb +13 -0
- data/app/views/alchemy/admin/attachments/_tag_list.html.erb +2 -3
- data/app/views/alchemy/admin/attachments/index.html.erb +5 -11
- data/app/views/alchemy/admin/attachments/show.html.erb +1 -1
- data/app/views/alchemy/admin/clipboard/_button.html.erb +1 -0
- data/app/views/alchemy/admin/clipboard/index.html.erb +4 -5
- data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/crop.html.erb +5 -7
- data/app/views/alchemy/admin/elements/_add_nested_element_form.html.erb +6 -6
- data/app/views/alchemy/admin/elements/_fixed_element.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_footer.html.erb +7 -1
- data/app/views/alchemy/admin/elements/_header.html.erb +5 -5
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +33 -8
- data/app/views/alchemy/admin/elements/create.turbo_stream.erb +10 -10
- data/app/views/alchemy/admin/elements/index.html.erb +29 -16
- data/app/views/alchemy/admin/elements/new.html.erb +2 -2
- data/app/views/alchemy/admin/ingredients/update.turbo_stream.erb +3 -5
- data/app/views/alchemy/admin/leave.html.erb +1 -1
- data/app/views/alchemy/admin/nodes/_node.html.erb +19 -0
- data/app/views/alchemy/admin/nodes/edit.html.erb +1 -1
- data/app/views/alchemy/admin/nodes/index.html.erb +3 -1
- data/app/views/alchemy/admin/nodes/new.html.erb +14 -1
- data/app/views/alchemy/admin/pages/_current_page.html.erb +3 -1
- data/app/views/alchemy/admin/pages/_form.html.erb +21 -9
- data/app/views/alchemy/admin/pages/_page_status.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_publication_fields.html.erb +28 -26
- data/app/views/alchemy/admin/pages/_table.html.erb +0 -7
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +3 -5
- data/app/views/alchemy/admin/pages/edit.html.erb +5 -11
- data/app/views/alchemy/admin/pages/flush.turbo_stream.erb +2 -0
- data/app/views/alchemy/admin/pages/fold.turbo_stream.erb +5 -0
- data/app/views/alchemy/admin/pages/index.html.erb +5 -3
- data/app/views/alchemy/admin/pages/new.html.erb +2 -12
- data/app/views/alchemy/admin/pages/publish.turbo_stream.erb +12 -0
- data/app/views/alchemy/admin/pages/tree.html.erb +13 -0
- data/app/views/alchemy/admin/pages/update.turbo_stream.erb +5 -16
- data/app/views/alchemy/admin/partials/_flash_notices.html.erb +1 -1
- data/app/views/alchemy/admin/partials/{_remote_search_form.html.erb → _overlay_search_form.html.erb} +1 -2
- data/app/views/alchemy/admin/partials/_paste_from_clipboard_form.html.erb +12 -0
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +24 -21
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +18 -26
- data/app/views/alchemy/admin/pictures/_picture.html.erb +11 -15
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +3 -6
- data/app/views/alchemy/admin/pictures/_tag_list.html.erb +2 -3
- data/app/views/alchemy/admin/pictures/index.html.erb +0 -1
- data/app/views/alchemy/admin/pictures/update.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/resources/_resource_usage_info.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_tag_list.html.erb +2 -3
- data/app/views/alchemy/admin/styleguide/index.html.erb +25 -20
- data/app/views/alchemy/admin/tags/edit.html.erb +1 -1
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +2 -2
- data/app/views/alchemy/admin/uploader/_button.html.erb +1 -15
- data/app/views/alchemy/attachments/show.html.erb +1 -1
- data/app/views/alchemy/base/permission_denied.js.erb +1 -1
- data/app/views/alchemy/ingredients/shared/_anchor.html.erb +9 -7
- data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +12 -5
- data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +10 -11
- data/app/views/alchemy/language_links/_spacer.html.erb +1 -1
- data/app/views/alchemy/messages_mailer/new.html.erb +1 -1
- data/app/views/alchemy/welcome.html.erb +1 -1
- data/config/locales/alchemy.en.yml +12 -2
- data/config/routes.rb +2 -2
- data/db/migrate/20230123112425_add_searchable_to_alchemy_pages.rb +1 -1
- data/db/migrate/20230505132743_add_indexes_to_alchemy_pictures.rb +1 -1
- data/db/migrate/20231113104432_create_page_mutexes.rb +1 -1
- data/db/migrate/20240314105244_create_alchemy_picture_descriptions.rb +1 -1
- data/db/migrate/20250626160259_add_unique_index_to_picture_descriptions.rb +1 -1
- data/db/migrate/20250905140323_add_created_at_index_to_pictures_and_attachments.rb +1 -1
- data/db/migrate/20251106150010_convert_select_value_for_multiple.rb +11 -0
- data/db/migrate/20260102121232_add_metadata_to_page_versions.rb +9 -0
- data/db/migrate/20260115164704_add_publication_timestamps_to_alchemy_elements.rb +30 -0
- data/db/migrate/20260115164705_add_index_to_element_publication_timestamps.rb +13 -0
- data/lib/alchemy/ability_helper.rb +1 -3
- data/lib/alchemy/auth_accessors.rb +51 -117
- data/lib/alchemy/configuration.rb +1 -0
- data/lib/alchemy/configurations/main.rb +63 -0
- data/lib/alchemy/controller_actions.rb +1 -1
- data/lib/alchemy/engine.rb +9 -12
- data/lib/alchemy/error_tracking/error_logger.rb +1 -1
- data/lib/alchemy/errors.rb +1 -1
- data/lib/alchemy/logger.rb +34 -4
- data/lib/alchemy/name_conversions.rb +0 -6
- data/lib/alchemy/seeder.rb +2 -2
- data/lib/alchemy/tasks/usage.rb +4 -4
- data/lib/alchemy/test_support/factories/page_version_factory.rb +3 -0
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +30 -0
- data/lib/alchemy/test_support/shared_ingredient_editor_examples.rb +26 -6
- data/lib/alchemy/test_support/shared_publishable_examples.rb +114 -0
- data/lib/alchemy/upgrader/eight_one.rb +56 -0
- data/lib/alchemy/upgrader.rb +9 -1
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy.rb +1 -4
- data/lib/alchemy_cms.rb +0 -1
- data/lib/generators/alchemy/elements/templates/view.html.erb +3 -3
- data/lib/generators/alchemy/ingredient/ingredient_generator.rb +6 -8
- data/lib/generators/alchemy/ingredient/templates/editor_component.rb.tt +22 -0
- data/lib/generators/alchemy/page_layouts/templates/layout.html.erb +1 -1
- data/lib/generators/alchemy/site_layouts/templates/layout.html.erb +1 -1
- data/lib/tasks/alchemy/upgrade.rake +21 -7
- data/vendor/javascript/shoelace.min.js +713 -31
- data/vendor/javascript/tinymce.min.js +1 -1
- metadata +103 -83
- data/app/decorators/alchemy/element_editor.rb +0 -90
- data/app/helpers/alchemy/admin/pictures_helper.rb +0 -14
- data/app/javascript/alchemy_admin/file_editors.js +0 -28
- data/app/javascript/alchemy_admin/image_loader.js +0 -54
- data/app/javascript/alchemy_admin/page_sorter.js +0 -71
- data/app/javascript/alchemy_admin/sitemap.js +0 -154
- data/app/javascript/alchemy_admin/templates/page_folder.hbs +0 -3
- data/app/views/alchemy/admin/attachments/archive_overlay.js.erb +0 -4
- data/app/views/alchemy/admin/pages/_page.html.erb +0 -163
- data/app/views/alchemy/admin/pages/_sitemap.html.erb +0 -30
- data/app/views/alchemy/admin/pages/flush.js.erb +0 -2
- data/app/views/alchemy/admin/pictures/archive_overlay.js.erb +0 -5
- data/app/views/alchemy/admin/pictures/index.js.erb +0 -2
- data/app/views/alchemy/ingredients/_audio_editor.html.erb +0 -5
- data/app/views/alchemy/ingredients/_boolean_editor.html.erb +0 -11
- data/app/views/alchemy/ingredients/_datetime_editor.html.erb +0 -20
- data/app/views/alchemy/ingredients/_file_editor.html.erb +0 -52
- data/app/views/alchemy/ingredients/_headline_editor.html.erb +0 -44
- data/app/views/alchemy/ingredients/_html_editor.html.erb +0 -8
- data/app/views/alchemy/ingredients/_link_editor.html.erb +0 -30
- data/app/views/alchemy/ingredients/_node_editor.html.erb +0 -13
- data/app/views/alchemy/ingredients/_number_editor.html.erb +0 -24
- data/app/views/alchemy/ingredients/_page_editor.html.erb +0 -13
- data/app/views/alchemy/ingredients/_picture_editor.html.erb +0 -59
- data/app/views/alchemy/ingredients/_richtext_editor.html.erb +0 -15
- data/app/views/alchemy/ingredients/_select_editor.html.erb +0 -31
- data/app/views/alchemy/ingredients/_text_editor.html.erb +0 -29
- data/app/views/alchemy/ingredients/_video_editor.html.erb +0 -5
- data/lib/generators/alchemy/ingredient/templates/editor.html.erb +0 -14
- /data/{lib → app/models}/alchemy/permissions.rb +0 -0
|
@@ -2,32 +2,28 @@ import debounce from "alchemy_admin/utils/debounce"
|
|
|
2
2
|
import max from "alchemy_admin/utils/max"
|
|
3
3
|
import { get } from "alchemy_admin/utils/ajax"
|
|
4
4
|
import { growl } from "alchemy_admin/growler"
|
|
5
|
-
import ImageLoader from "alchemy_admin/image_loader"
|
|
6
5
|
|
|
7
6
|
const UPDATE_DELAY = 125
|
|
8
7
|
const IMAGE_PLACEHOLDER = '<alchemy-icon name="image" size="xl"></alchemy-icon>'
|
|
9
8
|
const THUMBNAIL_SIZE = "160x120"
|
|
10
9
|
|
|
11
|
-
export class PictureEditor {
|
|
12
|
-
constructor(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
10
|
+
export class PictureEditor extends HTMLElement {
|
|
11
|
+
constructor() {
|
|
12
|
+
super()
|
|
13
|
+
|
|
14
|
+
this.cropFromField = this.querySelector("[data-crop-from]")
|
|
15
|
+
this.cropSizeField = this.querySelector("[data-crop-size]")
|
|
16
|
+
this.pictureIdField = this.querySelector("[data-picture-id]")
|
|
17
|
+
this.targetSizeField = this.querySelector("[data-target-size]")
|
|
18
|
+
this.imageCropperField = this.querySelector("[data-image-cropper]")
|
|
19
|
+
this.image = this.querySelector("img")
|
|
20
|
+
this.pictureThumbnail = this.querySelector("alchemy-picture-thumbnail")
|
|
21
|
+
this.deleteButton = this.querySelector(".picture_tool.delete")
|
|
22
|
+
this.cropLink = this.querySelector(".crop_link")
|
|
23
23
|
|
|
24
24
|
this.targetSize = this.targetSizeField.dataset.targetSize
|
|
25
25
|
this.pictureId = this.pictureIdField.value
|
|
26
26
|
|
|
27
|
-
if (this.image) {
|
|
28
|
-
this.imageLoader = new ImageLoader(this.image)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
27
|
// The mutation observer is observing multiple fields that all get updated
|
|
32
28
|
// simultaneously. We only want to update the image once, so we debounce.
|
|
33
29
|
this.update = debounce(() => {
|
|
@@ -35,15 +31,19 @@ export class PictureEditor {
|
|
|
35
31
|
this.updateCropLink()
|
|
36
32
|
}, UPDATE_DELAY)
|
|
37
33
|
|
|
38
|
-
this.deleteButton
|
|
34
|
+
this.deleteButton?.addEventListener("click", this.removeImage.bind(this))
|
|
39
35
|
}
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
connectedCallback() {
|
|
38
|
+
this.observer = new MutationObserver(this.mutationCallback.bind(this))
|
|
39
|
+
|
|
40
|
+
this.observer.observe(this.cropFromField, { attributes: true })
|
|
41
|
+
this.observer.observe(this.cropSizeField, { attributes: true })
|
|
42
|
+
this.observer.observe(this.pictureIdField, { attributes: true })
|
|
43
|
+
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
observer.
|
|
46
|
-
observer.observe(this.pictureIdField, { attributes: true })
|
|
45
|
+
disconnectedCallback() {
|
|
46
|
+
this.observer.disconnect()
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
mutationCallback(mutationsList) {
|
|
@@ -60,10 +60,7 @@ export class PictureEditor {
|
|
|
60
60
|
updateImage() {
|
|
61
61
|
if (!this.pictureId) return
|
|
62
62
|
|
|
63
|
-
this.
|
|
64
|
-
this.image.removeAttribute("alt")
|
|
65
|
-
this.image.removeAttribute("src")
|
|
66
|
-
this.imageLoader.load(true)
|
|
63
|
+
this.pictureThumbnail.loading = true
|
|
67
64
|
get(Alchemy.routes.url_admin_picture_path(this.pictureId), {
|
|
68
65
|
crop: this.imageCropperEnabled,
|
|
69
66
|
crop_from: this.cropFrom,
|
|
@@ -72,9 +69,9 @@ export class PictureEditor {
|
|
|
72
69
|
size: THUMBNAIL_SIZE
|
|
73
70
|
})
|
|
74
71
|
.then(({ data }) => {
|
|
75
|
-
this.
|
|
76
|
-
this.image.alt = data.alt
|
|
77
|
-
this.image.title = data.title
|
|
72
|
+
this.pictureThumbnail.src = data.url
|
|
73
|
+
this.pictureThumbnail.image.alt = data.alt
|
|
74
|
+
this.pictureThumbnail.image.title = data.title
|
|
78
75
|
this.setElementDirty()
|
|
79
76
|
})
|
|
80
77
|
.catch((error) => {
|
|
@@ -83,15 +80,8 @@ export class PictureEditor {
|
|
|
83
80
|
})
|
|
84
81
|
}
|
|
85
82
|
|
|
86
|
-
ensureImage() {
|
|
87
|
-
const img = new Image()
|
|
88
|
-
this.thumbnailBackground.replaceChildren(img)
|
|
89
|
-
this.image = img
|
|
90
|
-
this.imageLoader = new ImageLoader(img)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
83
|
removeImage() {
|
|
94
|
-
this.
|
|
84
|
+
this.pictureThumbnail.innerHTML = IMAGE_PLACEHOLDER
|
|
95
85
|
this.pictureIdField.value = ""
|
|
96
86
|
this.image = null
|
|
97
87
|
this.cropLink.classList.add("disabled")
|
|
@@ -99,7 +89,7 @@ export class PictureEditor {
|
|
|
99
89
|
}
|
|
100
90
|
|
|
101
91
|
setElementDirty() {
|
|
102
|
-
this.
|
|
92
|
+
this.closest(".element-editor").setDirty(this)
|
|
103
93
|
}
|
|
104
94
|
|
|
105
95
|
updateCropLink() {
|
|
@@ -167,9 +157,4 @@ export class PictureEditor {
|
|
|
167
157
|
}
|
|
168
158
|
}
|
|
169
159
|
|
|
170
|
-
|
|
171
|
-
document.querySelectorAll(selector).forEach((node) => {
|
|
172
|
-
const thumbnail = new PictureEditor(node)
|
|
173
|
-
thumbnail.observe()
|
|
174
|
-
})
|
|
175
|
-
}
|
|
160
|
+
customElements.define("alchemy-picture-editor", PictureEditor)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Shows spinner while loading images and
|
|
2
|
+
// fades the image after its been loaded
|
|
3
|
+
|
|
4
|
+
import Spinner from "alchemy_admin/spinner"
|
|
5
|
+
|
|
6
|
+
export default class PictureThumbnail extends HTMLElement {
|
|
7
|
+
constructor() {
|
|
8
|
+
super()
|
|
9
|
+
|
|
10
|
+
this.classList.add("thumbnail_background")
|
|
11
|
+
this.spinner = new Spinner("small")
|
|
12
|
+
|
|
13
|
+
if (this.src) {
|
|
14
|
+
this.start()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
handleEvent(evt) {
|
|
19
|
+
switch (evt.type) {
|
|
20
|
+
case "load":
|
|
21
|
+
this.#onLoaded()
|
|
22
|
+
break
|
|
23
|
+
case "error":
|
|
24
|
+
this.#onError(evt)
|
|
25
|
+
break
|
|
26
|
+
default:
|
|
27
|
+
break
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
connectedCallback() {
|
|
32
|
+
if (this.image) {
|
|
33
|
+
this.replaceChildren(this.image)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
disconnectedCallback() {
|
|
38
|
+
this.image?.removeEventListener("load", this)
|
|
39
|
+
this.image?.removeEventListener("error", this)
|
|
40
|
+
this.stop()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
createImage(src = this.src, alt = this.name) {
|
|
44
|
+
this.image = new Image()
|
|
45
|
+
this.image.src = src
|
|
46
|
+
if (alt) {
|
|
47
|
+
this.image.alt = alt
|
|
48
|
+
}
|
|
49
|
+
this.image.loading = "lazy"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
start(src) {
|
|
53
|
+
this.createImage(src)
|
|
54
|
+
this.image.addEventListener("load", this)
|
|
55
|
+
this.image.addEventListener("error", this)
|
|
56
|
+
this.load()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
load() {
|
|
60
|
+
if (this.image?.complete) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
this.setAttribute("loading", "loading")
|
|
64
|
+
this.innerHTML = ""
|
|
65
|
+
this.spinner.spin(this)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
stop() {
|
|
69
|
+
this.classList.remove("loading")
|
|
70
|
+
this.spinner.stop()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#onLoaded() {
|
|
74
|
+
this.spinner.stop()
|
|
75
|
+
this.removeAttribute("loading")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#onError(evt) {
|
|
79
|
+
const message = `Could not load ${this.image.src}`
|
|
80
|
+
this.spinner.stop()
|
|
81
|
+
this.innerHTML = `
|
|
82
|
+
<sl-tooltip content="${message}">
|
|
83
|
+
<alchemy-icon name="alert" class="error"></alchemy-icon>
|
|
84
|
+
</sl-tooltip>
|
|
85
|
+
`
|
|
86
|
+
console.error(message, evt)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
set loading(value) {
|
|
90
|
+
value ? this.load() : this.stop()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
set src(src) {
|
|
94
|
+
this.start(src)
|
|
95
|
+
this.replaceChildren(this.image)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get name() {
|
|
99
|
+
return this.getAttribute("name")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get src() {
|
|
103
|
+
return this.getAttribute("src")
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
customElements.define("alchemy-picture-thumbnail", PictureThumbnail)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class PublishPageButton extends HTMLElement {
|
|
2
|
+
constructor() {
|
|
3
|
+
super()
|
|
4
|
+
this.addEventListener("submit", this)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
connectedCallback() {
|
|
8
|
+
document.addEventListener("alchemy:page-dirty", this)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
disconnectedCallback() {
|
|
12
|
+
document.removeEventListener("alchemy:page-dirty", this)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
handleEvent(event) {
|
|
16
|
+
switch (event.type) {
|
|
17
|
+
case "alchemy:page-dirty":
|
|
18
|
+
this.markDirty(event.detail)
|
|
19
|
+
break
|
|
20
|
+
case "submit":
|
|
21
|
+
this.button.loading = true
|
|
22
|
+
break
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
markDirty(detail) {
|
|
27
|
+
this.button.variant = "primary"
|
|
28
|
+
this.button.disabled = false
|
|
29
|
+
this.tooltip.content = detail.tooltip
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get button() {
|
|
33
|
+
return this.querySelector("sl-button")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get tooltip() {
|
|
37
|
+
return this.querySelector("sl-tooltip")
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
customElements.define("alchemy-publish-page-button", PublishPageButton)
|
|
@@ -10,7 +10,9 @@ class Select extends HTMLSelectElement {
|
|
|
10
10
|
allowClear: !!this.allowClear
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
-
if
|
|
13
|
+
// For single selects, remove the close button if allowClear is not set
|
|
14
|
+
// For multiple selects, always keep the close buttons
|
|
15
|
+
if (!this.allowClear && !this.multiple) {
|
|
14
16
|
this.#select2Element
|
|
15
17
|
.prev(".select2-container")
|
|
16
18
|
.find(".select2-search-choice-close")
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import Sortable from "sortablejs"
|
|
2
|
+
import { growl } from "alchemy_admin/growler"
|
|
3
|
+
import { patch } from "alchemy_admin/utils/ajax"
|
|
4
|
+
import { translate } from "alchemy_admin/i18n"
|
|
5
|
+
import pleaseWaitOverlay from "alchemy_admin/please_wait_overlay"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Custom element for the sitemap container
|
|
9
|
+
* Handles search/filter functionality and drag-and-drop sorting
|
|
10
|
+
*/
|
|
11
|
+
export class AlchemySitemap extends HTMLElement {
|
|
12
|
+
connectedCallback() {
|
|
13
|
+
this.searchInput = document.querySelector(".search_input_field")
|
|
14
|
+
this.clearButton = document.querySelector("#search_field_clear")
|
|
15
|
+
this.resultCounter = document.querySelector("#page_filter_result")
|
|
16
|
+
|
|
17
|
+
this.setupSearch()
|
|
18
|
+
|
|
19
|
+
// Wait for child custom elements to be defined before setting up sortables
|
|
20
|
+
requestAnimationFrame(() => {
|
|
21
|
+
this.setupSortables()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Set up MutationObserver to re-initialize sortables when children containers are added
|
|
25
|
+
this.observer = new MutationObserver((mutations) => {
|
|
26
|
+
mutations.forEach((mutation) => {
|
|
27
|
+
mutation.addedNodes.forEach((node) => {
|
|
28
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return
|
|
29
|
+
|
|
30
|
+
// If the added node itself is a children container, initialize it
|
|
31
|
+
if (node.classList?.contains("children")) {
|
|
32
|
+
this.setupSortable(node)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Also check for children containers nested within the added node
|
|
36
|
+
// This handles cases where a parent element with nested children is added at once
|
|
37
|
+
node
|
|
38
|
+
.querySelectorAll(".children")
|
|
39
|
+
.forEach((el) => this.setupSortable(el))
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Observe the sitemap for added nodes
|
|
45
|
+
this.observer.observe(this, {
|
|
46
|
+
childList: true,
|
|
47
|
+
subtree: true
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
disconnectedCallback() {
|
|
52
|
+
this.teardownSearch()
|
|
53
|
+
this.observer?.disconnect()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setupSearch() {
|
|
57
|
+
this.searchInput?.addEventListener("input", this)
|
|
58
|
+
this.clearButton?.addEventListener("click", this)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
teardownSearch() {
|
|
62
|
+
this.searchInput?.removeEventListener("input", this)
|
|
63
|
+
this.clearButton?.removeEventListener("click", this)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
handleEvent(event) {
|
|
67
|
+
if (event.type === "input" && event.target === this.searchInput) {
|
|
68
|
+
this.handleSearch(event)
|
|
69
|
+
} else if (event.type === "click" && event.target === this.clearButton) {
|
|
70
|
+
this.handleClearSearch(event)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
handleSearch(event) {
|
|
75
|
+
const term = event.target.value.toLowerCase().trim()
|
|
76
|
+
|
|
77
|
+
if (term === "") {
|
|
78
|
+
this.clearFilter()
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.filterPages(term)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
filterPages(term) {
|
|
86
|
+
const allPages = this.querySelectorAll(".sitemap_page")
|
|
87
|
+
let matchCount = 0
|
|
88
|
+
let firstMatch = null
|
|
89
|
+
|
|
90
|
+
allPages.forEach((pageElement) => {
|
|
91
|
+
const pageName = pageElement.getAttribute("name") || ""
|
|
92
|
+
|
|
93
|
+
if (pageName.toLowerCase().includes(term)) {
|
|
94
|
+
pageElement.classList.add("highlight")
|
|
95
|
+
pageElement.classList.remove("no-match")
|
|
96
|
+
matchCount++
|
|
97
|
+
if (!firstMatch) firstMatch = pageElement
|
|
98
|
+
} else {
|
|
99
|
+
pageElement.classList.remove("highlight")
|
|
100
|
+
pageElement.classList.add("no-match")
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Update result counter
|
|
105
|
+
|
|
106
|
+
if (matchCount === 1) {
|
|
107
|
+
this.resultCounter.textContent = `1 ${translate("page_found")}`
|
|
108
|
+
this.resultCounter.style.display = "block"
|
|
109
|
+
} else if (matchCount > 1) {
|
|
110
|
+
this.resultCounter.textContent = `${matchCount} ${translate("pages_found")}`
|
|
111
|
+
this.resultCounter.style.display = "block"
|
|
112
|
+
} else {
|
|
113
|
+
this.resultCounter.style.display = "none"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Scroll first match into view
|
|
117
|
+
if (firstMatch) {
|
|
118
|
+
firstMatch.scrollIntoView({ behavior: "smooth", block: "center" })
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
clearFilter() {
|
|
123
|
+
const allPages = this.querySelectorAll(".sitemap_page")
|
|
124
|
+
allPages.forEach((pageElement) => {
|
|
125
|
+
pageElement.classList.remove("highlight", "no-match")
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
this.resultCounter.style.display = "none"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
handleClearSearch(event) {
|
|
132
|
+
event.preventDefault()
|
|
133
|
+
this.searchInput.value = ""
|
|
134
|
+
this.clearFilter()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
setupSortable(container) {
|
|
138
|
+
new Sortable(container, {
|
|
139
|
+
group: "pages",
|
|
140
|
+
animation: 150,
|
|
141
|
+
fallbackOnBody: true,
|
|
142
|
+
swapThreshold: 0.65,
|
|
143
|
+
handle: ".page-icon.handle",
|
|
144
|
+
draggable: "alchemy-page-node",
|
|
145
|
+
onEnd: (evt) => this.handleSort(evt)
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
setupSortables() {
|
|
150
|
+
const sortables = this.querySelectorAll(".children")
|
|
151
|
+
sortables.forEach((el) => this.setupSortable(el))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async handleSort(evt) {
|
|
155
|
+
// Only proceed if actually moved to different position/container
|
|
156
|
+
if (evt.from === evt.to && evt.oldIndex === evt.newIndex) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// evt.item is the <alchemy-page-node> element being dragged
|
|
161
|
+
const pageNode = evt.item
|
|
162
|
+
const pageId = pageNode.pageId
|
|
163
|
+
const url = Alchemy.routes.move_admin_page_path(pageId)
|
|
164
|
+
const data = {
|
|
165
|
+
target_parent_id: evt.to.dataset.parentId,
|
|
166
|
+
new_position: evt.newIndex
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
pleaseWaitOverlay(true)
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const response = await patch(url, data)
|
|
173
|
+
const pageData = await response.data
|
|
174
|
+
|
|
175
|
+
// Update the URL path of the moved page
|
|
176
|
+
const pageEl = pageNode.querySelector(`#page_${pageId}`)
|
|
177
|
+
if (pageEl) {
|
|
178
|
+
const urlPathEl = pageEl.querySelector(".sitemap_url")
|
|
179
|
+
if (urlPathEl && pageData.url_path) {
|
|
180
|
+
urlPathEl.textContent = pageData.url_path
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Update folder icons for affected parent pages
|
|
185
|
+
this.updateFolderIcons(evt.from, evt.to)
|
|
186
|
+
|
|
187
|
+
growl(translate("Successfully moved page"))
|
|
188
|
+
} catch (error) {
|
|
189
|
+
growl(error.message || error, "error")
|
|
190
|
+
// Revert the DOM change by reloading on error
|
|
191
|
+
window.location.reload()
|
|
192
|
+
} finally {
|
|
193
|
+
pleaseWaitOverlay(false)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
updateFolderIcons(fromContainer, toContainer) {
|
|
198
|
+
// Update folder icon for source parent (might now have no children)
|
|
199
|
+
const fromParent = fromContainer.closest("alchemy-page-node")
|
|
200
|
+
fromParent?.updateFolderButton()
|
|
201
|
+
|
|
202
|
+
// Update folder icon for destination parent (now definitely has children)
|
|
203
|
+
if (fromContainer !== toContainer) {
|
|
204
|
+
const toParent = toContainer.closest("alchemy-page-node")
|
|
205
|
+
toParent?.updateFolderButton()
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
customElements.define("alchemy-sitemap", AlchemySitemap)
|
|
@@ -2,10 +2,11 @@ import Sortable from "sortablejs"
|
|
|
2
2
|
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
|
+
import { dispatchPageDirtyEvent } from "alchemy_admin/components/element_editor"
|
|
5
6
|
|
|
6
7
|
const SORTABLE_OPTIONS = {
|
|
7
8
|
draggable: ".element-editor",
|
|
8
|
-
handle: ".element-handle",
|
|
9
|
+
handle: ".element-handle.draggable",
|
|
9
10
|
ghostClass: "dragged",
|
|
10
11
|
animation: 150,
|
|
11
12
|
swapThreshold: 0.65,
|
|
@@ -38,6 +39,9 @@ function onSort(event) {
|
|
|
38
39
|
post(Alchemy.routes.order_admin_elements_path, params).then((response) => {
|
|
39
40
|
const data = response.data
|
|
40
41
|
growl(data.message)
|
|
42
|
+
if (data.pageHasUnpublishedChanges) {
|
|
43
|
+
dispatchPageDirtyEvent(data)
|
|
44
|
+
}
|
|
41
45
|
reloadPreview()
|
|
42
46
|
item.updateTitle(data.preview_text)
|
|
43
47
|
})
|
|
@@ -51,31 +55,24 @@ function onEnd() {
|
|
|
51
55
|
)
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
.
|
|
60
|
-
|
|
58
|
+
class SortableElements extends HTMLElement {
|
|
59
|
+
connectedCallback() {
|
|
60
|
+
const group = {
|
|
61
|
+
name: this.dataset.elementName,
|
|
62
|
+
put(to, _from, item) {
|
|
63
|
+
return to.el.dataset.droppableElements
|
|
64
|
+
.split(" ")
|
|
65
|
+
.includes(item.dataset.elementName)
|
|
66
|
+
}
|
|
61
67
|
}
|
|
68
|
+
new Sortable(this, {
|
|
69
|
+
...SORTABLE_OPTIONS,
|
|
70
|
+
onStart,
|
|
71
|
+
onSort,
|
|
72
|
+
onEnd,
|
|
73
|
+
group
|
|
74
|
+
})
|
|
62
75
|
}
|
|
63
|
-
new Sortable(element, {
|
|
64
|
-
...SORTABLE_OPTIONS,
|
|
65
|
-
...options,
|
|
66
|
-
onStart,
|
|
67
|
-
onSort,
|
|
68
|
-
onEnd,
|
|
69
|
-
group
|
|
70
|
-
})
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
if (selector == null) selector = ".sortable-elements"
|
|
75
|
-
|
|
76
|
-
const sortable_areas = document.querySelectorAll(selector, {
|
|
77
|
-
direction: "vertical"
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
sortable_areas.forEach((element) => createSortable(element))
|
|
81
|
-
}
|
|
78
|
+
customElements.define("alchemy-sortable-elements", SortableElements)
|
|
@@ -162,11 +162,16 @@ class Tinymce extends AlchemyHTMLElement {
|
|
|
162
162
|
const config = this.getAttribute(attributeName)
|
|
163
163
|
const key = attributeName.replaceAll("-", "_")
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
// Handle boolean HTML attributes (e.g., readonly="readonly" or readonly="")
|
|
166
|
+
if (config === attributeName || config === "") {
|
|
167
|
+
customConfig[key] = true
|
|
168
|
+
} else {
|
|
169
|
+
try {
|
|
170
|
+
customConfig[key] = JSON.parse(config)
|
|
171
|
+
} catch (e) {
|
|
172
|
+
// also string values as parameter
|
|
173
|
+
customConfig[key] = config
|
|
174
|
+
}
|
|
170
175
|
}
|
|
171
176
|
}
|
|
172
177
|
})
|
|
@@ -21,6 +21,32 @@ export class Uploader extends AlchemyHTMLElement {
|
|
|
21
21
|
if (this.dropzone) {
|
|
22
22
|
this._dragAndDropBehavior()
|
|
23
23
|
}
|
|
24
|
+
this.addEventListener("Alchemy.upload.successful", this)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
handleEvent(evt) {
|
|
28
|
+
switch (evt.type) {
|
|
29
|
+
case "Alchemy.upload.successful":
|
|
30
|
+
this._handleUploadComplete()
|
|
31
|
+
break
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_handleUploadComplete() {
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
const url = this.redirectUrl
|
|
38
|
+
const turboFrame = this.closest("turbo-frame")
|
|
39
|
+
this.uploadProgress.visible = false
|
|
40
|
+
|
|
41
|
+
if (!url) return
|
|
42
|
+
|
|
43
|
+
if (turboFrame) {
|
|
44
|
+
turboFrame.setAttribute("src", url)
|
|
45
|
+
turboFrame.reload()
|
|
46
|
+
} else {
|
|
47
|
+
Turbo.visit(url)
|
|
48
|
+
}
|
|
49
|
+
}, 750)
|
|
24
50
|
}
|
|
25
51
|
|
|
26
52
|
/**
|
|
@@ -126,6 +152,10 @@ export class Uploader extends AlchemyHTMLElement {
|
|
|
126
152
|
get fileInput() {
|
|
127
153
|
return this.querySelector("input[type='file']")
|
|
128
154
|
}
|
|
155
|
+
|
|
156
|
+
get redirectUrl() {
|
|
157
|
+
return this.getAttribute("redirect-url")
|
|
158
|
+
}
|
|
129
159
|
}
|
|
130
160
|
|
|
131
161
|
customElements.define("alchemy-uploader", Uploader)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import ImageLoader from "alchemy_admin/image_loader"
|
|
2
1
|
import { Dialog } from "alchemy_admin/dialog"
|
|
3
2
|
|
|
4
3
|
export default class ImageOverlay extends Dialog {
|
|
@@ -7,7 +6,6 @@ export default class ImageOverlay extends Dialog {
|
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
init() {
|
|
10
|
-
ImageLoader.init(this.dialog_body[0])
|
|
11
9
|
$(".zoomed-picture-background").on("click", (e) => {
|
|
12
10
|
e.stopPropagation()
|
|
13
11
|
if (e.target.nodeName === "IMG") {
|
|
@@ -45,9 +45,6 @@ export default function Initializer() {
|
|
|
45
45
|
$(this.form).submit()
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
// Attaches the image loader on all images
|
|
49
|
-
Alchemy.ImageLoader("#main_content")
|
|
50
|
-
|
|
51
48
|
// Override the filter of keymaster.js so we can blur the fields on esc key.
|
|
52
49
|
key.filter = function (event) {
|
|
53
50
|
let tagName = (event.target || event.srcElement).tagName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var n=Handlebars.template
|
|
1
|
+
(()=>{var n=Handlebars.template;(Handlebars.templates=Handlebars.templates||{})["node_folder.hbs"]=n({1:function(n,e,l,a,r){return"right"},3:function(n,e,l,a,r){return"down"},compiler:[8,">= 4.3.0"],main:function(n,e,l,a,r){var o,t=n.lambda,d=n.escapeExpression,u=n.lookupProperty||function(n,e){if(Object.prototype.hasOwnProperty.call(n,e))return n[e]};return'<a class="node_folder" data-record-id="'+d(t(null!=(o=null!=e?u(e,"node"):e)?u(o,"id"):o,e))+'" data-record-type="'+d(t(null!=(o=null!=e?u(e,"node"):e)?u(o,"type"):o,e))+'">\n <alchemy-icon name="arrow-'+(null!=(o=u(l,"if").call(null!=e?e:n.nullContext||{},null!=(o=null!=e?u(e,"node"):e)?u(o,"folded"):o,{name:"if",hash:{},fn:n.program(1,r,0),inverse:n.program(3,r,0),data:r,loc:{start:{line:2,column:28},end:{line:2,column:72}}}))?o:"")+'-s"></alchemy-icon>\n</a>\n'},useData:!0})})();
|