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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/app/assets/config/alchemy_manifest.js +0 -1
- data/app/assets/javascripts/tinymce/plugins/alchemy_link/plugin.min.js +1 -1
- data/app/assets/stylesheets/alchemy/elements.scss +1 -5
- data/app/assets/stylesheets/alchemy/node-select.scss +9 -1
- data/app/assets/stylesheets/alchemy/preview_window.scss +3 -3
- data/app/components/alchemy/admin/link_dialog/file_tab.rb +1 -1
- data/app/components/alchemy/admin/link_dialog/internal_tab.rb +21 -3
- data/app/controllers/alchemy/api/nodes_controller.rb +1 -1
- data/app/javascript/alchemy_admin/components/action.js +41 -0
- data/app/javascript/alchemy_admin/components/index.js +1 -0
- data/app/javascript/alchemy_admin/components/node_select.js +3 -1
- data/app/javascript/alchemy_admin.js +0 -2
- data/app/views/alchemy/_menubar.html.erb +126 -13
- data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +1 -0
- data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -0
- data/app/views/alchemy/admin/ingredients/update.turbo_stream.erb +5 -0
- data/app/views/alchemy/admin/nodes/_page_nodes.html.erb +2 -1
- data/app/views/layouts/alchemy/admin.html.erb +2 -4
- data/lib/alchemy/engine.rb +10 -5
- data/lib/alchemy/test_support/shared_link_tab_examples.rb +57 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy.rb +36 -0
- metadata +5 -5
- data/app/assets/stylesheets/alchemy/menubar.scss +0 -81
- data/app/javascript/menubar.js +0 -10
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f25f82412085150403ec2bc51c9c5ad2ec0f81f82a05b44789f496d9be86b52c
|
4
|
+
data.tar.gz: 748944b05a82eb3166a2320e00107262c59dff8c796c995a2875c4d45507a876
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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))
|
@@ -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
|
-
|
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:
|
22
|
+
@media screen and (min-width: 1777px) {
|
23
23
|
width: calc(
|
24
|
-
100vw - #{$collapsed-main-menu-width - $default-border-width} - #{$elements-window-
|
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 ||=
|
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",
|
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
|
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)
|
@@ -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(
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
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
|
-
<%
|
24
|
+
<% Alchemy.admin_js_imports.each do |path| %>
|
25
25
|
<script type="module">
|
26
|
-
|
27
|
-
import "<%= path %>"
|
28
|
-
<% end %>
|
26
|
+
import "<%= path %>"
|
29
27
|
</script>
|
30
28
|
<% end %>
|
31
29
|
<%= yield :javascript_includes %>
|
data/lib/alchemy/engine.rb
CHANGED
@@ -21,14 +21,19 @@ module Alchemy
|
|
21
21
|
end
|
22
22
|
|
23
23
|
initializer "alchemy.importmap" do |app|
|
24
|
-
|
24
|
+
watch_paths = []
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
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:
|
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
|
data/lib/alchemy/version.rb
CHANGED
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.
|
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
|
+
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.
|
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
|
-
}
|
data/app/javascript/menubar.js
DELETED
@@ -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)
|