katalyst-content 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/controllers/content/editor/container_controller.js +36 -2
- data/app/assets/javascripts/controllers/content/editor/list_controller.js +6 -5
- data/app/assets/javascripts/controllers/content/editor/trix_controller.js +11 -1
- data/app/assets/javascripts/utils/content/editor/item.js +1 -20
- data/app/assets/javascripts/utils/content/editor/rules-engine.js +1 -1
- data/app/assets/stylesheets/katalyst/content/editor/_new-items.scss +1 -0
- data/app/controllers/katalyst/content/items_controller.rb +4 -4
- data/app/helpers/katalyst/content/editor/container.rb +6 -0
- data/app/helpers/katalyst/content/editor/errors.rb +24 -0
- data/app/helpers/katalyst/content/editor_helper.rb +6 -0
- data/app/helpers/katalyst/content/frontend_helper.rb +17 -2
- data/app/models/concerns/katalyst/content/container.rb +5 -0
- data/app/models/concerns/katalyst/content/version.rb +10 -4
- data/app/models/katalyst/content/figure.rb +1 -1
- data/app/models/katalyst/content/item.rb +5 -1
- data/app/views/katalyst/content/columns/_column.html.erb +1 -1
- data/app/views/katalyst/content/figures/_figure.html+form.erb +4 -10
- data/config/locales/en.yml +2 -0
- data/lib/katalyst/content/version.rb +1 -1
- metadata +3 -4
- data/app/assets/javascripts/controllers/content/editor/image_field_controller.js +0 -90
- data/app/helpers/katalyst/content/editor/image_field.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 427d77cb72196829474e950c6e3d184aa6d7215b4c01204d033202bb527e4e98
|
4
|
+
data.tar.gz: 7f42967dbc39534e2ed23f4380fb7e2161d4d5f6fbddee6abd03e5be2939b070
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bf16bbe915b8f3910b1707be2cf96c47f186de277b66e2f71c5a2abac11f23c2994482245ab99330fb86fa7e11a87b4e8495e2f4133dd7661ce2e8775de0d08
|
7
|
+
data.tar.gz: 0c910c64438aa44be0ed04f9f2b666e325a8b5135b9ff307f396c5fe70b40bcc16011ad00400b882f8a30a35bdc9dd8d39c1e3e45726f38cf1ab582b1f9de538
|
@@ -26,6 +26,36 @@ export default class ContainerController extends Controller {
|
|
26
26
|
this.container.reset();
|
27
27
|
}
|
28
28
|
|
29
|
+
drop(event) {
|
30
|
+
this.container.reindex(); // set indexes before calculating previous
|
31
|
+
|
32
|
+
const item = getEventItem(event);
|
33
|
+
const previous = item.previousItem;
|
34
|
+
|
35
|
+
let delta = 0;
|
36
|
+
if (previous === undefined) {
|
37
|
+
// if previous does not exist, set depth to 0
|
38
|
+
delta = -item.depth;
|
39
|
+
} else if (
|
40
|
+
previous.isLayout &&
|
41
|
+
item.nextItem &&
|
42
|
+
item.nextItem.depth > previous.depth
|
43
|
+
) {
|
44
|
+
// if previous is a layout and next is a child of previous, make item a child of previous
|
45
|
+
delta = previous.depth - item.depth + 1;
|
46
|
+
} else {
|
47
|
+
// otherwise, make item a sibling of previous
|
48
|
+
delta = previous.depth - item.depth;
|
49
|
+
}
|
50
|
+
|
51
|
+
item.traverse((child) => {
|
52
|
+
child.depth += delta;
|
53
|
+
});
|
54
|
+
|
55
|
+
this.#update();
|
56
|
+
event.preventDefault();
|
57
|
+
}
|
58
|
+
|
29
59
|
remove(event) {
|
30
60
|
const item = getEventItem(event);
|
31
61
|
|
@@ -38,7 +68,9 @@ export default class ContainerController extends Controller {
|
|
38
68
|
nest(event) {
|
39
69
|
const item = getEventItem(event);
|
40
70
|
|
41
|
-
item.
|
71
|
+
item.traverse((child) => {
|
72
|
+
child.depth += 1;
|
73
|
+
});
|
42
74
|
|
43
75
|
this.#update();
|
44
76
|
event.preventDefault();
|
@@ -47,7 +79,9 @@ export default class ContainerController extends Controller {
|
|
47
79
|
deNest(event) {
|
48
80
|
const item = getEventItem(event);
|
49
81
|
|
50
|
-
item.
|
82
|
+
item.traverse((child) => {
|
83
|
+
child.depth -= 1;
|
84
|
+
});
|
51
85
|
|
52
86
|
this.#update();
|
53
87
|
event.preventDefault();
|
@@ -46,7 +46,7 @@ export default class ListController extends Controller {
|
|
46
46
|
}
|
47
47
|
|
48
48
|
drop(event) {
|
49
|
-
|
49
|
+
let item = this.dragItem();
|
50
50
|
|
51
51
|
if (!item) return;
|
52
52
|
|
@@ -55,17 +55,18 @@ export default class ListController extends Controller {
|
|
55
55
|
swap(this.dropTarget(event.target), item);
|
56
56
|
|
57
57
|
if (item.dataset.hasOwnProperty("newItem")) {
|
58
|
+
const placeholder = item;
|
58
59
|
const template = document.createElement("template");
|
59
60
|
template.innerHTML = event.dataTransfer.getData("text/html");
|
60
|
-
|
61
|
+
item = template.content.querySelector("li");
|
61
62
|
|
62
|
-
this.element.replaceChild(
|
63
|
+
this.element.replaceChild(item, placeholder);
|
63
64
|
setTimeout(() =>
|
64
|
-
|
65
|
+
item.querySelector("[role='button'][value='edit']").click()
|
65
66
|
);
|
66
67
|
}
|
67
68
|
|
68
|
-
this.
|
69
|
+
this.dispatch("drop", { target: item, bubbles: true, prefix: "content" });
|
69
70
|
}
|
70
71
|
|
71
72
|
dragend() {
|
@@ -25,7 +25,7 @@ delete Trix.config.blockAttributes.heading1;
|
|
25
25
|
* input does not permit. Uses a permissive regex pattern which is not suitable
|
26
26
|
* for untrusted use cases.
|
27
27
|
*/
|
28
|
-
const LINK_PATTERN = "(https
|
28
|
+
const LINK_PATTERN = "(https?|mailto:|tel:|/|#).*?";
|
29
29
|
|
30
30
|
/**
|
31
31
|
* Customize default toolbar:
|
@@ -76,3 +76,13 @@ Trix.config.toolbar.getDefaultHTML = () => {
|
|
76
76
|
</div>
|
77
77
|
`;
|
78
78
|
};
|
79
|
+
|
80
|
+
/**
|
81
|
+
* If the <trix-editor> element is in the HTML when Trix loads, then Trix will have already injected the toolbar content
|
82
|
+
* before our code gets a chance to run. Fix that now.
|
83
|
+
*
|
84
|
+
* Note: in Trix 2 this is likely to no longer be necessary.
|
85
|
+
*/
|
86
|
+
document.querySelectorAll("trix-toolbar").forEach((e) => {
|
87
|
+
e.innerHTML = Trix.config.toolbar.getDefaultHTML();
|
88
|
+
});
|
@@ -131,7 +131,7 @@ export default class Item {
|
|
131
131
|
|
132
132
|
callback(this);
|
133
133
|
this.#traverseCollapsed(callback);
|
134
|
-
expanded.forEach((item) =>
|
134
|
+
expanded.forEach((item) => item.#traverseCollapsed(callback));
|
135
135
|
}
|
136
136
|
|
137
137
|
/**
|
@@ -148,16 +148,6 @@ export default class Item {
|
|
148
148
|
});
|
149
149
|
}
|
150
150
|
|
151
|
-
/**
|
152
|
-
* Increase the depth of this item and its descendants.
|
153
|
-
* If this causes it to become a child of a collapsed item, then collapse this item.
|
154
|
-
*/
|
155
|
-
nest() {
|
156
|
-
this.traverse((child) => {
|
157
|
-
child.depth += 1;
|
158
|
-
});
|
159
|
-
}
|
160
|
-
|
161
151
|
/**
|
162
152
|
* Move the given item into this element's hidden children list.
|
163
153
|
* Assumes the list already exists.
|
@@ -168,15 +158,6 @@ export default class Item {
|
|
168
158
|
this.#childrenListElement.appendChild(item.node);
|
169
159
|
}
|
170
160
|
|
171
|
-
/**
|
172
|
-
* Decrease the depth of this item (and its descendants).
|
173
|
-
*/
|
174
|
-
deNest() {
|
175
|
-
this.traverse((child) => {
|
176
|
-
child.depth -= 1;
|
177
|
-
});
|
178
|
-
}
|
179
|
-
|
180
161
|
/**
|
181
162
|
* Collapses visible (logical) children into this element's hidden children
|
182
163
|
* list, creating it if it doesn't already exist.
|
@@ -59,7 +59,7 @@ export default class RulesEngine {
|
|
59
59
|
* First item can't have a parent, so its depth should always be 0
|
60
60
|
*/
|
61
61
|
firstItemDepthZero(item) {
|
62
|
-
if (item.index === 0 &&
|
62
|
+
if (item.index === 0 && item.depth !== 0) {
|
63
63
|
this.debug(`enforce depth on item ${item.index}: ${item.depth} => 0`);
|
64
64
|
|
65
65
|
item.depth = 0;
|
@@ -13,6 +13,10 @@ module Katalyst
|
|
13
13
|
render locals: { item: @container.items.build(item_params) }
|
14
14
|
end
|
15
15
|
|
16
|
+
def edit
|
17
|
+
render locals: { item: @item }
|
18
|
+
end
|
19
|
+
|
16
20
|
def create
|
17
21
|
@item = item = @container.items.build(item_params)
|
18
22
|
if item.save
|
@@ -22,10 +26,6 @@ module Katalyst
|
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
25
|
-
def edit
|
26
|
-
render locals: { item: @item }
|
27
|
-
end
|
28
|
-
|
29
29
|
def update
|
30
30
|
@item.attributes = item_params
|
31
31
|
|
@@ -6,6 +6,7 @@ module Katalyst
|
|
6
6
|
class Container < Base
|
7
7
|
ACTIONS = <<~ACTIONS.gsub(/\s+/, " ").freeze
|
8
8
|
submit->#{CONTAINER_CONTROLLER}#reindex
|
9
|
+
content:drop->#{CONTAINER_CONTROLLER}#drop
|
9
10
|
content:reindex->#{CONTAINER_CONTROLLER}#reindex
|
10
11
|
content:reset->#{CONTAINER_CONTROLLER}#reset
|
11
12
|
ACTIONS
|
@@ -13,12 +14,17 @@ module Katalyst
|
|
13
14
|
def build(options)
|
14
15
|
form_with(model: container, **default_options(id: container_form_id, **options)) do |form|
|
15
16
|
concat hidden_input
|
17
|
+
concat errors
|
16
18
|
concat(capture { yield form })
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
22
|
private
|
21
23
|
|
24
|
+
def errors
|
25
|
+
Editor::Errors.new(self, container).build
|
26
|
+
end
|
27
|
+
|
22
28
|
# Hidden input ensures that if the container is empty then the controller
|
23
29
|
# receives an empty array.
|
24
30
|
def hidden_input
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Content
|
5
|
+
module Editor
|
6
|
+
class Errors < Base
|
7
|
+
def build(**options)
|
8
|
+
turbo_frame_tag dom_id(container, :errors) do
|
9
|
+
next unless container.errors.any?
|
10
|
+
|
11
|
+
tag.div(class: "content-errors", **options) do
|
12
|
+
tag.h2("Errors in content") +
|
13
|
+
tag.ul(class: "errors") do
|
14
|
+
container.errors.each do |error|
|
15
|
+
concat(tag.li(error.message))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -25,6 +25,12 @@ module Katalyst
|
|
25
25
|
Editor::List.new(self, container).items(item)
|
26
26
|
end
|
27
27
|
|
28
|
+
# Generate a turbo stream fragment that will show structural errors to the user.
|
29
|
+
def content_editor_errors(container:, **options)
|
30
|
+
turbo_stream.replace(dom_id(container, :errors),
|
31
|
+
Editor::Errors.new(self, container).build(**options))
|
32
|
+
end
|
33
|
+
|
28
34
|
# Gene
|
29
35
|
def content_editor_new_item(item:, container: item.container, **options, &block)
|
30
36
|
Editor::NewItem.new(self, container).build(item, **options, &block)
|
@@ -4,17 +4,32 @@ module Katalyst
|
|
4
4
|
module Content
|
5
5
|
module FrontendHelper
|
6
6
|
def render_content(version)
|
7
|
-
|
7
|
+
without_partial_path_prefix do
|
8
|
+
render partial: version.tree.select(&:visible?)
|
9
|
+
end
|
8
10
|
end
|
9
11
|
|
10
12
|
def render_content_items(items)
|
11
13
|
items = items.select(&:visible?)
|
12
|
-
|
14
|
+
without_partial_path_prefix do
|
15
|
+
render partial: items if items.any?
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
def content_item_tag(item, **options, &block)
|
16
20
|
FrontendBuilder.new(self, item).render(**options, &block)
|
17
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def without_partial_path_prefix
|
26
|
+
current = prefix_partial_path_with_controller_namespace
|
27
|
+
|
28
|
+
self.prefix_partial_path_with_controller_namespace = false
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
self.prefix_partial_path_with_controller_namespace = current
|
32
|
+
end
|
18
33
|
end
|
19
34
|
|
20
35
|
class FrontendBuilder
|
@@ -95,6 +95,11 @@ module Katalyst
|
|
95
95
|
self
|
96
96
|
end
|
97
97
|
|
98
|
+
# Required for testing items validation
|
99
|
+
def items_attributes
|
100
|
+
draft_version&.nodes&.as_json
|
101
|
+
end
|
102
|
+
|
98
103
|
# Updates the current draft version with new structure. Attributes should be structural information about the
|
99
104
|
# items, e.g. `{index => {id:, depth:}` or `[{id:, depth:}]`.
|
100
105
|
#
|
@@ -13,6 +13,12 @@ module Katalyst
|
|
13
13
|
# rubocop:enable Rails/ReflectionClassName
|
14
14
|
|
15
15
|
attribute :nodes, Katalyst::Content::Types::NodesType.new, default: -> { [] }
|
16
|
+
|
17
|
+
validate :ensure_items_exists
|
18
|
+
end
|
19
|
+
|
20
|
+
def ensure_items_exists
|
21
|
+
parent.errors.add(:items, :missing_item) unless items.all?(&:present?)
|
16
22
|
end
|
17
23
|
|
18
24
|
def items
|
@@ -22,10 +28,10 @@ module Katalyst
|
|
22
28
|
|
23
29
|
items = parent.items.where(id: nodes.map(&:id)).index_by(&:id)
|
24
30
|
nodes.map do |node|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
31
|
+
items[node.id]&.tap do |item|
|
32
|
+
item.index = node.index
|
33
|
+
item.depth = node.depth
|
34
|
+
end
|
29
35
|
end
|
30
36
|
end
|
31
37
|
|
@@ -20,7 +20,7 @@ module Katalyst
|
|
20
20
|
# if image has changed, duplicate the change, otherwise attach the existing blob
|
21
21
|
if source.attachment_changes["image"]
|
22
22
|
self.image = source.attachment_changes["image"].attachable
|
23
|
-
elsif source.image.attached?
|
23
|
+
elsif source.image.attached? && !source.image.marked_for_destruction?
|
24
24
|
image.attach(source.image.blob)
|
25
25
|
end
|
26
26
|
end
|
@@ -11,7 +11,7 @@ module Katalyst
|
|
11
11
|
belongs_to :container, polymorphic: true
|
12
12
|
|
13
13
|
validates :heading, presence: true
|
14
|
-
validates :background, presence: true, inclusion: { in: config.backgrounds }
|
14
|
+
validates :background, presence: true, inclusion: { in: config.backgrounds }, if: :validate_background?
|
15
15
|
|
16
16
|
after_initialize :initialize_tree
|
17
17
|
|
@@ -43,6 +43,10 @@ module Katalyst
|
|
43
43
|
self.parent ||= nil
|
44
44
|
self.children ||= []
|
45
45
|
end
|
46
|
+
|
47
|
+
def validate_background?
|
48
|
+
true
|
49
|
+
end
|
46
50
|
end
|
47
51
|
end
|
48
52
|
end
|
@@ -2,16 +2,10 @@
|
|
2
2
|
<%= render "hidden_fields", form: form %>
|
3
3
|
<%= render "form_errors", form: form %>
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
<%= builder.preview class: "image-wrapper" %>
|
10
|
-
</div>
|
11
|
-
<%= form.file_field :image, **builder.file_input_options %>
|
12
|
-
<hint><%= builder.hint_text %></hint>
|
13
|
-
</div>
|
14
|
-
<% end %>
|
5
|
+
<div class="field">
|
6
|
+
<%= form.label :image %>
|
7
|
+
<%= form.file_field :image %>
|
8
|
+
</div>
|
15
9
|
|
16
10
|
<div class="field">
|
17
11
|
<%= form.label :heading %>
|
data/config/locales/en.yml
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: katalyst-content
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katalyst Interactive
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_storage_validations
|
@@ -35,7 +35,6 @@ files:
|
|
35
35
|
- README.md
|
36
36
|
- app/assets/config/katalyst-content.js
|
37
37
|
- app/assets/javascripts/controllers/content/editor/container_controller.js
|
38
|
-
- app/assets/javascripts/controllers/content/editor/image_field_controller.js
|
39
38
|
- app/assets/javascripts/controllers/content/editor/item_controller.js
|
40
39
|
- app/assets/javascripts/controllers/content/editor/list_controller.js
|
41
40
|
- app/assets/javascripts/controllers/content/editor/new_item_controller.js
|
@@ -58,7 +57,7 @@ files:
|
|
58
57
|
- app/controllers/katalyst/content/items_controller.rb
|
59
58
|
- app/helpers/katalyst/content/editor/base.rb
|
60
59
|
- app/helpers/katalyst/content/editor/container.rb
|
61
|
-
- app/helpers/katalyst/content/editor/
|
60
|
+
- app/helpers/katalyst/content/editor/errors.rb
|
62
61
|
- app/helpers/katalyst/content/editor/item.rb
|
63
62
|
- app/helpers/katalyst/content/editor/list.rb
|
64
63
|
- app/helpers/katalyst/content/editor/new_item.rb
|
@@ -1,90 +0,0 @@
|
|
1
|
-
import { Controller } from "@hotwired/stimulus";
|
2
|
-
|
3
|
-
export default class ImageFieldController extends Controller {
|
4
|
-
static targets = ["preview"];
|
5
|
-
static values = {
|
6
|
-
mimeTypes: Array,
|
7
|
-
};
|
8
|
-
|
9
|
-
#counter = 0;
|
10
|
-
|
11
|
-
onUpload(event) {
|
12
|
-
this.previewTarget.classList.remove("hidden");
|
13
|
-
|
14
|
-
const reader = new FileReader();
|
15
|
-
|
16
|
-
reader.onload = (e) => {
|
17
|
-
this.imageTag.src = e.target.result;
|
18
|
-
};
|
19
|
-
|
20
|
-
reader.readAsDataURL(event.currentTarget.files[0]);
|
21
|
-
}
|
22
|
-
|
23
|
-
drop(event) {
|
24
|
-
event.preventDefault();
|
25
|
-
|
26
|
-
const file = fileForEvent(event, this.mimeTypesValue);
|
27
|
-
if (file) {
|
28
|
-
const dT = new DataTransfer();
|
29
|
-
dT.items.add(file);
|
30
|
-
this.fileInput.files = dT.files;
|
31
|
-
this.fileInput.dispatchEvent(new Event("change"));
|
32
|
-
}
|
33
|
-
|
34
|
-
this.#counter = 0;
|
35
|
-
this.element.classList.remove("droppable");
|
36
|
-
}
|
37
|
-
|
38
|
-
dragover(event) {
|
39
|
-
event.preventDefault();
|
40
|
-
}
|
41
|
-
|
42
|
-
dragenter(event) {
|
43
|
-
event.preventDefault();
|
44
|
-
|
45
|
-
if (this.#counter === 0) {
|
46
|
-
this.element.classList.add("droppable");
|
47
|
-
}
|
48
|
-
this.#counter++;
|
49
|
-
}
|
50
|
-
|
51
|
-
dragleave(event) {
|
52
|
-
event.preventDefault();
|
53
|
-
|
54
|
-
this.#counter--;
|
55
|
-
if (this.#counter === 0) {
|
56
|
-
this.element.classList.remove("droppable");
|
57
|
-
}
|
58
|
-
}
|
59
|
-
|
60
|
-
get fileInput() {
|
61
|
-
return this.element.querySelector("input[type='file']");
|
62
|
-
}
|
63
|
-
|
64
|
-
get imageTag() {
|
65
|
-
return this.previewTarget.querySelector("img");
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
/**
|
70
|
-
* Given a drop event, find the first acceptable file.
|
71
|
-
* @param event {DropEvent}
|
72
|
-
* @param mimeTypes {String[]}
|
73
|
-
* @returns {File}
|
74
|
-
*/
|
75
|
-
function fileForEvent(event, mimeTypes) {
|
76
|
-
const accept = (file) => mimeTypes.indexOf(file.type) > -1;
|
77
|
-
|
78
|
-
let file;
|
79
|
-
|
80
|
-
if (event.dataTransfer.items) {
|
81
|
-
const item = [...event.dataTransfer.items].find(accept);
|
82
|
-
if (item) {
|
83
|
-
file = item.getAsFile();
|
84
|
-
}
|
85
|
-
} else {
|
86
|
-
file = [...event.dataTransfer.files].find(accept);
|
87
|
-
}
|
88
|
-
|
89
|
-
return file;
|
90
|
-
}
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Katalyst
|
4
|
-
module Content
|
5
|
-
module Editor
|
6
|
-
class ImageField < Base
|
7
|
-
IMAGE_FIELD_CONTROLLER = "content--editor--image-field"
|
8
|
-
|
9
|
-
ACTIONS = <<~ACTIONS.gsub(/\s+/, " ").freeze
|
10
|
-
dragover->#{IMAGE_FIELD_CONTROLLER}#dragover
|
11
|
-
dragenter->#{IMAGE_FIELD_CONTROLLER}#dragenter
|
12
|
-
dragleave->#{IMAGE_FIELD_CONTROLLER}#dragleave
|
13
|
-
drop->#{IMAGE_FIELD_CONTROLLER}#drop
|
14
|
-
ACTIONS
|
15
|
-
|
16
|
-
attr_accessor :item, :method
|
17
|
-
|
18
|
-
def build(item, method, **options, &block)
|
19
|
-
self.item = item
|
20
|
-
self.method = method
|
21
|
-
|
22
|
-
tag.div **default_options(**options) do
|
23
|
-
concat(capture { yield self }) if block
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def preview(**options)
|
28
|
-
add_option(options, :data, "#{IMAGE_FIELD_CONTROLLER}_target", "preview")
|
29
|
-
add_option(options, :class, "hidden") unless preview?
|
30
|
-
|
31
|
-
tag.div **options do
|
32
|
-
image_tag preview_url, class: "image-thumbnail"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def file_input_options(options = {})
|
37
|
-
add_option(options, :accept, config.image_mime_types.join(","))
|
38
|
-
add_option(options, :data, :action, "change->#{IMAGE_FIELD_CONTROLLER}#onUpload")
|
39
|
-
|
40
|
-
options
|
41
|
-
end
|
42
|
-
|
43
|
-
def hint_text
|
44
|
-
t("views.katalyst.content.item.size_hint", max_size: number_to_human_size(config.max_image_size.megabytes))
|
45
|
-
end
|
46
|
-
|
47
|
-
def preview?
|
48
|
-
value&.attached? && value&.persisted?
|
49
|
-
end
|
50
|
-
|
51
|
-
def preview_url
|
52
|
-
preview? ? main_app.url_for(value) : ""
|
53
|
-
end
|
54
|
-
|
55
|
-
def value
|
56
|
-
item.send(method)
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def default_options(**options)
|
62
|
-
add_option(options, :data, :controller, IMAGE_FIELD_CONTROLLER)
|
63
|
-
add_option(options, :data, :action, ACTIONS)
|
64
|
-
add_option(options, :data, :"#{IMAGE_FIELD_CONTROLLER}_mime_types_value",
|
65
|
-
config.image_mime_types.to_json)
|
66
|
-
|
67
|
-
options
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|