alchemy_cms 7.2.0.b → 7.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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
- );