alchemy_cms 7.2.0.b → 7.2.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -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: 1ab8abb53948898989f3d8a8b597737a63da7e541ef5cd0de08a23d505fb7729
|
4
|
+
data.tar.gz: ce85ac778f47134d757d46c9c4d56879d98cb456b32ebe3a1d8133f757643b8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6e1a233177adbdac5ae8a61fae4653f392fe458938e1b87752dc3c1317ad881fd0e6270e8a612bb8f7b45e31430584f8bd27e018ec7d0fb702a6518483daf34
|
7
|
+
data.tar.gz: 1effffc22e22464f9cf4cf0d1e22107746d93cbac01a2610f7e29707de7f25b186e3c87bc2527dfe820fbb611e4cf65c1dfb1c6d3ba062e0ae3cb7bc0224a0de
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 7.2.0.rc2 (2024-05-27)
|
4
|
+
|
5
|
+
- Harden specs [#2896](https://github.com/AlchemyCMS/alchemy_cms/pull/2896) ([tvdeyen](https://github.com/tvdeyen))
|
6
|
+
- Support trailing slash in internal link tab selection [#2895](https://github.com/AlchemyCMS/alchemy_cms/pull/2895) ([tvdeyen](https://github.com/tvdeyen))
|
7
|
+
|
8
|
+
## 7.2.0.rc1 (2024-05-24)
|
9
|
+
|
10
|
+
- Deprecate float positioning in File and Picture ingredients [#2894](https://github.com/AlchemyCMS/alchemy_cms/pull/2894) ([tvdeyen](https://github.com/tvdeyen))
|
11
|
+
- Use inline styles for menubar component [#2893](https://github.com/AlchemyCMS/alchemy_cms/pull/2893) ([tvdeyen](https://github.com/tvdeyen))
|
12
|
+
- Fix link selection of internal root or locale urls [#2892](https://github.com/AlchemyCMS/alchemy_cms/pull/2892) ([tvdeyen](https://github.com/tvdeyen))
|
13
|
+
- 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))
|
14
|
+
- Prevent jumping of toggle button in element window [#2888](https://github.com/AlchemyCMS/alchemy_cms/pull/2888) ([sascha-karnatz](https://github.com/sascha-karnatz))
|
15
|
+
- Fix preview window width for smaller viewports [#2887](https://github.com/AlchemyCMS/alchemy_cms/pull/2887) ([tvdeyen](https://github.com/tvdeyen))
|
16
|
+
- fix(alchemy_link.plugin): Avoid getting an absolute URL [#2885](https://github.com/AlchemyCMS/alchemy_cms/pull/2885) ([tvdeyen](https://github.com/tvdeyen))
|
17
|
+
- Allow to add to Alchemy's importmap from Rails application [#2884](https://github.com/AlchemyCMS/alchemy_cms/pull/2884) ([tvdeyen](https://github.com/tvdeyen))
|
18
|
+
- Only set url on attachment select if attachment found [#2882](https://github.com/AlchemyCMS/alchemy_cms/pull/2882) ([tvdeyen](https://github.com/tvdeyen))
|
19
|
+
- Fix Preview Window width [#2879](https://github.com/AlchemyCMS/alchemy_cms/pull/2879) ([tvdeyen](https://github.com/tvdeyen))
|
20
|
+
- Allow engine importmaps [#2878](https://github.com/AlchemyCMS/alchemy_cms/pull/2878) ([tvdeyen](https://github.com/tvdeyen))
|
21
|
+
- fix(node-select): Display order and seperator style [#2877](https://github.com/AlchemyCMS/alchemy_cms/pull/2877) ([tvdeyen](https://github.com/tvdeyen))
|
22
|
+
|
3
23
|
## 7.2.0.b (2024-05-16)
|
4
24
|
|
5
25
|
- 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 = /\A\/(?<locale>[a-z]{2})?(?<slash>\/)?(?<urlname>.*?)(?<trailing-slash>\/)?\z/
|
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, _, 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.rc2
|
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-27 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)
|