alchemy_cms 8.0.11 → 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 +17 -13
- 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/languages_controller.rb +0 -3
- 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 +36 -42
- 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/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/link_dialog.js +1 -6
- 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/language/code.rb +0 -1
- 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 +13 -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/navigation.scss +1 -1
- 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/dashboard/widgets/_locked_pages.html.erb +1 -1
- 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 -6
- 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 -3
- 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 +2 -3
- 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 +104 -84
- 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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const formatItem = (object) => {
|
|
2
|
+
const optionEl = object.element[0]
|
|
3
|
+
const swatch = optionEl.dataset.swatch || optionEl.value
|
|
4
|
+
const customColor = optionEl.value === "custom_color"
|
|
5
|
+
const colorIndicator = customColor
|
|
6
|
+
? `<alchemy-icon name="palette"></alchemy-icon>`
|
|
7
|
+
: `<span class="color-indicator" style="--color: ${swatch}"></span>`
|
|
8
|
+
|
|
9
|
+
return `
|
|
10
|
+
<div class="select-color-option">
|
|
11
|
+
${colorIndicator}
|
|
12
|
+
<span>${object.text}</span>
|
|
13
|
+
</div>`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class ColorSelect extends HTMLElement {
|
|
17
|
+
connectedCallback() {
|
|
18
|
+
if (this.select) {
|
|
19
|
+
this.#initializeSelect2()
|
|
20
|
+
$(this.select).on("change", (event) =>
|
|
21
|
+
this.#toggleColorPicker(event.val === "custom_color")
|
|
22
|
+
)
|
|
23
|
+
} else {
|
|
24
|
+
this.colorInput?.addEventListener("input", this)
|
|
25
|
+
this.textInput?.addEventListener("input", this)
|
|
26
|
+
this.#toggleColorPicker(true)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
handleEvent(event) {
|
|
31
|
+
switch (event.target) {
|
|
32
|
+
case this.colorInput:
|
|
33
|
+
this.textInput.value = this.colorInput.value
|
|
34
|
+
break
|
|
35
|
+
case this.textInput:
|
|
36
|
+
this.colorInput.value = this.textInput.value
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
disconnectedCallback() {
|
|
42
|
+
this.colorInput?.removeEventListener("input", this)
|
|
43
|
+
this.textInput?.removeEventListener("input", this)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#initializeSelect2() {
|
|
47
|
+
this.select.classList.add("alchemy_selectbox")
|
|
48
|
+
const options = {
|
|
49
|
+
minimumResultsForSearch: 10,
|
|
50
|
+
formatResult: formatItem,
|
|
51
|
+
formatSelection: formatItem
|
|
52
|
+
}
|
|
53
|
+
$(this.select).select2(options)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#toggleColorPicker(enabled = true) {
|
|
57
|
+
this.colorInput.disabled = !enabled
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get colorInput() {
|
|
61
|
+
return this.querySelector("input[type='color']")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get textInput() {
|
|
65
|
+
return this.querySelector("input[type='text']")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get select() {
|
|
69
|
+
return this.querySelector("select")
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
customElements.define("alchemy-color-select", ColorSelect)
|
|
@@ -3,11 +3,12 @@ import { removeTab } from "alchemy_admin/fixed_elements"
|
|
|
3
3
|
import { growl } from "alchemy_admin/growler"
|
|
4
4
|
import { reloadPreview } from "alchemy_admin/components/preview_window"
|
|
5
5
|
import { openConfirmDialog } from "alchemy_admin/confirm_dialog"
|
|
6
|
+
import { dispatchPageDirtyEvent } from "alchemy_admin/components/element_editor"
|
|
6
7
|
|
|
7
8
|
export class DeleteElementButton extends HTMLElement {
|
|
8
9
|
constructor() {
|
|
9
10
|
super()
|
|
10
|
-
this.addEventListener("click", this)
|
|
11
|
+
this.button?.addEventListener("click", this)
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
async handleEvent() {
|
|
@@ -18,7 +19,7 @@ export class DeleteElementButton extends HTMLElement {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
#removeElement(
|
|
22
|
+
#removeElement(data) {
|
|
22
23
|
const elementEditor = this.closest("alchemy-element-editor")
|
|
23
24
|
elementEditor.addEventListener("transitionend", () => {
|
|
24
25
|
if (elementEditor.fixed) {
|
|
@@ -27,7 +28,10 @@ export class DeleteElementButton extends HTMLElement {
|
|
|
27
28
|
elementEditor.remove()
|
|
28
29
|
})
|
|
29
30
|
elementEditor.classList.add("dismiss")
|
|
30
|
-
growl(
|
|
31
|
+
growl(data.message)
|
|
32
|
+
if (data.pageHasUnpublishedChanges) {
|
|
33
|
+
dispatchPageDirtyEvent(data)
|
|
34
|
+
}
|
|
31
35
|
reloadPreview()
|
|
32
36
|
}
|
|
33
37
|
|
|
@@ -38,6 +42,10 @@ export class DeleteElementButton extends HTMLElement {
|
|
|
38
42
|
get message() {
|
|
39
43
|
return this.getAttribute("message")
|
|
40
44
|
}
|
|
45
|
+
|
|
46
|
+
get button() {
|
|
47
|
+
return this.querySelector("button")
|
|
48
|
+
}
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
customElements.define("alchemy-delete-element-button", DeleteElementButton)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { patch } from "alchemy_admin/utils/ajax"
|
|
2
2
|
import { reloadPreview } from "alchemy_admin/components/preview_window"
|
|
3
3
|
import { growl } from "alchemy_admin/growler"
|
|
4
|
+
import { dispatchPageDirtyEvent } from "alchemy_admin/components/element_editor"
|
|
4
5
|
|
|
5
6
|
export class PublishElementButton extends HTMLElement {
|
|
6
7
|
constructor() {
|
|
@@ -14,9 +15,13 @@ export class PublishElementButton extends HTMLElement {
|
|
|
14
15
|
if (elementEditor === this.elementEditor) {
|
|
15
16
|
patch(Alchemy.routes.publish_admin_element_path(this.elementId))
|
|
16
17
|
.then((response) => {
|
|
17
|
-
|
|
18
|
-
this.
|
|
18
|
+
const data = response.data
|
|
19
|
+
this.elementEditor.published = data.public
|
|
20
|
+
this.tooltip.setAttribute("content", data.label)
|
|
19
21
|
reloadPreview()
|
|
22
|
+
if (data.pageHasUnpublishedChanges) {
|
|
23
|
+
dispatchPageDirtyEvent(data)
|
|
24
|
+
}
|
|
20
25
|
})
|
|
21
26
|
.catch((error) => growl(error.message, "error"))
|
|
22
27
|
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import ImageLoader from "alchemy_admin/image_loader"
|
|
2
|
-
import fileEditors from "alchemy_admin/file_editors"
|
|
3
|
-
import pictureEditors from "alchemy_admin/picture_editors"
|
|
4
|
-
import SortableElements from "alchemy_admin/sortable_elements"
|
|
5
1
|
import IngredientAnchorLink from "alchemy_admin/ingredient_anchor_link"
|
|
6
2
|
import { post } from "alchemy_admin/utils/ajax"
|
|
7
3
|
import { createHtmlElement } from "alchemy_admin/utils/dom_helpers"
|
|
@@ -10,6 +6,14 @@ import { growl } from "alchemy_admin/growler"
|
|
|
10
6
|
import "alchemy_admin/components/element_editor/publish_element_button"
|
|
11
7
|
import "alchemy_admin/components/element_editor/delete_element_button"
|
|
12
8
|
|
|
9
|
+
export function dispatchPageDirtyEvent(data) {
|
|
10
|
+
document.dispatchEvent(
|
|
11
|
+
new CustomEvent("alchemy:page-dirty", {
|
|
12
|
+
detail: { tooltip: data.publishButtonTooltip }
|
|
13
|
+
})
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
export class ElementEditor extends HTMLElement {
|
|
14
18
|
constructor() {
|
|
15
19
|
super()
|
|
@@ -50,14 +54,6 @@ export class ElementEditor extends HTMLElement {
|
|
|
50
54
|
})
|
|
51
55
|
this.removeAttribute("created")
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
// Init GUI elements
|
|
55
|
-
ImageLoader.init(this)
|
|
56
|
-
fileEditors(
|
|
57
|
-
`#${this.id} .ingredient-editor.file, #${this.id} .ingredient-editor.audio, #${this.id} .ingredient-editor.video`
|
|
58
|
-
)
|
|
59
|
-
pictureEditors(`#${this.id} .ingredient-editor.picture`)
|
|
60
|
-
SortableElements(`#${this.id} .nested-elements`)
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
handleEvent(event) {
|
|
@@ -163,6 +159,9 @@ export class ElementEditor extends HTMLElement {
|
|
|
163
159
|
data.ingredientAnchors.forEach((anchor) => {
|
|
164
160
|
IngredientAnchorLink.updateIcon(anchor.ingredientId, anchor.active)
|
|
165
161
|
})
|
|
162
|
+
if (data.pageHasUnpublishedChanges) {
|
|
163
|
+
dispatchPageDirtyEvent(data)
|
|
164
|
+
}
|
|
166
165
|
}
|
|
167
166
|
}
|
|
168
167
|
|
|
@@ -1,43 +1,65 @@
|
|
|
1
1
|
import { hightlightTerm } from "alchemy_admin/components/remote_select"
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
return
|
|
3
|
+
const formatSelection = (option) => {
|
|
4
|
+
return `
|
|
5
|
+
<div class="element-select-name">${option.icon} ${option.name}</div>
|
|
6
|
+
`
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
const formatItem = (icon, name, hint) => {
|
|
10
|
+
const description = hint
|
|
11
|
+
? `<div class="element-select-description">${hint}</div>`
|
|
12
|
+
: ""
|
|
13
|
+
return `
|
|
14
|
+
<div class="element-select-item">
|
|
15
|
+
${formatSelection({ icon, name })}
|
|
16
|
+
${description}
|
|
17
|
+
</div>
|
|
18
|
+
`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class ElementSelect extends HTMLElement {
|
|
8
22
|
constructor() {
|
|
9
23
|
super()
|
|
10
|
-
this.classList.add("alchemy_selectbox")
|
|
11
24
|
}
|
|
12
25
|
|
|
13
26
|
connectedCallback() {
|
|
14
|
-
const
|
|
27
|
+
const results = this.options
|
|
15
28
|
const options = {
|
|
16
29
|
minimumResultsForSearch: 3,
|
|
17
30
|
dropdownAutoWidth: true,
|
|
18
31
|
data() {
|
|
19
|
-
return { results
|
|
32
|
+
return { results }
|
|
20
33
|
},
|
|
21
34
|
formatResult: (option, _el, search) => {
|
|
22
35
|
let text
|
|
23
36
|
|
|
24
|
-
if (option.id === "") return option.
|
|
37
|
+
if (option.id === "") return option.name
|
|
25
38
|
if (search.term !== "") {
|
|
26
|
-
text = hightlightTerm(option.
|
|
39
|
+
text = hightlightTerm(option.name, search.term)
|
|
27
40
|
} else {
|
|
28
|
-
text = option.
|
|
41
|
+
text = option.name
|
|
29
42
|
}
|
|
30
43
|
|
|
31
|
-
return formatItem(option.icon, text)
|
|
44
|
+
return formatItem(option.icon, text, option.hint)
|
|
32
45
|
},
|
|
33
|
-
formatSelection
|
|
34
|
-
|
|
35
|
-
}
|
|
46
|
+
formatSelection,
|
|
47
|
+
placeholder: this.placeholder
|
|
36
48
|
}
|
|
37
|
-
$(this).select2(options)
|
|
49
|
+
$(this.inputField).select2(options)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get options() {
|
|
53
|
+
return JSON.parse(this.getAttribute("options"))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get placeholder() {
|
|
57
|
+
return this.getAttribute("placeholder")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get inputField() {
|
|
61
|
+
return this.querySelector("input")
|
|
38
62
|
}
|
|
39
63
|
}
|
|
40
64
|
|
|
41
|
-
customElements.define("alchemy-element-select", ElementSelect
|
|
42
|
-
extends: "input"
|
|
43
|
-
})
|
|
65
|
+
customElements.define("alchemy-element-select", ElementSelect)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import SortableElements from "alchemy_admin/sortable_elements"
|
|
2
1
|
import { ElementEditor } from "alchemy_admin/components/element_editor"
|
|
3
2
|
|
|
4
3
|
class ElementsWindow extends HTMLElement {
|
|
@@ -18,7 +17,6 @@ class ElementsWindow extends HTMLElement {
|
|
|
18
17
|
if (window.location.hash) {
|
|
19
18
|
this.focusElementEditor(window.location.hash)
|
|
20
19
|
}
|
|
21
|
-
SortableElements()
|
|
22
20
|
this.resize()
|
|
23
21
|
}
|
|
24
22
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class FileEditor extends HTMLElement {
|
|
2
|
+
constructor() {
|
|
3
|
+
super()
|
|
4
|
+
this.deleteLink = this.querySelector(".remove_file_link")
|
|
5
|
+
this.fileIcon = this.querySelector(".file_icon")
|
|
6
|
+
this.fileName = this.querySelector(".file_name")
|
|
7
|
+
this.formFieldId = this.deleteLink?.dataset.formFieldId
|
|
8
|
+
this.formField = this.querySelector(`#${this.formFieldId}`)
|
|
9
|
+
this.deleteLink?.addEventListener("click", this)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
handleEvent(event) {
|
|
13
|
+
if (event.type === "click") this.removeFile()
|
|
14
|
+
event.stopPropagation()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
removeFile() {
|
|
18
|
+
this.formField.value = ""
|
|
19
|
+
this.fileIcon.innerHTML = ""
|
|
20
|
+
this.fileName.innerHTML = ""
|
|
21
|
+
this.deleteLink?.classList.add("hidden")
|
|
22
|
+
this.closest("alchemy-element-editor").setDirty(this.formField)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
customElements.define("alchemy-file-editor", FileEditor)
|
|
@@ -7,6 +7,7 @@ import "alchemy_admin/components/auto_submit"
|
|
|
7
7
|
import "alchemy_admin/components/button"
|
|
8
8
|
import "alchemy_admin/components/char_counter"
|
|
9
9
|
import "alchemy_admin/components/clipboard_button"
|
|
10
|
+
import "alchemy_admin/components/color_select"
|
|
10
11
|
import "alchemy_admin/components/datepicker"
|
|
11
12
|
import "alchemy_admin/components/dialog_link"
|
|
12
13
|
import "alchemy_admin/components/dom_id_select"
|
|
@@ -14,6 +15,7 @@ import "alchemy_admin/components/element_editor"
|
|
|
14
15
|
import "alchemy_admin/components/element_select"
|
|
15
16
|
import "alchemy_admin/components/elements_window"
|
|
16
17
|
import "alchemy_admin/components/elements_window_handle"
|
|
18
|
+
import "alchemy_admin/components/file_editor"
|
|
17
19
|
import "alchemy_admin/components/list_filter"
|
|
18
20
|
import "alchemy_admin/components/message"
|
|
19
21
|
import "alchemy_admin/components/growl"
|
|
@@ -23,10 +25,17 @@ import "alchemy_admin/components/link_buttons"
|
|
|
23
25
|
import "alchemy_admin/components/node_select"
|
|
24
26
|
import "alchemy_admin/components/uploader"
|
|
25
27
|
import "alchemy_admin/components/overlay"
|
|
28
|
+
import "alchemy_admin/components/page_node"
|
|
29
|
+
import "alchemy_admin/components/page_publication_fields"
|
|
26
30
|
import "alchemy_admin/components/page_select"
|
|
27
31
|
import "alchemy_admin/components/picture_description_select"
|
|
32
|
+
import "alchemy_admin/components/picture_editor"
|
|
33
|
+
import "alchemy_admin/components/picture_thumbnail"
|
|
28
34
|
import "alchemy_admin/components/preview_window"
|
|
35
|
+
import "alchemy_admin/components/publish_page_button"
|
|
29
36
|
import "alchemy_admin/components/select"
|
|
37
|
+
import "alchemy_admin/components/sitemap"
|
|
38
|
+
import "alchemy_admin/components/sortable_elements"
|
|
30
39
|
import "alchemy_admin/components/spinner"
|
|
31
40
|
import "alchemy_admin/components/tags_autocomplete"
|
|
32
41
|
import "alchemy_admin/components/tinymce"
|
|
@@ -1,14 +1,27 @@
|
|
|
1
|
+
const DEFAULT_DEBOUNCE_TIME = 150
|
|
2
|
+
|
|
1
3
|
class ListFilter extends HTMLElement {
|
|
4
|
+
#debounceTimer
|
|
5
|
+
|
|
2
6
|
constructor() {
|
|
3
7
|
super()
|
|
4
8
|
this.#attachEvents()
|
|
5
9
|
}
|
|
6
10
|
|
|
7
11
|
#attachEvents() {
|
|
12
|
+
if (this.hotkey) {
|
|
13
|
+
key(this.hotkey, () => {
|
|
14
|
+
this.filterField.focus()
|
|
15
|
+
return false
|
|
16
|
+
})
|
|
17
|
+
}
|
|
8
18
|
this.filterField.addEventListener("keyup", () => {
|
|
9
|
-
|
|
10
|
-
this
|
|
11
|
-
|
|
19
|
+
clearTimeout(this.#debounceTimer)
|
|
20
|
+
this.#debounceTimer = setTimeout(() => {
|
|
21
|
+
const term = this.filterField.value
|
|
22
|
+
this.clearButton.style.visibility = term ? "visible" : "hidden"
|
|
23
|
+
this.filter(term)
|
|
24
|
+
}, this.debounceTime)
|
|
12
25
|
})
|
|
13
26
|
this.clearButton.addEventListener("click", (e) => {
|
|
14
27
|
e.preventDefault()
|
|
@@ -23,25 +36,53 @@ class ListFilter extends HTMLElement {
|
|
|
23
36
|
})
|
|
24
37
|
}
|
|
25
38
|
|
|
39
|
+
disconnectedCallback() {
|
|
40
|
+
if (this.hotkey) {
|
|
41
|
+
key.unbind(this.hotkey)
|
|
42
|
+
}
|
|
43
|
+
key.unbind("esc", "list_filter")
|
|
44
|
+
}
|
|
45
|
+
|
|
26
46
|
filter(term) {
|
|
27
47
|
if (term === "") {
|
|
28
48
|
this.clearButton.style.visibility = "hidden"
|
|
29
49
|
}
|
|
30
50
|
|
|
51
|
+
const matchedItems = []
|
|
52
|
+
const itemsToShow = new Set()
|
|
53
|
+
const lowerTerm = term.toLowerCase()
|
|
54
|
+
|
|
55
|
+
// First pass: find matching items and mark their ancestors as visible too
|
|
31
56
|
this.items.forEach((item) => {
|
|
32
57
|
const name = item.getAttribute(this.nameAttribute)?.toLowerCase()
|
|
33
58
|
// indexOf is much faster then match()
|
|
34
|
-
if (name.indexOf(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
if (name.indexOf(lowerTerm) !== -1) {
|
|
60
|
+
matchedItems.push(item)
|
|
61
|
+
itemsToShow.add(item)
|
|
62
|
+
// Mark ancestor items as visible so nested matches stay visible
|
|
63
|
+
let ancestor = item.parentElement?.closest(this.itemsSelector)
|
|
64
|
+
while (ancestor) {
|
|
65
|
+
itemsToShow.add(ancestor)
|
|
66
|
+
ancestor = ancestor.parentElement?.closest(this.itemsSelector)
|
|
67
|
+
}
|
|
38
68
|
}
|
|
39
69
|
})
|
|
70
|
+
|
|
71
|
+
// Second pass: apply visibility
|
|
72
|
+
this.items.forEach((item) => {
|
|
73
|
+
item.classList.toggle("hidden", !itemsToShow.has(item))
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Scroll into view if only one match
|
|
77
|
+
if (matchedItems.length === 1) {
|
|
78
|
+
matchedItems[0].scrollIntoView({ behavior: "smooth", block: "nearest" })
|
|
79
|
+
}
|
|
40
80
|
}
|
|
41
81
|
|
|
42
82
|
clear() {
|
|
43
83
|
this.filterField.value = ""
|
|
44
|
-
this.
|
|
84
|
+
this.clearButton.style.visibility = "hidden"
|
|
85
|
+
this.items.forEach((item) => item.classList.remove("hidden"))
|
|
45
86
|
}
|
|
46
87
|
|
|
47
88
|
get nameAttribute() {
|
|
@@ -63,6 +104,14 @@ class ListFilter extends HTMLElement {
|
|
|
63
104
|
get itemsSelector() {
|
|
64
105
|
return this.getAttribute("items-selector")
|
|
65
106
|
}
|
|
107
|
+
|
|
108
|
+
get debounceTime() {
|
|
109
|
+
return parseInt(this.getAttribute("debounce-time")) || DEFAULT_DEBOUNCE_TIME
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get hotkey() {
|
|
113
|
+
return this.getAttribute("hotkey")
|
|
114
|
+
}
|
|
66
115
|
}
|
|
67
116
|
|
|
68
117
|
customElements.define("alchemy-list-filter", ListFilter)
|
|
@@ -26,7 +26,7 @@ class Message extends HTMLElement {
|
|
|
26
26
|
if (this.dismissable && this.type !== "error") {
|
|
27
27
|
setTimeout(() => {
|
|
28
28
|
this.dismiss()
|
|
29
|
-
}, this.
|
|
29
|
+
}, this.dismissDelay)
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -43,8 +43,10 @@ class Message extends HTMLElement {
|
|
|
43
43
|
return this.getAttribute("type") || "notice"
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
get
|
|
47
|
-
return parseInt(
|
|
46
|
+
get dismissDelay() {
|
|
47
|
+
return parseInt(
|
|
48
|
+
this.noticesWrapper?.dataset.autoDismissDelay || DISMISS_DELAY
|
|
49
|
+
)
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
get iconName() {
|
|
@@ -64,6 +66,10 @@ class Message extends HTMLElement {
|
|
|
64
66
|
return this.type
|
|
65
67
|
}
|
|
66
68
|
}
|
|
69
|
+
|
|
70
|
+
get noticesWrapper() {
|
|
71
|
+
return this.closest("#flash_notices")
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
customElements.define("alchemy-message", Message)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { patch } from "alchemy_admin/utils/ajax"
|
|
2
|
+
import { growl } from "alchemy_admin/growler"
|
|
3
|
+
import Spinner from "alchemy_admin/spinner"
|
|
4
|
+
|
|
5
|
+
const BUTTON = "BUTTON"
|
|
6
|
+
const SPAN = "SPAN"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Custom element for page nodes in the sitemap tree
|
|
10
|
+
* Handles folding/unfolding of page children
|
|
11
|
+
*/
|
|
12
|
+
export class AlchemyPageNode extends HTMLElement {
|
|
13
|
+
connectedCallback() {
|
|
14
|
+
this.pageId = this.getAttribute("page-id")
|
|
15
|
+
this.folded = this.hasAttribute("folded")
|
|
16
|
+
|
|
17
|
+
this.folderButton?.addEventListener("click", this)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
disconnectedCallback() {
|
|
21
|
+
this.folderButton?.removeEventListener("click", this)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async handleEvent(event) {
|
|
25
|
+
if (event.type === "click") {
|
|
26
|
+
await this.handleFolderClick(event)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async handleFolderClick(event) {
|
|
31
|
+
event.preventDefault()
|
|
32
|
+
event.stopPropagation()
|
|
33
|
+
|
|
34
|
+
const folderButton = event.currentTarget
|
|
35
|
+
folderButton.innerHTML = ""
|
|
36
|
+
const spinner = new Spinner("small")
|
|
37
|
+
spinner.spin(folderButton)
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await patch(
|
|
41
|
+
Alchemy.routes.fold_admin_page_path(this.pageId),
|
|
42
|
+
null,
|
|
43
|
+
"text/vnd.turbo-stream.html"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
this.folded = !this.folded
|
|
47
|
+
this.toggleAttribute("folded", this.folded)
|
|
48
|
+
this.toggleChildren()
|
|
49
|
+
this.updateFolderIcon()
|
|
50
|
+
} catch (error) {
|
|
51
|
+
growl(error.message || error, "error")
|
|
52
|
+
this.updateFolderIcon()
|
|
53
|
+
} finally {
|
|
54
|
+
spinner.stop()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
toggleChildren() {
|
|
59
|
+
const childrenContainer = this.querySelector(
|
|
60
|
+
`#page_${this.pageId}_children`
|
|
61
|
+
)
|
|
62
|
+
if (childrenContainer) {
|
|
63
|
+
childrenContainer.classList.toggle("hidden", this.folded)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
updateFolderIcon() {
|
|
68
|
+
if (this.folderButton) {
|
|
69
|
+
const iconName = this.folded ? "arrow-right-s" : "arrow-down-s"
|
|
70
|
+
this.folderButton.innerHTML = `<alchemy-icon name="${iconName}"></alchemy-icon>`
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Updates the folder button state based on whether the node has children
|
|
76
|
+
* Converts between button and span as needed
|
|
77
|
+
*/
|
|
78
|
+
updateFolderButton() {
|
|
79
|
+
const folderElement = this.querySelector(".page_folder")
|
|
80
|
+
if (!folderElement) return
|
|
81
|
+
|
|
82
|
+
const shouldShowButton = this.hasChildren || this.folded
|
|
83
|
+
|
|
84
|
+
if (shouldShowButton && folderElement.tagName === SPAN) {
|
|
85
|
+
// Convert span to button with icon
|
|
86
|
+
const iconName = this.folded ? "arrow-right-s" : "arrow-down-s"
|
|
87
|
+
folderElement.outerHTML = `<button class="page_folder icon_button">
|
|
88
|
+
<alchemy-icon name="${iconName}"></alchemy-icon>
|
|
89
|
+
</button>`
|
|
90
|
+
|
|
91
|
+
// Re-attach event listener to the new button element
|
|
92
|
+
this.folderButton?.addEventListener("click", this)
|
|
93
|
+
} else if (!shouldShowButton && folderElement.tagName === BUTTON) {
|
|
94
|
+
// Convert button to empty span (no children and not folded)
|
|
95
|
+
folderElement.outerHTML = '<span class="page_folder"></span>'
|
|
96
|
+
} else if (shouldShowButton && folderElement.tagName === BUTTON) {
|
|
97
|
+
// Button exists, just update the icon direction
|
|
98
|
+
this.updateFolderIcon()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get hasChildren() {
|
|
103
|
+
const childrenContainer = this.querySelector(
|
|
104
|
+
`#page_${this.pageId}_children`
|
|
105
|
+
)
|
|
106
|
+
if (!childrenContainer) return false
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
childrenContainer.querySelectorAll(":scope > alchemy-page-node").length >
|
|
110
|
+
0
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get folderButton() {
|
|
115
|
+
return this.querySelector("button.page_folder")
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
customElements.define("alchemy-page-node", AlchemyPageNode)
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// Handles the page publication date fields
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const publication_date_fields = dialog.querySelector(
|
|
2
|
+
export class PagePublicationFields extends HTMLElement {
|
|
3
|
+
connectedCallback() {
|
|
4
|
+
const public_on_field = this.querySelector("#page_public_on")
|
|
5
|
+
const public_until_field = this.querySelector("#page_public_until")
|
|
6
|
+
const publication_date_fields = this.querySelector(
|
|
8
7
|
".page-publication-date-fields"
|
|
9
8
|
)
|
|
10
|
-
const public_field =
|
|
9
|
+
const public_field = this.querySelector("#page_public")
|
|
11
10
|
|
|
12
11
|
if (!public_field) return
|
|
13
12
|
|
|
@@ -24,5 +23,7 @@ export default function () {
|
|
|
24
23
|
}
|
|
25
24
|
public_until_field.value = ""
|
|
26
25
|
})
|
|
27
|
-
}
|
|
26
|
+
}
|
|
28
27
|
}
|
|
28
|
+
|
|
29
|
+
customElements.define("alchemy-page-publication-fields", PagePublicationFields)
|