alchemy_cms 7.2.0.b → 7.2.0.rc1

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.

Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/app/assets/config/alchemy_manifest.js +0 -1
  4. data/app/assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js +1 -1
  5. data/app/assets/stylesheets/alchemy/elements.scss +1 -5
  6. data/app/assets/stylesheets/alchemy/node-select.scss +9 -1
  7. data/app/assets/stylesheets/alchemy/preview_window.scss +3 -3
  8. data/app/components/alchemy/admin/link_dialog/file_tab.rb +1 -1
  9. data/app/components/alchemy/admin/link_dialog/internal_tab.rb +21 -3
  10. data/app/controllers/alchemy/api/nodes_controller.rb +1 -1
  11. data/app/javascript/alchemy_admin/components/action.js +41 -0
  12. data/app/javascript/alchemy_admin/components/index.js +1 -0
  13. data/app/javascript/alchemy_admin/components/node_select.js +3 -1
  14. data/app/javascript/alchemy_admin.js +0 -2
  15. data/app/views/alchemy/_menubar.html.erb +126 -13
  16. data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +1 -0
  17. data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -0
  18. data/app/views/alchemy/admin/ingredients/update.turbo_stream.erb +5 -0
  19. data/app/views/alchemy/admin/nodes/_page_nodes.html.erb +2 -1
  20. data/app/views/layouts/alchemy/admin.html.erb +2 -4
  21. data/lib/alchemy/engine.rb +10 -5
  22. data/lib/alchemy/test_support/shared_link_tab_examples.rb +57 -0
  23. data/lib/alchemy/version.rb +1 -1
  24. data/lib/alchemy.rb +36 -0
  25. metadata +5 -5
  26. data/app/assets/stylesheets/alchemy/menubar.scss +0 -81
  27. data/app/javascript/menubar.js +0 -10
  28. data/app/views/alchemy/admin/ingredients/update.js.erb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ad1126eb844c56fb3b6259da82715a8c414a8d52c6864ee37bedc6e4ee41823
4
- data.tar.gz: 4c06518d41d2b6b55b75b6a39df2754d9b32d05ed781d68eccfe8685d1bd7a03
3
+ metadata.gz: f25f82412085150403ec2bc51c9c5ad2ec0f81f82a05b44789f496d9be86b52c
4
+ data.tar.gz: 748944b05a82eb3166a2320e00107262c59dff8c796c995a2875c4d45507a876
5
5
  SHA512:
6
- metadata.gz: b83b7b9326660aaea585e0f7edfb1b7c627e519e8dc2d586c2c469afa728bbc1a1adf0e3437f82a0b02833080136c4388cbe02c01c0d79daa74b63deb26937f5
7
- data.tar.gz: 5cabdfca80aa27c1722f6291546fb25cd2253f12d3d3ac337b27edac74e3a44a454d30a09f6caf8cb6a76194df421499ab5835cdb31a007440aac2c398700f7b
6
+ metadata.gz: 46a28dc0a6d45859c6d58ee85b8ddedf667320618ce30a5c76db2e66b316031cec19db2b9c82c137cdf779f7f62ab29e489f1a94e89f0677f122620d3ac718e8
7
+ data.tar.gz: 81bb5104dc546f1c261f1d75eb2945109a6e9fd098f521748c22f2e0829e66669cf4854e329b8a1e2d3a025fd7c32a67f62bda7ab04f9a0a9383d1b24bf0f752
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 7.2.0.rc1 (2024-05-24)
4
+
5
+ - Deprecate float positioning in File and Picture ingredients [#2894](https://github.com/AlchemyCMS/alchemy_cms/pull/2894) ([tvdeyen](https://github.com/tvdeyen))
6
+ - Use inline styles for menubar component [#2893](https://github.com/AlchemyCMS/alchemy_cms/pull/2893) ([tvdeyen](https://github.com/tvdeyen))
7
+ - Fix link selection of internal root or locale urls [#2892](https://github.com/AlchemyCMS/alchemy_cms/pull/2892) ([tvdeyen](https://github.com/tvdeyen))
8
+ - Switch ingredient update response from Javascript to Turob Stream [#2891](https://github.com/AlchemyCMS/alchemy_cms/pull/2891) ([sascha-karnatz](https://github.com/sascha-karnatz))
9
+ - Prevent jumping of toggle button in element window [#2888](https://github.com/AlchemyCMS/alchemy_cms/pull/2888) ([sascha-karnatz](https://github.com/sascha-karnatz))
10
+ - Fix preview window width for smaller viewports [#2887](https://github.com/AlchemyCMS/alchemy_cms/pull/2887) ([tvdeyen](https://github.com/tvdeyen))
11
+ - fix(alchemy_link.plugin): Avoid getting an absolute URL [#2885](https://github.com/AlchemyCMS/alchemy_cms/pull/2885) ([tvdeyen](https://github.com/tvdeyen))
12
+ - Allow to add to Alchemy's importmap from Rails application [#2884](https://github.com/AlchemyCMS/alchemy_cms/pull/2884) ([tvdeyen](https://github.com/tvdeyen))
13
+ - Only set url on attachment select if attachment found [#2882](https://github.com/AlchemyCMS/alchemy_cms/pull/2882) ([tvdeyen](https://github.com/tvdeyen))
14
+ - Fix Preview Window width [#2879](https://github.com/AlchemyCMS/alchemy_cms/pull/2879) ([tvdeyen](https://github.com/tvdeyen))
15
+ - Allow engine importmaps [#2878](https://github.com/AlchemyCMS/alchemy_cms/pull/2878) ([tvdeyen](https://github.com/tvdeyen))
16
+ - fix(node-select): Display order and seperator style [#2877](https://github.com/AlchemyCMS/alchemy_cms/pull/2877) ([tvdeyen](https://github.com/tvdeyen))
17
+
3
18
  ## 7.2.0.b (2024-05-16)
4
19
 
5
20
  - fix language scope in picture description field [#2876](https://github.com/AlchemyCMS/alchemy_cms/pull/2876) ([tvdeyen](https://github.com/tvdeyen))
@@ -1,7 +1,6 @@
1
1
  //= link alchemy/admin/all.css
2
2
  //= link alchemy/admin/all.js
3
3
  //= link alchemy/preview.js
4
- //= link alchemy/menubar.css
5
4
  //= link alchemy/print.css
6
5
  //= link alchemy/welcome.css
7
6
  //= link tinymce/plugins/alchemy_link/plugin.min.js
@@ -10,7 +10,7 @@ tinymce.PluginManager.add("alchemy_link", function (editor) {
10
10
  const anchor = getAnchor(editor.selection.getNode())
11
11
  if (anchor) {
12
12
  link = {
13
- url: anchor.href,
13
+ url: anchor.getAttribute("href"), // avoid getting an absolute URL
14
14
  title: anchor.title,
15
15
  target: anchor.target,
16
16
  type: anchor.className
@@ -78,7 +78,7 @@ alchemy-tinymce {
78
78
  line-height: 1;
79
79
  max-width: 85%;
80
80
 
81
- .element-hidden & {
81
+ .element-hidden > & {
82
82
  max-width: 65%;
83
83
  }
84
84
 
@@ -114,10 +114,6 @@ alchemy-tinymce {
114
114
  padding: 0;
115
115
  margin: 0 0 0 auto;
116
116
 
117
- .element-hidden & {
118
- margin-left: unset;
119
- }
120
-
121
117
  &:hover {
122
118
  &:not(:focus):not(:active) {
123
119
  background-color: $default-border-color;
@@ -10,15 +10,23 @@
10
10
  align-items: center;
11
11
  height: 21px;
12
12
 
13
- .icon {
13
+ > alchemy-icon {
14
14
  margin: 0 8px 0 4px;
15
+ }
15
16
 
17
+ .icon {
16
18
  .select2-highlighted & {
17
19
  fill: $white;
18
20
  }
19
21
  }
20
22
  }
21
23
 
24
+ .node-select--node-display_name,
25
+ .node-select--node-ancestors {
26
+ display: inline-flex;
27
+ align-items: center;
28
+ }
29
+
22
30
  .node-select--node-name {
23
31
  font-weight: bold;
24
32
  }
@@ -16,12 +16,12 @@
16
16
 
17
17
  .collapsed-menu.elements-window-visible & {
18
18
  width: calc(
19
- 100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-width}
19
+ 100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-min-width}
20
20
  );
21
21
 
22
- @media screen and (min-width: $large-screen-break-point) {
22
+ @media screen and (min-width: 1777px) {
23
23
  width: calc(
24
- 100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-min-width}
24
+ 100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-width}
25
25
  );
26
26
  }
27
27
  }
@@ -35,7 +35,7 @@ module Alchemy
35
35
 
36
36
  def attachment_select
37
37
  label = label_tag("file_link", Alchemy.t(:file), class: "control-label")
38
- input = text_field_tag("file_link", url, id: "file_link")
38
+ input = text_field_tag("file_link", attachment && url, id: "file_link")
39
39
  select = render Alchemy::Admin::AttachmentSelect.new(attachment).with_content(input)
40
40
  content_tag("div", label + select, class: "input select")
41
41
  end
@@ -4,6 +4,8 @@ module Alchemy
4
4
  module Admin
5
5
  module LinkDialog
6
6
  class InternalTab < BaseTab
7
+ PAGE_URL_PATTERN = /\/(?<locale>[a-z]{2})?(?<slash>\/)?(?<urlname>.*)/
8
+
7
9
  def title
8
10
  Alchemy.t("link_overlay_tab_label.internal")
9
11
  end
@@ -39,12 +41,28 @@ module Alchemy
39
41
  end
40
42
 
41
43
  def page
42
- @_page ||= uri ? Alchemy::Page.find_by(urlname: uri.path[1..]) : nil
44
+ @_page ||= if uri&.path == "/"
45
+ Alchemy::Current.site.default_language.root_page
46
+ elsif uri
47
+ Alchemy::Page.find_by(page_attributes)
48
+ end
49
+ end
50
+
51
+ def page_attributes
52
+ locale, _slash, urlname = uri.path.match(PAGE_URL_PATTERN)&.captures
53
+
54
+ if locale && urlname.present?
55
+ {language_code: locale, urlname: urlname}
56
+ elsif locale
57
+ {language_code: locale, language_root: true}
58
+ else
59
+ {urlname: urlname}
60
+ end
43
61
  end
44
62
 
45
63
  def page_select
46
64
  label = label_tag("internal_link", Alchemy.t(:page), class: "control-label")
47
- input = text_field_tag("internal_link", is_selected? ? uri : "", id: "internal_link")
65
+ input = text_field_tag("internal_link", page && uri, id: "internal_link")
48
66
  page_select = render Alchemy::Admin::PageSelect.new(page, allow_clear: true).with_content(input)
49
67
  content_tag("div", label + page_select, class: "input select")
50
68
  end
@@ -53,7 +71,7 @@ module Alchemy
53
71
  fragment = "##{uri.fragment}" if uri&.fragment
54
72
  label = label_tag("element_anchor", Alchemy.t(:anchor), class: "control-label")
55
73
  options = [[page.nil? ? Alchemy.t("Select a page first") : Alchemy.t("None"), ""]]
56
- options += [[fragment, fragment]] if is_selected? && fragment
74
+ options += [[fragment, fragment]] if page && fragment
57
75
 
58
76
  select = select_tag("element_anchor", options_for_select(options, fragment), is: "alchemy-select", disabled: page.nil?)
59
77
  select_component = content_tag("alchemy-dom-id-api-select", select, {page: page&.id})
@@ -9,7 +9,7 @@ module Alchemy
9
9
  @nodes = Node.all
10
10
  @nodes = @nodes.includes(:parent)
11
11
  @nodes = @nodes.where(language_id: params[:language_id]) if params[:language_id]
12
- @nodes = @nodes.ransack(params[:filter]).result
12
+ @nodes = @nodes.ransack(params[:filter]).result.order(:lft)
13
13
 
14
14
  if params[:page]
15
15
  @nodes = @nodes.page(params[:page]).per(params[:per_page])
@@ -0,0 +1,41 @@
1
+ import { reloadPreview } from "alchemy_admin/components/preview_window"
2
+ import IngredientAnchorLink from "alchemy_admin/ingredient_anchor_link"
3
+
4
+ class Action extends HTMLElement {
5
+ constructor() {
6
+ super()
7
+
8
+ // map action names with Javascript functions
9
+ this.actions = {
10
+ // add a intermediate closeCurrentDialog - action
11
+ // this will be gone, if all dialogs are working with a promise and
12
+ // we don't have to implicitly close the dialog
13
+ closeCurrentDialog: Alchemy.closeCurrentDialog,
14
+ reloadPreview,
15
+ updateAnchorIcon: IngredientAnchorLink.updateIcon
16
+ }
17
+ }
18
+
19
+ connectedCallback() {
20
+ const func = this.actions[this.name]
21
+
22
+ if (func) {
23
+ func(...this.params)
24
+ } else {
25
+ console.error(`Unknown Alchemy action: ${this.name}`)
26
+ }
27
+ }
28
+
29
+ get name() {
30
+ return this.getAttribute("name")
31
+ }
32
+
33
+ get params() {
34
+ if (this.hasAttribute("params")) {
35
+ return JSON.parse(this.getAttribute("params"))
36
+ }
37
+ return []
38
+ }
39
+ }
40
+
41
+ customElements.define("alchemy-action", Action)
@@ -1,3 +1,4 @@
1
+ import "alchemy_admin/components/action"
1
2
  import "alchemy_admin/components/attachment_select"
2
3
  import "alchemy_admin/components/button"
3
4
  import "alchemy_admin/components/char_counter"
@@ -23,12 +23,14 @@ class NodeSelect extends RemoteSelect {
23
23
  */
24
24
  _renderListEntry(node) {
25
25
  const ancestors = node.ancestors.map((a) => a.name)
26
+ const seperator = `<alchemy-icon name="arrow-right-s"></alchemy-icon>`
27
+
26
28
  return `
27
29
  <div class="node-select--node">
28
30
  <alchemy-icon name="menu-2"></alchemy-icon>
29
31
  <div class="node-select--node-display_name">
30
32
  <span class="node-select--node-ancestors">
31
- ${ancestors.join(" /&nbsp;")}
33
+ ${ancestors.length > 0 ? ancestors.join(seperator) + seperator : ""}
32
34
  </span>
33
35
  <span class="node-select--node-name">
34
36
  ${node.name}
@@ -8,7 +8,6 @@ import { translate } from "alchemy_admin/i18n"
8
8
  import Dirty from "alchemy_admin/dirty"
9
9
  import * as FixedElements from "alchemy_admin/fixed_elements"
10
10
  import { growl } from "alchemy_admin/growler"
11
- import IngredientAnchorLink from "alchemy_admin/ingredient_anchor_link"
12
11
  import ImageLoader from "alchemy_admin/image_loader"
13
12
  import ImageCropper from "alchemy_admin/image_cropper"
14
13
  import Initializer from "alchemy_admin/initializer"
@@ -44,7 +43,6 @@ Object.assign(Alchemy, {
44
43
  growl,
45
44
  ImageLoader: ImageLoader.init,
46
45
  ImageCropper,
47
- IngredientAnchorLink,
48
46
  LinkDialog,
49
47
  pictureSelector,
50
48
  pleaseWaitOverlay,
@@ -1,20 +1,133 @@
1
1
  <% if !Alchemy::Current.preview_page? && @page && can?(:edit_content, @page) %>
2
2
  <alchemy-menubar>
3
3
  <template>
4
- <%= stylesheet_link_tag("alchemy/menubar") %>
5
- <div id="alchemy_menubar" data-turbo="false">
6
- <ul>
7
- <li><%= link_to Alchemy.t(:to_alchemy), alchemy.admin_dashboard_url %></li>
8
- <li><%= link_to Alchemy.t(:edit_page), alchemy.edit_admin_page_url(@page) %></li>
9
- <li>
10
- <%= form_tag Alchemy.logout_path, method: Alchemy.logout_method do %>
11
- <%= button_tag Alchemy.t(:logout) %>
12
- <% end %>
13
- </li>
14
- </ul>
4
+ <style>
5
+ .menubar {
6
+ --icon-size: 24px;
7
+ --panel-width: 525px;
8
+ --border-radius: 3px;
9
+ --left-offset: 0px;
10
+ display: flex;
11
+ position: fixed;
12
+ top: 0;
13
+ left: calc(-1 * (var(--panel-width) - var(--left-offset)));
14
+ z-index: 10000;
15
+ background: #214166;
16
+ transition: left 0.25s cubic-bezier(0.23, 1, 0.32, 1);
17
+ box-shadow: 0 0 0 1px #fff;
18
+ box-sizing: border-box;
19
+ border-bottom-right-radius: var(--border-radius);
20
+ padding: 12px 16px 12px;
21
+ gap: 12px;
22
+ justify-content: space-between;
23
+ align-items: center;
24
+ flex-wrap: nowrap;
25
+ overflow: hidden;
26
+ font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode",
27
+ "Lucida Sans", Verdana, Tahoma, sans-serif;
28
+ font-size: 13px;
29
+ }
30
+
31
+ .menubar * {
32
+ box-sizing: border-box;
33
+ margin: 0;
34
+ padding: 0;
35
+ }
36
+
37
+ .menubar:hover,
38
+ .menubar:focus-within {
39
+ left: 0;
40
+ }
41
+
42
+ .menubar > svg {
43
+ width: var(--icon-size);
44
+ height: var(--icon-size);
45
+ flex-shrink: 0;
46
+ margin-left: 4px;
47
+ }
48
+
49
+ .menubar .button {
50
+ display: inline-flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ gap: 8px;
54
+ font-size: inherit;
55
+ font-weight: 700;
56
+ cursor: pointer;
57
+ border-radius: var(--border-radius);
58
+ background-color: transparent;
59
+ border: 1px solid rgba(255, 255, 255, 0.5);
60
+ color: #fff;
61
+ margin: 0;
62
+ padding: 0.75em 1.5em;
63
+ appearance: none;
64
+ transition: all 250ms;
65
+ -webkit-font-smoothing: antialiased;
66
+ -moz-osx-font-smoothing: grayscale;
67
+ text-decoration: none;
68
+ white-space: nowrap;
69
+ }
70
+
71
+ .menubar .button:hover {
72
+ text-decoration: none;
73
+ background-color: rgba(0, 0, 0, 0.25);
74
+ border-color: rgba(255, 255, 255, 0.75)
75
+ }
76
+
77
+ .menubar .button:active {
78
+ box-shadow: inset 0px 1px 1px -1px #333;
79
+ }
80
+
81
+ .menubar .button:focus {
82
+ outline: solid 2px #eca96e;
83
+ outline-offset: 2px;
84
+ }
85
+ </style>
86
+
87
+ <div class="menubar" data-turbo="false">
88
+ <%= link_to alchemy.admin_dashboard_url, class: "button" do %>
89
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
90
+ <path d="M7.82843 10.9999H20V12.9999H7.82843L13.1924 18.3638L11.7782 19.778L4 11.9999L11.7782 4.22168L13.1924 5.63589L7.82843 10.9999Z"></path>
91
+ </svg>
92
+ <%= Alchemy.t(:to_alchemy) %>
93
+ <% end %>
94
+ <%= link_to alchemy.edit_admin_page_url(@page), class: "button" do %>
95
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
96
+ <path d="M6.41421 15.89L16.5563 5.74785L15.1421 4.33363L5 14.4758V15.89H6.41421ZM7.24264 17.89H3V13.6473L14.435 2.21231C14.8256 1.82179 15.4587 1.82179 15.8492 2.21231L18.6777 5.04074C19.0682 5.43126 19.0682 6.06443 18.6777 6.45495L7.24264 17.89ZM3 19.89H21V21.89H3V19.89Z"></path>
97
+ </svg>
98
+ <%= Alchemy.t(:edit_page) %>
99
+ <% end %>
100
+ <%= form_tag Alchemy.logout_path, method: Alchemy.logout_method do %>
101
+ <%= button_tag class: "button" do %>
102
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
103
+ <path d="M5 22C4.44772 22 4 21.5523 4 21V3C4 2.44772 4.44772 2 5 2H19C19.5523 2 20 2.44772 20 3V6H18V4H6V20H18V18H20V21C20 21.5523 19.5523 22 19 22H5ZM18 16V13H11V11H18V8L23 12L18 16Z"></path>
104
+ </svg>
105
+ <%= Alchemy.t(:logout) %>
106
+ <% end %>
107
+ <% end %>
108
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
109
+ <path fill="#fff" d="M15.7 7.9L9.9 2.2 2.1 4.3 0 12.1l5.7 5.8 7.8-2.1 2.2-7.9zm-5.3 10.3l-1.2 4.4 3.2 3.2 4.4-1.2 1.2-4.4-3.2-3.2-4.4 1.2zM23.5 7.3L17.2 9l-1.7 6.2 4.5 4.6 6.2-1.7 1.7-6.2-4.4-4.6z"/>
110
+ </svg>
15
111
  </div>
16
112
  </template>
17
- </alchemy-menubar>
18
113
 
19
- <%= javascript_include_tag('menubar', type: "module") %>
114
+ <script type="module">
115
+ class Menubar extends HTMLElement {
116
+ constructor() {
117
+ super()
118
+ const template = this.querySelector("template")
119
+ const attachedShadowRoot = this.attachShadow({ mode: "open" })
120
+ attachedShadowRoot.appendChild(template.content.cloneNode(true))
121
+ }
122
+
123
+ connectedCallback() {
124
+ const bar = this.shadowRoot.querySelector(".menubar")
125
+ const width = bar.offsetWidth
126
+ bar.style = `--panel-width: ${width}px; --left-offset: calc(var(--icon-size) + 32px);`
127
+ }
128
+ }
129
+
130
+ customElements.define("alchemy-menubar", Menubar)
131
+ </script>
132
+ </alchemy-menubar>
20
133
  <% end %>
@@ -7,6 +7,7 @@
7
7
  collection: css_classes,
8
8
  include_blank: Alchemy.t('None') %>
9
9
  <%- else -%>
10
+ <% Alchemy::Deprecation.warn %(Float positioning in File ingredients is deprecated. If you rely on them, please use `css_classes` in your "#{ingredient.role}" settings instead.) %>
10
11
  <%= f.input :css_class,
11
12
  label: Alchemy.t(:position_in_text),
12
13
  collection: [
@@ -13,6 +13,7 @@
13
13
  collection: ingredient.settings[:css_classes],
14
14
  include_blank: Alchemy.t('None') %>
15
15
  <%- else -%>
16
+ <% Alchemy::Deprecation.warn %(Float positioning in Picture ingredients is deprecated. If you rely on them, please use `css_classes` in your "#{ingredient.role}" settings instead.) %>
16
17
  <%= f.input :css_class,
17
18
  label: Alchemy.t(:position_in_text),
18
19
  collection: [
@@ -0,0 +1,5 @@
1
+ <alchemy-action name="closeCurrentDialog"></alchemy-action>
2
+ <alchemy-action name="reloadPreview"></alchemy-action>
3
+ <% if @ingredient.settings[:anchor] %>
4
+ <alchemy-action name="updateAnchorIcon" params="<%= [@ingredient.id, @ingredient.dom_id.present?].to_json %>"></alchemy-action>
5
+ <% end %>
@@ -7,10 +7,11 @@
7
7
  <th class="tools"></th>
8
8
  </tr>
9
9
  <% nodes = @page.nodes.select(&:persisted?) %>
10
+ <% seperator = '<alchemy-icon name="arrow-right-s" style="vertical-align: text-bottom"></alchemy-icon>' %>
10
11
  <% if nodes.length > 0 %>
11
12
  <% nodes.each do |node| %>
12
13
  <tr class="even">
13
- <td><%= "#{node.ancestors.map(&:name).join(" / ")} / #{node.name}" %></td>
14
+ <td><%== "#{node.ancestors.map(&:name).join(seperator)}#{seperator}<strong>#{node.name}</strong>" %></td>
14
15
  <td class="tools">
15
16
  <sl-tooltip content="<%= Alchemy.t("delete_node") %>">
16
17
  <%= link_to render_icon(:minus),
@@ -21,11 +21,9 @@
21
21
  <%= render 'alchemy/admin/partials/routes' %>
22
22
  <%= javascript_include_tag('alchemy/admin/all', 'data-turbo-track' => true) %>
23
23
  <%= javascript_importmap_tags("alchemy_admin", importmap: Alchemy.importmap) %>
24
- <% if Alchemy.admin_js_imports.any? %>
24
+ <% Alchemy.admin_js_imports.each do |path| %>
25
25
  <script type="module">
26
- <% Alchemy.admin_js_imports.each do |path| %>
27
- import "<%= path %>"
28
- <% end %>
26
+ import "<%= path %>"
29
27
  </script>
30
28
  <% end %>
31
29
  <%= yield :javascript_includes %>
@@ -21,14 +21,19 @@ module Alchemy
21
21
  end
22
22
 
23
23
  initializer "alchemy.importmap" do |app|
24
- Alchemy.importmap.draw(Engine.root.join("config", "importmap.rb"))
24
+ watch_paths = []
25
25
 
26
- package_path = Engine.root.join("app/javascript")
27
- vendor_packages_path = Engine.root.join("vendor/javascript")
28
- app.config.assets.paths += [package_path, vendor_packages_path]
26
+ Alchemy.admin_importmaps.each do |admin_import|
27
+ Alchemy.importmap.draw admin_import[:importmap_path]
28
+ watch_paths += admin_import[:source_paths]
29
+ app.config.assets.paths += admin_import[:source_paths]
30
+ if admin_import[:name] != "alchemy_admin"
31
+ Alchemy.admin_js_imports.add(admin_import[:name])
32
+ end
33
+ end
29
34
 
30
35
  if app.config.importmap.sweep_cache
31
- Alchemy.importmap.cache_sweeper(watches: package_path)
36
+ Alchemy.importmap.cache_sweeper(watches: watch_paths)
32
37
  ActiveSupport.on_load(:action_controller_base) do
33
38
  before_action { Alchemy.importmap.cache_sweeper.execute_if_updated }
34
39
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.shared_examples_for "a link dialog tab" do |name, title|
6
+ context "default configuration" do
7
+ it "should render a tab with a panel" do
8
+ expect(page).to have_selector("sl-tab[panel='overlay_tab_#{name}_link']")
9
+ expect(page).to have_selector("sl-tab-panel[name='overlay_tab_#{name}_link']")
10
+ end
11
+
12
+ it "should have a title" do
13
+ expect(page).to have_text(title)
14
+ end
15
+
16
+ it "should allow to add title input" do
17
+ expect(page).to have_selector("input[name=#{name}_link_title]", text: "")
18
+ end
19
+
20
+ it "is not active" do
21
+ expect(page).to_not have_selector("sl-tab[active]")
22
+ end
23
+ end
24
+
25
+ context "active tab" do
26
+ let(:is_selected) { true }
27
+
28
+ it "is active" do
29
+ expect(page).to have_selector("sl-tab[active]")
30
+ end
31
+ end
32
+
33
+ context "title input" do
34
+ let(:link_title) { "test" }
35
+
36
+ it "should have a pre-filled value" do
37
+ expect(page.find(:css, "input[name=#{name}_link_title]").value).to eq(link_title)
38
+ end
39
+ end
40
+ end
41
+
42
+ shared_examples_for "a link dialog - target select" do |name|
43
+ context "target select" do
44
+ context "without content" do
45
+ it "should allow to add target select" do
46
+ expect(page).to have_selector("select[name=#{name}_link_target]")
47
+ end
48
+ end
49
+
50
+ context "with content"
51
+ let(:link_target) { "_blank" }
52
+
53
+ it "should have a pre-filled value" do
54
+ expect(page.find(:css, "select[name=#{name}_link_target]").value).to eq(link_target)
55
+ end
56
+ end
57
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "7.2.0.b"
4
+ VERSION = "7.2.0.rc1"
5
5
 
6
6
  def self.version
7
7
  VERSION
data/lib/alchemy.rb CHANGED
@@ -62,6 +62,42 @@ module Alchemy
62
62
  @_admin_js_imports = Set[sources]
63
63
  end
64
64
 
65
+ # Additional importmaps to be included in the Alchemy admin UI
66
+ #
67
+ # Be sure to also pin modules with +Alchemy.importmap+.
68
+ #
69
+ # == Example
70
+ #
71
+ # # config/alchemy/importmap.rb
72
+ # Alchemy.importmap.pin "alchemy_solidus", to: "alchemy_solidus.js", preload: true
73
+ # Alchemy.importmap.pin_all_from Alchemy::Solidus::Engine.root.join("app/javascript/alchemy_solidus"),
74
+ # under: "alchemy_solidus",
75
+ # preload: true
76
+ #
77
+ # # lib/alchemy/solidus/engine.rb
78
+ # initializer "alchemy_solidus.assets", before: "alchemy.importmap" do |app|
79
+ # Alchemy.admin_importmaps.add({
80
+ # importmap_path: root.join("config/importmap.rb"),
81
+ # source_paths: [
82
+ # root.join("app/javascript")
83
+ # ],
84
+ # name: "alchemy_solidus"
85
+ # })
86
+ # app.config.assets.precompile << "alchemy_solidus/manifest.js"
87
+ # end
88
+ #
89
+ # @return [Set<Hash>]
90
+ def self.admin_importmaps
91
+ @_admin_importmaps ||= Set.new([{
92
+ importmap_path: Engine.root.join("config/importmap.rb"),
93
+ source_paths: [
94
+ Engine.root.join("app/javascript"),
95
+ Engine.root.join("vendor/javascript")
96
+ ],
97
+ name: "alchemy_admin"
98
+ }])
99
+ end
100
+
65
101
  # Define page publish targets
66
102
  #
67
103
  # A publish target is a ActiveJob that gets performed
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.0.b
4
+ version: 7.2.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2024-05-16 00:00:00.000000000 Z
16
+ date: 2024-05-24 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: actionmailer
@@ -766,7 +766,6 @@ files:
766
766
  - app/assets/stylesheets/alchemy/labels.scss
767
767
  - app/assets/stylesheets/alchemy/list_filter.scss
768
768
  - app/assets/stylesheets/alchemy/lists.scss
769
- - app/assets/stylesheets/alchemy/menubar.scss
770
769
  - app/assets/stylesheets/alchemy/navigation.scss
771
770
  - app/assets/stylesheets/alchemy/node-select.scss
772
771
  - app/assets/stylesheets/alchemy/nodes.scss
@@ -879,6 +878,7 @@ files:
879
878
  - app/helpers/alchemy/pages_helper.rb
880
879
  - app/helpers/alchemy/url_helper.rb
881
880
  - app/javascript/alchemy_admin.js
881
+ - app/javascript/alchemy_admin/components/action.js
882
882
  - app/javascript/alchemy_admin/components/alchemy_html_element.js
883
883
  - app/javascript/alchemy_admin/components/attachment_select.js
884
884
  - app/javascript/alchemy_admin/components/button.js
@@ -943,7 +943,6 @@ files:
943
943
  - app/javascript/alchemy_admin/utils/format.js
944
944
  - app/javascript/alchemy_admin/utils/max.js
945
945
  - app/javascript/alchemy_admin/utils/string_conversions.js
946
- - app/javascript/menubar.js
947
946
  - app/jobs/alchemy/base_job.rb
948
947
  - app/jobs/alchemy/publish_page_job.rb
949
948
  - app/mailers/alchemy/base_mailer.rb
@@ -1070,7 +1069,7 @@ files:
1070
1069
  - app/views/alchemy/admin/ingredients/_text_fields.html.erb
1071
1070
  - app/views/alchemy/admin/ingredients/_video_fields.html.erb
1072
1071
  - app/views/alchemy/admin/ingredients/edit.html.erb
1073
- - app/views/alchemy/admin/ingredients/update.js.erb
1072
+ - app/views/alchemy/admin/ingredients/update.turbo_stream.erb
1074
1073
  - app/views/alchemy/admin/languages/_form.html.erb
1075
1074
  - app/views/alchemy/admin/languages/_language.html.erb
1076
1075
  - app/views/alchemy/admin/languages/_table.html.erb
@@ -1335,6 +1334,7 @@ files:
1335
1334
  - lib/alchemy/test_support/shared_dom_ids_examples.rb
1336
1335
  - lib/alchemy/test_support/shared_ingredient_editor_examples.rb
1337
1336
  - lib/alchemy/test_support/shared_ingredient_examples.rb
1337
+ - lib/alchemy/test_support/shared_link_tab_examples.rb
1338
1338
  - lib/alchemy/test_support/shared_uploader_examples.rb
1339
1339
  - lib/alchemy/tinymce.rb
1340
1340
  - lib/alchemy/upgrader.rb
@@ -1,81 +0,0 @@
1
- /*
2
- *= require_self
3
- */
4
-
5
- @import "alchemy/variables";
6
- @import "alchemy/mixins";
7
-
8
- #alchemy_menubar {
9
- position: fixed;
10
- top: 0;
11
- left: -358px;
12
- width: 400px;
13
- z-index: 10000;
14
- background: $main-menu-bg-color;
15
- transition: left 0.25s cubic-bezier(0.23, 1, 0.32, 1);
16
- box-shadow: 0 0 0 1px $white;
17
- box-sizing: border-box;
18
- height: auto;
19
- padding: 8px 40px 8px 8px;
20
- overflow: hidden;
21
- font-family: $default-font-family;
22
- font-size: $base-font-size;
23
-
24
- * {
25
- box-sizing: border-box;
26
- margin: 0;
27
- padding: 0;
28
- }
29
-
30
- &:hover {
31
- left: 0;
32
- }
33
-
34
- &:after {
35
- content: "";
36
- width: 24px;
37
- height: 24px;
38
- position: absolute;
39
- right: 10px;
40
- top: 50%;
41
- background: image-url("alchemy/icon-white.svg") 1px 1px no-repeat;
42
- background-size: 24px 24px;
43
- transform: translateY(-50%);
44
- }
45
-
46
- ul {
47
- padding: 0;
48
- margin: 0;
49
- height: 100%;
50
-
51
- li {
52
- width: 33.333%;
53
- height: 100%;
54
- margin: 0;
55
- padding: 0 $default-padding;
56
- float: left;
57
- list-style-type: none;
58
- text-align: center;
59
-
60
- a,
61
- button {
62
- @include button-defaults(
63
- $background-color: $main-menu-bg-color,
64
- $hover-color: $blue,
65
- $hover-border-color: $white,
66
- $border: 1px solid $white,
67
- $box-shadow: none,
68
- $padding: 0.5em 0,
69
- $margin: 0,
70
- $color: $white
71
- );
72
- width: 100%;
73
- text-decoration: none !important;
74
-
75
- &:after {
76
- display: none;
77
- }
78
- }
79
- }
80
- }
81
- }
@@ -1,10 +0,0 @@
1
- class Menubar extends HTMLElement {
2
- constructor() {
3
- super()
4
- const template = this.querySelector("template")
5
- const attachedShadowRoot = this.attachShadow({ mode: "open" })
6
- attachedShadowRoot.appendChild(template.content.cloneNode(true))
7
- }
8
- }
9
-
10
- customElements.define("alchemy-menubar", Menubar)
@@ -1,7 +0,0 @@
1
- Alchemy.closeCurrentDialog(
2
- <% if @ingredient.settings[:anchor] %>
3
- function() {
4
- Alchemy.IngredientAnchorLink.updateIcon(<%= @ingredient.id %>, <%= @ingredient.dom_id.present? %>);
5
- }
6
- <% end %>
7
- );