alchemy_cms 7.4.6 → 8.0.0.a
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.
Potentially problematic release.
This version of alchemy_cms might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -0
- data/Gemfile +13 -6
- data/README.md +13 -5
- data/alchemy_cms.gemspec +14 -5
- 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.css +2 -2
- data/app/assets/builds/alchemy/custom-properties.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/ui/alchemy/content.min.css +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
- data/app/assets/config/alchemy_manifest.js +0 -2
- data/app/assets/images/alchemy/icons-sprite.svg +1 -0
- data/app/components/alchemy/admin/resource/applied_filter.rb +29 -0
- data/app/components/alchemy/admin/resource/checkbox_filter.rb +36 -0
- data/app/components/alchemy/admin/resource/datepicker_filter.rb +42 -0
- data/app/components/alchemy/admin/resource/select_filter.rb +43 -0
- data/app/components/alchemy/admin/toolbar_button.rb +5 -2
- data/app/components/alchemy/ingredients/number_view.rb +18 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +8 -15
- data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -6
- data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +15 -15
- data/app/controllers/alchemy/admin/pictures_controller.rb +9 -5
- data/app/controllers/alchemy/admin/resources_controller.rb +16 -106
- data/app/controllers/alchemy/attachments_controller.rb +43 -14
- data/app/controllers/alchemy/messages_controller.rb +1 -1
- data/app/controllers/alchemy/pages_controller.rb +7 -2
- data/app/controllers/concerns/alchemy/admin/resource_filter.rb +92 -0
- data/app/decorators/alchemy/element_editor.rb +5 -48
- data/app/decorators/alchemy/ingredient_editor.rb +3 -53
- data/app/helpers/alchemy/admin/base_helper.rb +14 -84
- data/app/helpers/alchemy/admin/elements_helper.rb +4 -4
- data/app/helpers/alchemy/admin/pages_helper.rb +1 -1
- data/app/helpers/alchemy/base_helper.rb +0 -30
- data/app/helpers/alchemy/elements_block_helper.rb +0 -14
- data/app/helpers/alchemy/pages_helper.rb +1 -1
- data/{lib → app/helpers}/alchemy/resources_helper.rb +5 -45
- data/app/javascript/alchemy_admin/components/action.js +2 -0
- data/app/javascript/alchemy_admin/components/alchemy_html_element.js +3 -3
- data/app/javascript/alchemy_admin/components/datepicker.js +10 -2
- data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +7 -7
- data/app/javascript/alchemy_admin/components/element_editor.js +1 -1
- data/app/javascript/alchemy_admin/components/index.js +1 -0
- data/app/javascript/alchemy_admin/components/remote_select.js +4 -1
- data/app/javascript/alchemy_admin/components/tags_autocomplete.js +5 -1
- data/app/javascript/alchemy_admin/components/tinymce.js +4 -2
- data/app/javascript/alchemy_admin/components/update_check.js +42 -0
- data/app/javascript/alchemy_admin/components/uploader/file_upload.js +15 -8
- data/app/javascript/alchemy_admin/components/uploader/progress.js +12 -6
- data/app/javascript/alchemy_admin/components/uploader.js +4 -2
- data/app/javascript/alchemy_admin/confirm_dialog.js +27 -57
- data/app/javascript/alchemy_admin/dirty.js +3 -2
- data/app/javascript/alchemy_admin/i18n.js +15 -16
- data/app/javascript/alchemy_admin/initializer.js +1 -49
- data/app/javascript/alchemy_admin/utils/ajax.js +51 -44
- data/app/javascript/alchemy_admin.js +3 -8
- data/app/models/alchemy/admin/filters/base.rb +38 -0
- data/app/models/alchemy/admin/filters/checkbox.rb +24 -0
- data/app/models/alchemy/admin/filters/datepicker.rb +53 -0
- data/app/models/alchemy/admin/filters/select.rb +70 -0
- data/app/models/alchemy/admin/resource_name.rb +27 -0
- data/app/models/alchemy/attachment.rb +51 -34
- data/app/models/alchemy/base_record.rb +2 -0
- data/app/models/alchemy/element/definitions.rb +1 -1
- data/app/models/alchemy/element/element_ingredients.rb +6 -6
- data/app/models/alchemy/element/presenters.rb +3 -12
- data/app/models/alchemy/element.rb +9 -27
- data/app/models/alchemy/element_definition.rb +160 -0
- data/app/models/alchemy/ingredient.rb +10 -43
- data/app/models/alchemy/ingredient_definition.rb +134 -0
- data/app/models/alchemy/ingredient_validator.rb +7 -3
- data/app/models/alchemy/ingredients/number.rb +19 -0
- data/app/models/alchemy/language.rb +0 -14
- data/app/models/alchemy/message.rb +3 -7
- data/app/models/alchemy/node.rb +1 -1
- data/app/models/alchemy/page/{page_layouts.rb → definitions.rb} +12 -19
- data/app/models/alchemy/page/fixed_attributes.rb +1 -1
- data/app/models/alchemy/page/page_elements.rb +13 -14
- data/app/models/alchemy/page/page_natures.rb +7 -7
- data/app/models/alchemy/page/page_scopes.rb +1 -1
- data/app/models/alchemy/page.rb +11 -33
- data/app/models/alchemy/page_definition.rb +115 -0
- data/app/models/alchemy/picture.rb +69 -79
- data/app/models/alchemy/picture_variant.rb +115 -5
- data/{lib → app/models}/alchemy/resource.rb +4 -18
- data/{lib → app/models}/alchemy/searchable_resource.rb +15 -0
- data/app/models/alchemy/site/layout.rb +5 -5
- data/app/models/alchemy/site.rb +0 -15
- data/app/models/alchemy/storage_adapter/active_storage/attachment_url.rb +41 -0
- data/app/models/alchemy/storage_adapter/active_storage/picture_url.rb +55 -0
- data/app/models/alchemy/storage_adapter/active_storage/preprocessor.rb +40 -0
- data/app/models/alchemy/storage_adapter/active_storage.rb +173 -0
- data/app/models/alchemy/{attachment/url.rb → storage_adapter/dragonfly/attachment_url.rb} +12 -12
- data/app/models/alchemy/{picture/url.rb → storage_adapter/dragonfly/picture_url.rb} +28 -12
- data/app/models/alchemy/{picture → storage_adapter/dragonfly}/preprocessor.rb +4 -4
- data/app/models/alchemy/storage_adapter/dragonfly.rb +183 -0
- data/app/models/alchemy/storage_adapter.rb +74 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +19 -6
- data/app/serializers/alchemy/element_serializer.rb +0 -1
- data/app/services/alchemy/dragonfly_to_image_processing.rb +100 -0
- data/app/stylesheets/alchemy/_defaults.scss +3 -0
- data/app/stylesheets/alchemy/_extends.scss +69 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/_mixins.scss +36 -49
- data/app/stylesheets/alchemy/_variables.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/archive.scss +20 -37
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/base.scss +16 -14
- data/app/stylesheets/alchemy/admin/buttons.scss +160 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/clipboard.scss +2 -2
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dashboard.scss +13 -16
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dialogs.scss +23 -16
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/elements.scss +150 -105
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/errors.scss +5 -5
- data/app/stylesheets/alchemy/admin/filters.scss +58 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flatpickr.scss +53 -60
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/form_fields.scss +21 -7
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/forms.scss +31 -19
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/frame.scss +20 -16
- data/app/stylesheets/alchemy/admin/hints.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/icons.scss +10 -1
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/image_library.scss +10 -8
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/images.scss +1 -1
- data/app/stylesheets/alchemy/admin/labels.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/lists.scss +3 -3
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/navigation.scss +61 -55
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/nodes.scss +21 -18
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/notices.scss +18 -18
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/page-select.scss +2 -2
- data/app/stylesheets/alchemy/admin/pagination.scss +144 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/preview_window.scss +8 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/print.scss +1 -1
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/resource_info.scss +8 -5
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/search.scss +9 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/selects.scss +49 -37
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/shoelace.scss +5 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/sitemap.scss +38 -33
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tables.scss +6 -4
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tags.scss +6 -4
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/toolbar.scss +12 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/typography.scss +2 -2
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/upload.scss +7 -5
- data/app/stylesheets/alchemy/admin.scss +44 -0
- data/app/stylesheets/alchemy/custom-properties.css +244 -0
- data/app/stylesheets/alchemy/welcome.scss +75 -0
- data/app/{assets/stylesheets → stylesheets}/tinymce/skins/content/alchemy/content.scss +8 -9
- data/app/stylesheets/tinymce/skins/ui/alchemy/content.scss +1 -0
- data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/skin.scss +133 -136
- data/app/views/alchemy/admin/attachments/_files_list.html.erb +2 -2
- data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
- data/app/views/alchemy/admin/{elements/_clipboard_button.html.erb → clipboard/_button.html.erb} +3 -5
- data/app/views/alchemy/admin/clipboard/_update_nested_element_button.turbo_stream.erb +11 -0
- data/app/views/alchemy/admin/clipboard/clear.turbo_stream.erb +4 -0
- data/app/views/alchemy/admin/clipboard/index.html.erb +15 -13
- data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +18 -0
- data/app/views/alchemy/admin/clipboard/remove.turbo_stream.erb +9 -0
- data/app/views/alchemy/admin/dashboard/info.html.erb +17 -31
- data/app/views/alchemy/admin/elements/_element.html.erb +4 -8
- data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_header.html.erb +1 -0
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +4 -6
- data/app/views/alchemy/admin/elements/create.turbo_stream.erb +2 -1
- data/app/views/alchemy/admin/elements/index.html.erb +2 -2
- data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +3 -16
- data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +0 -9
- data/app/views/alchemy/admin/languages/_form.html.erb +1 -1
- data/app/views/alchemy/admin/languages/_table.html.erb +1 -1
- data/app/views/alchemy/admin/languages/index.html.erb +5 -2
- data/app/views/alchemy/admin/layoutpages/index.html.erb +1 -12
- data/app/views/alchemy/admin/pages/_form.html.erb +2 -2
- data/app/views/alchemy/admin/pages/_page.html.erb +2 -3
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +1 -15
- data/app/views/alchemy/admin/pages/index.html.erb +1 -1
- data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +9 -12
- data/app/views/alchemy/admin/partials/_search_form.html.erb +4 -9
- data/app/views/alchemy/admin/pictures/_archive.html.erb +4 -7
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +2 -1
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/index.html.erb +2 -7
- data/app/views/alchemy/admin/resources/_applied_filters.html.erb +8 -0
- data/app/views/alchemy/admin/resources/_filter_bar.html.erb +11 -21
- data/app/views/alchemy/admin/resources/_pagination.html.erb +6 -0
- data/app/views/alchemy/admin/resources/_per_page_select.html.erb +4 -2
- data/app/views/alchemy/admin/resources/_resource_table.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_table_header.html.erb +1 -15
- data/app/views/alchemy/admin/sites/index.html.erb +5 -1
- data/app/views/alchemy/admin/styleguide/index.html.erb +8 -0
- data/app/views/alchemy/admin/tags/index.html.erb +1 -1
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +7 -7
- data/app/{javascript/alchemy_admin/locales/en.js → views/alchemy/admin/translations/_en.js} +5 -2
- data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
- data/app/views/alchemy/admin/uploader/_setup.html.erb +4 -4
- data/app/views/alchemy/base/redirect.js.erb +1 -1
- data/app/views/alchemy/ingredients/_number_editor.html.erb +24 -0
- data/app/views/alchemy/no_index.html.erb +31 -0
- data/app/views/alchemy/welcome.html.erb +12 -10
- data/app/views/kaminari/alchemy/_first_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_last_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_next_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_paginator.html.erb +18 -13
- data/app/views/kaminari/alchemy/_prev_page.html.erb +5 -3
- data/app/views/layouts/alchemy/admin.html.erb +5 -9
- data/bun.lockb +0 -0
- data/bundles/remixicon.mjs +153 -0
- data/config/alchemy/config.yml +3 -2
- data/config/initializers/dragonfly.rb +0 -1
- data/config/initializers/mime_types.rb +1 -0
- data/config/locales/alchemy.en.yml +32 -14
- data/config/routes.rb +0 -2
- data/eslint.config.js +2 -1
- data/lib/alchemy/admin/preview_url.rb +4 -5
- data/lib/alchemy/cache_digests/template_tracker.rb +6 -9
- data/lib/alchemy/config_missing.rb +14 -0
- data/lib/alchemy/configuration/base_option.rb +24 -0
- data/lib/alchemy/configuration/boolean_option.rb +16 -0
- data/lib/alchemy/configuration/class_option.rb +15 -0
- data/lib/alchemy/configuration/class_set_option.rb +46 -0
- data/lib/alchemy/configuration/integer_list_option.rb +13 -0
- data/lib/alchemy/configuration/integer_option.rb +12 -0
- data/lib/alchemy/configuration/list_option.rb +22 -0
- data/lib/alchemy/configuration/regexp_option.rb +11 -0
- data/lib/alchemy/configuration/string_list_option.rb +13 -0
- data/lib/alchemy/configuration/string_option.rb +11 -0
- data/lib/alchemy/configuration.rb +115 -0
- data/lib/alchemy/configuration_methods.rb +3 -1
- data/lib/alchemy/configurations/default_language.rb +12 -0
- data/lib/alchemy/configurations/default_site.rb +10 -0
- data/lib/alchemy/configurations/format_matchers.rb +11 -0
- data/lib/alchemy/configurations/mailer.rb +16 -0
- data/lib/alchemy/configurations/main.rb +216 -0
- data/lib/alchemy/configurations/preview.rb +32 -0
- data/lib/alchemy/configurations/sitemap.rb +10 -0
- data/lib/alchemy/configurations/uploader.rb +34 -0
- data/lib/alchemy/engine.rb +65 -17
- data/lib/alchemy/hints.rb +3 -7
- data/lib/alchemy/on_page_layout.rb +2 -2
- data/lib/alchemy/propshaft/tinymce_asset.rb +15 -0
- data/lib/alchemy/seeder.rb +2 -2
- data/lib/alchemy/tasks/usage.rb +4 -4
- data/lib/alchemy/test_support/config_stubbing.rb +1 -7
- data/lib/alchemy/test_support/factories/attachment_factory.rb +13 -2
- data/lib/alchemy/test_support/factories/language_factory.rb +1 -1
- data/lib/alchemy/test_support/factories/page_factory.rb +2 -3
- data/lib/alchemy/test_support/factories/picture_factory.rb +30 -2
- data/lib/alchemy/test_support/factories/site_factory.rb +2 -2
- data/lib/alchemy/test_support/having_crop_action_examples.rb +2 -2
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +80 -26
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +5 -5
- data/lib/alchemy/upgrader/.keep +0 -0
- data/lib/alchemy/upgrader/eight_zero.rb +14 -0
- data/lib/alchemy/upgrader.rb +33 -20
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy.rb +192 -170
- data/lib/alchemy_cms.rb +1 -7
- data/lib/generators/alchemy/ingredient/ingredient_generator.rb +0 -3
- data/lib/generators/alchemy/install/files/_article.html.erb +6 -4
- data/lib/generators/alchemy/install/files/alchemy.en.yml +22 -3
- data/lib/generators/alchemy/install/files/application.html.erb +5 -0
- data/lib/generators/alchemy/install/install_generator.rb +5 -14
- data/lib/generators/alchemy/install/templates/alchemy.rb.tt +196 -0
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +0 -1
- data/lib/generators/alchemy/install/templates/elements.yml.tt +3 -1
- data/lib/generators/alchemy/install/templates/menus.yml.tt +1 -1
- data/lib/generators/alchemy/install/templates/page_layouts.yml.tt +2 -2
- data/lib/generators/alchemy/page_layouts/page_layouts_generator.rb +2 -2
- data/lib/tasks/alchemy/assets.rake +14 -0
- data/lib/tasks/alchemy/upgrade.rake +12 -47
- data/vendor/javascript/tinymce.min.js +1 -1
- data/vitest.config.js +21 -0
- metadata +181 -180
- data/app/assets/builds/alchemy/admin/page-select.css.map +0 -1
- data/app/assets/builds/alchemy/admin/print.css.map +0 -1
- data/app/assets/builds/alchemy/admin.css.map +0 -1
- data/app/assets/builds/alchemy/custom-properties.css.map +0 -1
- data/app/assets/builds/alchemy/welcome.css.map +0 -1
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +0 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css.map +0 -1
- data/app/assets/javascripts/alchemy/admin.js +0 -10
- data/app/assets/stylesheets/alchemy/_defaults.scss +0 -3
- data/app/assets/stylesheets/alchemy/_deprecated_variables.scss +0 -45
- data/app/assets/stylesheets/alchemy/_deprecation.scss +0 -17
- data/app/assets/stylesheets/alchemy/_extends.scss +0 -62
- data/app/assets/stylesheets/alchemy/_variables.scss +0 -201
- data/app/assets/stylesheets/alchemy/admin/buttons.scss +0 -124
- data/app/assets/stylesheets/alchemy/admin/hints.scss +0 -5
- data/app/assets/stylesheets/alchemy/admin/labels.scss +0 -3
- data/app/assets/stylesheets/alchemy/admin/pagination.scss +0 -92
- data/app/assets/stylesheets/alchemy/admin.scss +0 -42
- data/app/assets/stylesheets/alchemy/custom-properties.css +0 -98
- data/app/assets/stylesheets/alchemy/welcome.scss +0 -57
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.css +0 -711
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.css +0 -705
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.css +0 -29
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.css +0 -677
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.min.css +0 -7
- data/app/controllers/alchemy/elements_controller.rb +0 -32
- data/app/models/alchemy/element/dom_id.rb +0 -31
- data/app/models/alchemy/picture/calculations.rb +0 -49
- data/app/models/alchemy/picture/transformations.rb +0 -115
- data/app/views/alchemy/admin/attachments/destroy.js.erb +0 -1
- data/app/views/alchemy/admin/clipboard/clear.js.erb +0 -3
- data/app/views/alchemy/admin/clipboard/insert.js.erb +0 -29
- data/app/views/alchemy/admin/clipboard/remove.js.erb +0 -10
- data/app/views/alchemy/admin/resources/_filter.html.erb +0 -12
- data/app/views/alchemy/admin/resources/_resource.html.erb +0 -34
- data/app/views/alchemy/admin/resources/_table.html.erb +0 -29
- data/app/views/alchemy/elements/show.html.erb +0 -1
- data/app/views/alchemy/elements/show.js.erb +0 -1
- data/app/views/alchemy/ingredients/_audio_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_boolean_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_datetime_view.html.erb +0 -3
- data/app/views/alchemy/ingredients/_file_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_headline_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_html_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_link_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_node_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_page_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_picture_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_richtext_view.html.erb +0 -3
- data/app/views/alchemy/ingredients/_select_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_text_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_video_view.html.erb +0 -3
- data/babel.config.js +0 -12
- data/config/initializers/assets.rb +0 -4
- data/lib/alchemy/config.rb +0 -114
- data/lib/alchemy/element_definition.rb +0 -73
- data/lib/alchemy/page_layout.rb +0 -73
- data/lib/alchemy/resource_filter.rb +0 -40
- data/lib/alchemy/upgrader/seven_point_four.rb +0 -26
- data/lib/alchemy/upgrader/seven_point_three.rb +0 -52
- data/lib/generators/alchemy/ingredient/templates/view.html.erb +0 -1
- data/lib/generators/alchemy/install/files/alchemy_admin.js +0 -1
- data/lib/generators/alchemy/install/files/all.js +0 -11
- data/lib/generators/alchemy/install/files/article.css +0 -25
- data/vendor/assets/images/remixicon.symbol.svg +0 -11
- /data/app/{assets/stylesheets → stylesheets}/alchemy/_fonts.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachment-select.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachments.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flash.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/list_filter.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/node-select.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/spinner.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/skintool.json +0 -0
- /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/fonts/tinymce-mobile.woff +0 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
import Spinner from "alchemy_admin/spinner"
|
2
|
+
|
3
|
+
class UpdateCheck extends HTMLElement {
|
4
|
+
async connectedCallback() {
|
5
|
+
const spinner = new Spinner("small")
|
6
|
+
spinner.spin(this)
|
7
|
+
|
8
|
+
try {
|
9
|
+
const response = await fetch(this.url)
|
10
|
+
const responseText = await response.text()
|
11
|
+
|
12
|
+
if (response.ok) {
|
13
|
+
this.showStatus(responseText)
|
14
|
+
} else {
|
15
|
+
this.showError(response)
|
16
|
+
}
|
17
|
+
} catch (error) {
|
18
|
+
this.showError(error)
|
19
|
+
} finally {
|
20
|
+
spinner.stop()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
get url() {
|
25
|
+
return this.getAttribute("url")
|
26
|
+
}
|
27
|
+
|
28
|
+
showStatus(responseText) {
|
29
|
+
if (responseText == "true") {
|
30
|
+
this.querySelector(".update_available").classList.remove("hidden")
|
31
|
+
} else {
|
32
|
+
this.querySelector(".up_to_date").classList.remove("hidden")
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
showError(error) {
|
37
|
+
this.querySelector(".error").classList.remove("hidden")
|
38
|
+
console.error("[alchemy] Error fetching update status", error)
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
customElements.define("alchemy-update-check", UpdateCheck)
|
@@ -4,21 +4,28 @@ import { translate } from "alchemy_admin/i18n"
|
|
4
4
|
import { growl } from "alchemy_admin/growler"
|
5
5
|
|
6
6
|
export class FileUpload extends AlchemyHTMLElement {
|
7
|
+
constructor() {
|
8
|
+
super()
|
9
|
+
|
10
|
+
this.file = null
|
11
|
+
this.request = null
|
12
|
+
|
13
|
+
this.progressEventLoaded = 0
|
14
|
+
this.progressEventTotal = 0
|
15
|
+
this.className = "in-progress"
|
16
|
+
this.valid = true
|
17
|
+
this.value = 0
|
18
|
+
}
|
19
|
+
|
7
20
|
/**
|
21
|
+
* Initialize the component with file and request
|
8
22
|
* @param {File} file
|
9
23
|
* @param {XMLHttpRequest} request
|
10
24
|
*/
|
11
|
-
|
12
|
-
super({})
|
13
|
-
|
25
|
+
initialize(file, request) {
|
14
26
|
this.file = file
|
15
27
|
this.request = request
|
16
|
-
|
17
|
-
this.progressEventLoaded = 0
|
18
28
|
this.progressEventTotal = file ? file.size : 0
|
19
|
-
this.className = "in-progress"
|
20
|
-
this.valid = true
|
21
|
-
this.value = 0
|
22
29
|
|
23
30
|
this._validateFile()
|
24
31
|
this._addRequestEventListener()
|
@@ -6,19 +6,25 @@ import { translate } from "alchemy_admin/i18n"
|
|
6
6
|
export class Progress extends AlchemyHTMLElement {
|
7
7
|
#visible = false
|
8
8
|
|
9
|
-
|
10
|
-
* @param {FileUpload[]} fileUploads
|
11
|
-
*/
|
12
|
-
constructor(fileUploads = []) {
|
9
|
+
constructor() {
|
13
10
|
super()
|
14
11
|
this.buttonLabel = translate("Cancel all uploads")
|
15
|
-
this.fileUploads =
|
16
|
-
this.fileCount =
|
12
|
+
this.fileUploads = []
|
13
|
+
this.fileCount = 0
|
17
14
|
this.className = "in-progress"
|
18
15
|
this.visible = true
|
19
16
|
this.handleFileChange = () => this._updateView()
|
20
17
|
}
|
21
18
|
|
19
|
+
/**
|
20
|
+
* Initialize the component with file uploads
|
21
|
+
* @param {FileUpload[]} fileUploads
|
22
|
+
*/
|
23
|
+
initialize(fileUploads = []) {
|
24
|
+
this.fileUploads = fileUploads
|
25
|
+
this.fileCount = fileUploads.length
|
26
|
+
}
|
27
|
+
|
22
28
|
/**
|
23
29
|
* append file progress - components for each file
|
24
30
|
*/
|
@@ -68,7 +68,8 @@ export class Uploader extends AlchemyHTMLElement {
|
|
68
68
|
|
69
69
|
const fileUploads = files.map((file) => {
|
70
70
|
const request = new XMLHttpRequest()
|
71
|
-
const fileUpload = new FileUpload(
|
71
|
+
const fileUpload = new FileUpload()
|
72
|
+
fileUpload.initialize(file, request)
|
72
73
|
|
73
74
|
if (Alchemy.uploader_defaults.upload_limit - 1 < fileUploadCount) {
|
74
75
|
fileUpload.valid = false
|
@@ -110,7 +111,8 @@ export class Uploader extends AlchemyHTMLElement {
|
|
110
111
|
this.uploadProgress.cancel()
|
111
112
|
document.body.removeChild(this.uploadProgress)
|
112
113
|
}
|
113
|
-
this.uploadProgress = new Progress(
|
114
|
+
this.uploadProgress = new Progress()
|
115
|
+
this.uploadProgress.initialize(fileUploads)
|
114
116
|
this.uploadProgress.onComplete = (status) => {
|
115
117
|
this.dispatchCustomEvent(`upload.${status}`)
|
116
118
|
}
|
@@ -1,20 +1,19 @@
|
|
1
|
-
import { growl } from "alchemy_admin/growler"
|
2
|
-
import pleaseWaitOverlay from "alchemy_admin/please_wait_overlay"
|
3
1
|
import { createHtmlElement } from "alchemy_admin/utils/dom_helpers"
|
4
2
|
import { translate } from "alchemy_admin/i18n"
|
5
3
|
|
6
|
-
const
|
4
|
+
const getDefaults = () => ({
|
5
|
+
// The default size of the dialog
|
7
6
|
size: "300x100",
|
8
7
|
title: translate("Please confirm"),
|
9
8
|
ok_label: translate("Yes"),
|
10
9
|
cancel_label: translate("No"),
|
11
10
|
on_ok() {}
|
12
|
-
}
|
11
|
+
})
|
13
12
|
|
14
13
|
class ConfirmDialog {
|
15
14
|
constructor(message, options = {}) {
|
16
15
|
this.message = message
|
17
|
-
this.options = { ...
|
16
|
+
this.options = { ...getDefaults(), ...options }
|
18
17
|
this.#build()
|
19
18
|
this.#bindEvents()
|
20
19
|
}
|
@@ -44,6 +43,7 @@ class ConfirmDialog {
|
|
44
43
|
#bindEvents() {
|
45
44
|
this.cancelButton.addEventListener("click", (evt) => {
|
46
45
|
evt.preventDefault()
|
46
|
+
this.options.on_cancel()
|
47
47
|
this.dialog.hide()
|
48
48
|
})
|
49
49
|
this.okButton.addEventListener("click", (evt) => {
|
@@ -54,6 +54,7 @@ class ConfirmDialog {
|
|
54
54
|
// Prevent the dialog from closing when the user clicks on the overlay
|
55
55
|
this.dialog.addEventListener("sl-request-close", (event) => {
|
56
56
|
if (event.detail.source === "overlay") {
|
57
|
+
this.options.on_cancel()
|
57
58
|
event.preventDefault()
|
58
59
|
}
|
59
60
|
})
|
@@ -72,60 +73,29 @@ class ConfirmDialog {
|
|
72
73
|
}
|
73
74
|
}
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
//
|
76
|
+
/* Opens a confirm dialog
|
77
|
+
*
|
78
|
+
* @param {string} message - The message that will be displayed to the user
|
79
|
+
* @param {Object} [options={}] - Configuration options for the dialog
|
80
|
+
* @param {string} [options.title="Please confirm"] - The title of the overlay window
|
81
|
+
* @param {string} [options.cancel_label="No"] - The label of the cancel button
|
82
|
+
* @param {string} [options.ok_label="Yes"] - The label of the ok button
|
83
|
+
*
|
84
|
+
* @returns {Promise<void>} A promise that resolves to true when the OK button is clicked and
|
85
|
+
* resolves to false when the cancel button is clicked. Works as confirm dialog replacement
|
86
|
+
* for Turbo.confirm.
|
87
|
+
*/
|
88
88
|
export function openConfirmDialog(message, options = {}) {
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
}
|
93
|
-
|
94
|
-
// Opens a confirm to delete dialog
|
95
|
-
//
|
96
|
-
// Arguments:
|
97
|
-
//
|
98
|
-
// url - The url to the server delete action. Uses DELETE as HTTP method. (String)
|
99
|
-
// opts - An options object (Object)
|
100
|
-
//
|
101
|
-
// Options:
|
102
|
-
//
|
103
|
-
// title: '' - The title of the confirmation window (String)
|
104
|
-
// message: '' - The message that will be displayed to the user (String)
|
105
|
-
// ok_label: '' - The label for the ok button (String)
|
106
|
-
// cancel_label: '' - The label for the cancel button (String)
|
107
|
-
//
|
108
|
-
export function confirmToDeleteDialog(url, opts = {}) {
|
109
|
-
return new Promise((resolve, reject) => {
|
110
|
-
const options = {
|
89
|
+
return new Promise((resolve) => {
|
90
|
+
const dialog = new ConfirmDialog(message, {
|
91
|
+
...options,
|
111
92
|
on_ok() {
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
error(xhr, _status, error) {
|
117
|
-
const type = xhr.status === 403 ? "warning" : "error"
|
118
|
-
growl(xhr.responseText || error, type)
|
119
|
-
reject(error)
|
120
|
-
},
|
121
|
-
complete(response) {
|
122
|
-
pleaseWaitOverlay(false)
|
123
|
-
resolve(response)
|
124
|
-
}
|
125
|
-
})
|
93
|
+
resolve(true)
|
94
|
+
},
|
95
|
+
on_cancel() {
|
96
|
+
resolve(false)
|
126
97
|
}
|
127
|
-
}
|
128
|
-
|
129
|
-
openConfirmDialog(opts.message, { ...options, ...opts })
|
98
|
+
})
|
99
|
+
dialog.open()
|
130
100
|
})
|
131
101
|
}
|
@@ -27,8 +27,9 @@ function checkPageDirtyness(element) {
|
|
27
27
|
openConfirmDialog(translate("page_dirty_notice"), {
|
28
28
|
title: translate("warning"),
|
29
29
|
ok_label: translate("ok"),
|
30
|
-
cancel_label: translate("cancel")
|
31
|
-
|
30
|
+
cancel_label: translate("cancel")
|
31
|
+
}).then((proceed) => {
|
32
|
+
if (proceed) {
|
32
33
|
window.onbeforeunload = void 0
|
33
34
|
callback()
|
34
35
|
}
|
@@ -1,20 +1,5 @@
|
|
1
|
-
import { en } from "alchemy_admin/locales/en"
|
2
|
-
|
3
|
-
Alchemy.translations = Object.assign(Alchemy.translations || {}, { en })
|
4
|
-
|
5
1
|
const KEY_SEPARATOR = /\./
|
6
2
|
|
7
|
-
function getTranslations() {
|
8
|
-
const locale = currentLocale()
|
9
|
-
const translations = Alchemy.translations && Alchemy.translations[locale]
|
10
|
-
|
11
|
-
if (translations) {
|
12
|
-
return translations
|
13
|
-
}
|
14
|
-
console.warn(`Translations for locale ${locale} not found!`)
|
15
|
-
return {}
|
16
|
-
}
|
17
|
-
|
18
3
|
function nestedTranslation(translations, key) {
|
19
4
|
const keys = key.split(KEY_SEPARATOR)
|
20
5
|
const group = translations[keys[0]]
|
@@ -25,7 +10,13 @@ function nestedTranslation(translations, key) {
|
|
25
10
|
}
|
26
11
|
|
27
12
|
function getTranslation(key) {
|
28
|
-
const
|
13
|
+
const locale = currentLocale()
|
14
|
+
const translations = Alchemy.translations
|
15
|
+
|
16
|
+
if (!translations) {
|
17
|
+
console.warn(`Translations for locale ${locale} not found!`)
|
18
|
+
return key
|
19
|
+
}
|
29
20
|
|
30
21
|
if (KEY_SEPARATOR.test(key)) {
|
31
22
|
return nestedTranslation(translations, key)
|
@@ -48,3 +39,11 @@ export function translate(key, replacement = undefined) {
|
|
48
39
|
}
|
49
40
|
return translation
|
50
41
|
}
|
42
|
+
|
43
|
+
export async function setupSelectLocale() {
|
44
|
+
const locale = currentLocale()
|
45
|
+
if (locale === "en") return
|
46
|
+
|
47
|
+
await import(`select2/${locale}.js`)
|
48
|
+
$.extend($.fn.select2.defaults, $.fn.select2.locales[locale])
|
49
|
+
}
|
@@ -1,8 +1,3 @@
|
|
1
|
-
import {
|
2
|
-
confirmToDeleteDialog,
|
3
|
-
openConfirmDialog
|
4
|
-
} from "alchemy_admin/confirm_dialog"
|
5
|
-
|
6
1
|
import Hotkeys from "alchemy_admin/hotkeys"
|
7
2
|
import pleaseWaitOverlay from "alchemy_admin/please_wait_overlay"
|
8
3
|
|
@@ -26,44 +21,6 @@ function selectHandler(selectId, parameterName, forcedReload = false) {
|
|
26
21
|
})
|
27
22
|
}
|
28
23
|
|
29
|
-
// Watches elements for Alchemy Dialogs
|
30
|
-
//
|
31
|
-
// Links having a data-alchemy-confirm-delete
|
32
|
-
// and input/buttons having a data-alchemy-confirm attribute get watched.
|
33
|
-
//
|
34
|
-
// You can pass a scope so that only elements inside this scope are queried.
|
35
|
-
//
|
36
|
-
// The href attribute of the link is the url for the overlay window.
|
37
|
-
//
|
38
|
-
// See Dialog for further options you can add to the data attribute.
|
39
|
-
//
|
40
|
-
function watchForConfirmDialogs(scope) {
|
41
|
-
if (scope == null) {
|
42
|
-
scope = "#alchemy"
|
43
|
-
}
|
44
|
-
$(scope).on("click", "[data-alchemy-confirm-delete]", function (event) {
|
45
|
-
const $this = $(this)
|
46
|
-
const options = $this.data("alchemy-confirm-delete")
|
47
|
-
confirmToDeleteDialog($this.attr("href"), options)
|
48
|
-
event.preventDefault()
|
49
|
-
})
|
50
|
-
$(scope).on("click", "[data-alchemy-confirm]", function (event) {
|
51
|
-
const options = $(this).data("alchemy-confirm")
|
52
|
-
openConfirmDialog(
|
53
|
-
options.message,
|
54
|
-
$.extend(options, {
|
55
|
-
ok_label: options.ok_label,
|
56
|
-
cancel_label: options.cancel_label,
|
57
|
-
on_ok: () => {
|
58
|
-
pleaseWaitOverlay()
|
59
|
-
this.form.submit()
|
60
|
-
}
|
61
|
-
})
|
62
|
-
)
|
63
|
-
event.preventDefault()
|
64
|
-
})
|
65
|
-
}
|
66
|
-
|
67
24
|
export default function Initializer() {
|
68
25
|
// We obviously have javascript enabled.
|
69
26
|
$("html").removeClass("no-js")
|
@@ -71,13 +28,8 @@ export default function Initializer() {
|
|
71
28
|
// Initialize hotkeys.
|
72
29
|
Hotkeys()
|
73
30
|
|
74
|
-
// Watch for click on confirm dialog links.
|
75
|
-
watchForConfirmDialogs()
|
76
|
-
|
77
31
|
// Add observer for please wait overlay.
|
78
|
-
$(".please_wait")
|
79
|
-
.not("*[data-alchemy-confirm]")
|
80
|
-
.on("click", Alchemy.pleaseWaitOverlay)
|
32
|
+
$(".please_wait").on("click", pleaseWaitOverlay)
|
81
33
|
|
82
34
|
// Hack for enabling tab focus for <a>'s styled as button.
|
83
35
|
$("a.button").attr({ tabindex: 0 })
|
@@ -1,27 +1,37 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
1
|
+
const JSON_CONTENT_TYPE = "application/json"
|
2
|
+
|
3
|
+
function isGetRequest(method) {
|
4
|
+
return method.toLowerCase() === "get"
|
5
|
+
}
|
6
|
+
|
7
|
+
function prepareURL(path, data, method) {
|
8
|
+
const url = new URL(window.location.origin + path)
|
9
|
+
|
10
|
+
if (data && isGetRequest(method)) {
|
11
|
+
url.search = new URLSearchParams(data).toString()
|
12
|
+
}
|
13
|
+
|
14
|
+
return url.toString()
|
15
|
+
}
|
16
|
+
|
17
|
+
function prepareHeaders(accept) {
|
18
|
+
return {
|
19
|
+
"Content-Type": "application/json; charset=utf-8",
|
20
|
+
Accept: accept,
|
21
|
+
"X-Requested-With": "XMLHttpRequest",
|
22
|
+
"X-CSRF-Token": getToken()
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
function prepareOptions(method, data, accept) {
|
27
|
+
const headers = prepareHeaders(accept)
|
28
|
+
const options = { method, headers }
|
29
|
+
|
30
|
+
if (data && !isGetRequest(method)) {
|
31
|
+
options.body = JSON.stringify(data)
|
32
|
+
}
|
33
|
+
|
34
|
+
return options
|
25
35
|
}
|
26
36
|
|
27
37
|
export function getToken() {
|
@@ -37,30 +47,27 @@ export function patch(url, data) {
|
|
37
47
|
return ajax("PATCH", url, data)
|
38
48
|
}
|
39
49
|
|
40
|
-
export function post(url, data, accept =
|
50
|
+
export function post(url, data, accept = JSON_CONTENT_TYPE) {
|
41
51
|
return ajax("POST", url, data, accept)
|
42
52
|
}
|
43
53
|
|
44
|
-
export default function ajax(
|
45
|
-
|
46
|
-
|
47
|
-
|
54
|
+
export default async function ajax(
|
55
|
+
method,
|
56
|
+
path,
|
57
|
+
data,
|
58
|
+
accept = JSON_CONTENT_TYPE
|
59
|
+
) {
|
60
|
+
const response = await fetch(
|
61
|
+
prepareURL(path, data, method),
|
62
|
+
prepareOptions(method, data, accept)
|
63
|
+
)
|
64
|
+
const contentType = response.headers.get("content-type")
|
65
|
+
const isJson = contentType?.includes(JSON_CONTENT_TYPE)
|
66
|
+
const responseData = isJson ? await response.json() : null
|
48
67
|
|
49
|
-
if (
|
50
|
-
|
51
|
-
}
|
52
|
-
|
53
|
-
xhr.open(method, url.toString())
|
54
|
-
xhr.setRequestHeader("Content-type", "application/json; charset=utf-8")
|
55
|
-
xhr.setRequestHeader("Accept", accept)
|
56
|
-
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
57
|
-
xhr.setRequestHeader("X-CSRF-Token", getToken())
|
58
|
-
|
59
|
-
if (data && method.toLowerCase() !== "get") {
|
60
|
-
xhr.send(JSON.stringify(data))
|
68
|
+
if (response.ok) {
|
69
|
+
return { data: responseData, status: response.status }
|
61
70
|
} else {
|
62
|
-
|
71
|
+
throw responseData || new Error("An error occurred during the transaction")
|
63
72
|
}
|
64
|
-
|
65
|
-
return promise
|
66
73
|
}
|
@@ -20,10 +20,7 @@ import Sitemap from "alchemy_admin/sitemap"
|
|
20
20
|
import Spinner from "alchemy_admin/spinner"
|
21
21
|
import PagePublicationFields from "alchemy_admin/page_publication_fields"
|
22
22
|
import { reloadPreview } from "alchemy_admin/components/preview_window"
|
23
|
-
import {
|
24
|
-
openConfirmDialog,
|
25
|
-
confirmToDeleteDialog
|
26
|
-
} from "alchemy_admin/confirm_dialog"
|
23
|
+
import { openConfirmDialog } from "alchemy_admin/confirm_dialog"
|
27
24
|
|
28
25
|
// Web Components
|
29
26
|
import "alchemy_admin/components"
|
@@ -53,11 +50,9 @@ Object.assign(Alchemy, {
|
|
53
50
|
Sitemap,
|
54
51
|
Spinner,
|
55
52
|
PagePublicationFields,
|
56
|
-
reloadPreview
|
57
|
-
openConfirmDialog,
|
58
|
-
confirmToDeleteDialog
|
53
|
+
reloadPreview
|
59
54
|
})
|
60
55
|
|
61
56
|
Rails.start()
|
62
|
-
|
57
|
+
Turbo.config.forms.confirm = openConfirmDialog
|
63
58
|
$(document).on("turbo:load", Initializer)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alchemy
|
4
|
+
module Admin
|
5
|
+
module Filters
|
6
|
+
class Base
|
7
|
+
attr_reader :name, :resource_name
|
8
|
+
|
9
|
+
def initialize(name:, resource_name:)
|
10
|
+
@name = name
|
11
|
+
@resource_name = resource_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def applied_filter_component(search_filter_params:, resource_url_proxy:, query:)
|
15
|
+
Alchemy::Admin::Resource::AppliedFilter.new(
|
16
|
+
link: dismiss_filter_url(search_filter_params, resource_url_proxy),
|
17
|
+
applied_filter_label: translated_name,
|
18
|
+
applied_filter_value: translated_value(search_filter_params[:q][name], query)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def translated_name
|
25
|
+
Alchemy.t(:name, scope: [:filters, resource_name, name], default: name.to_s.humanize)
|
26
|
+
end
|
27
|
+
|
28
|
+
def dismiss_filter_url(search_filter_params, resource_url_proxy)
|
29
|
+
tmp_params = search_filter_params.dup
|
30
|
+
tmp_params[:q] = tmp_params[:q].except(name)
|
31
|
+
resource_url_proxy.url_for(
|
32
|
+
{action: "index"}.merge(tmp_params.except(:page))
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alchemy
|
4
|
+
module Admin
|
5
|
+
module Filters
|
6
|
+
class Checkbox < Base
|
7
|
+
# Renders a checkbox filter input.
|
8
|
+
# @param [Hash] params The controller params.
|
9
|
+
# @param [Ransack::Search] _query The current search query.
|
10
|
+
# @return [Alchemy::Admin::FilterInputs::Checkbox] The checkbox filter input component.
|
11
|
+
def input_component(params, _query)
|
12
|
+
Alchemy::Admin::Resource::CheckboxFilter.new(name:, label: translated_name, params:)
|
13
|
+
end
|
14
|
+
|
15
|
+
def applied_filter_component(search_filter_params:, resource_url_proxy:, query:)
|
16
|
+
Alchemy::Admin::Resource::AppliedFilter.new(
|
17
|
+
link: dismiss_filter_url(search_filter_params, resource_url_proxy),
|
18
|
+
applied_filter_label: translated_name
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alchemy
|
4
|
+
module Admin
|
5
|
+
module Filters
|
6
|
+
class Datepicker < Base
|
7
|
+
attr_reader :input_type
|
8
|
+
# Creates a resource filter that displays as a datepicker.
|
9
|
+
# @param name [String] The name of the filter.
|
10
|
+
# @param resource_name [String] The name of the resource.
|
11
|
+
# @param input_type [Symbol] The input type of the datepicker. Can be :date, :datetime, or :time.
|
12
|
+
# @example
|
13
|
+
# Alchemy::Admin::Filters::Datepicker.new(
|
14
|
+
# name: :created_at_lt,
|
15
|
+
# resource_name: :events,
|
16
|
+
# mode: :single,
|
17
|
+
# default: "2023-01-01"
|
18
|
+
def initialize(name:, resource_name:, input_type: :datetime)
|
19
|
+
super(name:, resource_name:)
|
20
|
+
@input_type = input_type
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns a datepicker filter component.
|
24
|
+
# @param params [Hash] The search filter params.
|
25
|
+
# @param query [Ransack::Search] The current search query.
|
26
|
+
# @return [Alchemy::Admin::Resource::DatepickerFilter] The datepicker filter component.
|
27
|
+
def input_component(params, _query)
|
28
|
+
Alchemy::Admin::Resource::DatepickerFilter.new(
|
29
|
+
name:,
|
30
|
+
label: translated_name,
|
31
|
+
input_type: @input_type,
|
32
|
+
params:
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def translated_value(value, query)
|
39
|
+
date = Time.zone.parse(value) if value.is_a?(String)
|
40
|
+
format = case input_type
|
41
|
+
when :date
|
42
|
+
::I18n.t(:default, scope: [:date, :formats, :alchemy])
|
43
|
+
when :datetime
|
44
|
+
::I18n.t(:default, scope: [:time, :formats, :alchemy])
|
45
|
+
when :time
|
46
|
+
::I18n.t(:time, scope: [:time, :formats, :alchemy])
|
47
|
+
end
|
48
|
+
::I18n.l(date, format: format)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|