coveragebook_components 0.5.0 → 0.5.2
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.
- checksums.yaml +4 -4
- data/app/assets/build/coco/app.css +28 -28
- data/app/assets/build/coco/app.js +8113 -974
- data/app/assets/build/coco/book.css +3 -0
- data/app/assets/build/coco/book.js +37 -4
- data/app/assets/js/base/polyfills/request-submit.js +39 -0
- data/app/assets/js/base/setup.js +2 -0
- data/app/assets/js/helpers/path.js +9 -1
- data/app/assets/js/helpers/screenshot.js +20 -4
- data/app/components/coco/app/blocks/slide_editor/slide_editor.css +8 -11
- data/app/components/coco/app/blocks/slide_editor/slide_editor.html.erb +155 -196
- data/app/components/coco/app/blocks/slide_editor/slide_editor.js +92 -70
- data/app/components/coco/app/blocks/slide_editor/slide_editor.rb +36 -85
- data/app/components/coco/app/elements/alert/alert.css +1 -1
- data/app/components/coco/app/elements/button/button.css +11 -11
- data/app/components/coco/app/elements/color_picker_button/color_picker_button.html.erb +17 -16
- data/app/components/coco/app/elements/color_picker_button/color_picker_button.js +0 -8
- data/app/components/coco/app/elements/color_picker_button/color_picker_button.rb +2 -15
- data/app/components/coco/app/elements/image_picker/image_picker.html.erb +4 -4
- data/app/components/coco/app/elements/image_picker/image_picker.js +18 -6
- data/app/components/coco/app/elements/image_picker/image_picker.rb +2 -11
- data/app/components/coco/app/elements/image_picker_button/image_picker_button.html.erb +9 -8
- data/app/components/coco/app/elements/image_picker_button/image_picker_button.js +10 -23
- data/app/components/coco/app/elements/image_picker_button/image_picker_button.rb +19 -35
- data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.html.erb +7 -4
- data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.js +9 -3
- data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.rb +5 -20
- data/app/components/coco/base/button/button.css +3 -3
- data/app/components/coco/base/button/button.js +1 -3
- data/app/components/coco/base/image_uploader/image_uploader.html.erb +3 -2
- data/app/components/coco/base/image_uploader/image_uploader.rb +2 -12
- data/app/components/coco/book/blocks/slides/media_slide/media_slide.css +1 -1
- data/app/components/coco/component.rb +1 -0
- data/app/components/coco/concerns/has_name.rb +1 -1
- data/app/components/coco/concerns/translatable.rb +18 -0
- data/config/credentials/production.key +1 -0
- data/config/credentials/production.yml.enc +1 -0
- data/config/icons.json +886 -0
- data/config/locales/coco.en.yml +22 -0
- data/config/tailwind.app.config.cjs +21 -0
- data/config/tailwind.base.config.cjs +79 -0
- data/config/tailwind.book.config.cjs +20 -0
- data/config/tokens.cjs +229 -0
- data/config/utils.cjs +16 -0
- data/lib/coco.rb +1 -1
- metadata +13 -6
- data/app/assets/build/coco/app.dev.css +0 -3427
- data/app/assets/build/coco/app.dev.js +0 -24891
- data/app/assets/build/coco/book.dev.css +0 -992
- data/app/assets/build/coco/book.dev.js +0 -19840
@@ -2,26 +2,33 @@ import { CocoComponent } from "@js/coco";
|
|
2
2
|
import { getData } from "@helpers/alpine";
|
3
3
|
import { captureElementScreenshot } from "@helpers/screenshot";
|
4
4
|
import { isDark } from "@helpers/color";
|
5
|
-
import { wasSuccessful } from "@helpers/turbo_events";
|
6
5
|
import { withUndo } from "@js/base/mixins";
|
7
6
|
|
8
|
-
const thumbnailOpts = {
|
9
|
-
canvasWidth: 800,
|
10
|
-
canvasHeight: 500,
|
11
|
-
quality: 0.7,
|
12
|
-
pixelRatio: 1,
|
13
|
-
};
|
14
|
-
|
15
7
|
export default CocoComponent("appSlideEditor", (data) => {
|
8
|
+
const initialData = {
|
9
|
+
layout: data.layout,
|
10
|
+
title: data.title,
|
11
|
+
text1: data.text1,
|
12
|
+
bgColor: data.bgColor,
|
13
|
+
textColor: data.textColor,
|
14
|
+
bgImage: {
|
15
|
+
name: data.bgImage,
|
16
|
+
data: data.bgImage,
|
17
|
+
},
|
18
|
+
};
|
19
|
+
|
16
20
|
return {
|
17
21
|
use: [withUndo()],
|
18
22
|
|
19
|
-
...
|
20
|
-
|
23
|
+
...initialData,
|
24
|
+
|
25
|
+
saved: { ...initialData },
|
26
|
+
|
21
27
|
saving: false,
|
22
28
|
ready: false,
|
23
29
|
dragging: false,
|
24
30
|
errors: [],
|
31
|
+
|
25
32
|
thumbnailFile: null,
|
26
33
|
|
27
34
|
get bgImagePicker() {
|
@@ -29,21 +36,20 @@ export default CocoComponent("appSlideEditor", (data) => {
|
|
29
36
|
},
|
30
37
|
|
31
38
|
init() {
|
32
|
-
// Add property changes to the undo/redo history
|
33
|
-
this._fields.forEach((name) => {
|
34
|
-
this.$watch(name, (value, oldValue) =>
|
35
|
-
this.history.add(name, value, oldValue)
|
36
|
-
);
|
37
|
-
});
|
38
|
-
|
39
|
-
// Update thumbnail field when new thumbnail is generated
|
40
|
-
this.$watch("thumbnailFile", () => this._syncThumbnailField());
|
41
|
-
|
42
39
|
this.$watch("errors", (errors) => {
|
43
40
|
errors.forEach((error) => console.error(error.message)); // TODO display errors properly!
|
44
41
|
});
|
45
42
|
|
46
|
-
this.$nextTick(() =>
|
43
|
+
this.$nextTick(() => {
|
44
|
+
// Add property changes to the undo/redo history
|
45
|
+
this._fields.forEach((name) => {
|
46
|
+
this.$watch(name, (value, oldValue) =>
|
47
|
+
this.history.add(name, value, oldValue)
|
48
|
+
);
|
49
|
+
});
|
50
|
+
|
51
|
+
this.ready = true;
|
52
|
+
});
|
47
53
|
},
|
48
54
|
|
49
55
|
undo(name, value) {
|
@@ -65,10 +71,12 @@ export default CocoComponent("appSlideEditor", (data) => {
|
|
65
71
|
},
|
66
72
|
|
67
73
|
async save() {
|
68
|
-
this.
|
74
|
+
if (this.saving) return;
|
75
|
+
|
76
|
+
this.clearErrors();
|
69
77
|
this.saving = true;
|
70
78
|
|
71
|
-
if (this.
|
79
|
+
if (this.$refs.thumbnail) {
|
72
80
|
try {
|
73
81
|
await this._generateThumbnail();
|
74
82
|
} catch (error) {
|
@@ -79,12 +87,13 @@ export default CocoComponent("appSlideEditor", (data) => {
|
|
79
87
|
}
|
80
88
|
}
|
81
89
|
|
82
|
-
this.$refs.form.
|
83
|
-
},
|
90
|
+
const form = this.$refs.form && this.$refs.form.querySelector("form");
|
84
91
|
|
85
|
-
|
86
|
-
|
87
|
-
|
92
|
+
if (form && form.dataset.submit !== "false") {
|
93
|
+
form.requestSubmit();
|
94
|
+
} else {
|
95
|
+
this.submitSuccess();
|
96
|
+
}
|
88
97
|
},
|
89
98
|
|
90
99
|
submitSuccess() {
|
@@ -92,7 +101,7 @@ export default CocoComponent("appSlideEditor", (data) => {
|
|
92
101
|
this._fields.forEach((name) => (this.saved[name] = this[name]));
|
93
102
|
this.saving = false;
|
94
103
|
|
95
|
-
console.info("
|
104
|
+
console.info("Slide changes saved");
|
96
105
|
this.$dispatch("slide:save-end", { success: true });
|
97
106
|
},
|
98
107
|
|
@@ -106,49 +115,42 @@ export default CocoComponent("appSlideEditor", (data) => {
|
|
106
115
|
this._handleSaveError($event.detail.error, { event: $event });
|
107
116
|
},
|
108
117
|
|
109
|
-
|
110
|
-
|
111
|
-
backgroundColor: this.bgColor,
|
112
|
-
color: this.textColor,
|
113
|
-
backgroundImage:
|
114
|
-
this.bgImage && this.bgImage.data && `url('${this.bgImage.data}')`,
|
115
|
-
};
|
118
|
+
clearErrors() {
|
119
|
+
this.errors.length = 0;
|
116
120
|
},
|
117
121
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
}
|
122
|
+
syncFileInput(input, file) {
|
123
|
+
const dataTransfer = new DataTransfer();
|
124
|
+
if (file && file instanceof File) {
|
125
|
+
dataTransfer.items.add(file);
|
126
|
+
}
|
127
|
+
input.files = dataTransfer.files;
|
123
128
|
},
|
124
129
|
|
125
|
-
get
|
126
|
-
return (this.bgImage && this.bgImage.data)
|
127
|
-
? isDark(this.bgColor)
|
128
|
-
: false;
|
130
|
+
get hasBgImage() {
|
131
|
+
return !!(this.bgImage && this.bgImage.data);
|
129
132
|
},
|
130
133
|
|
131
|
-
get
|
132
|
-
return
|
134
|
+
get bgColorHex() {
|
135
|
+
return this.bgColor.replace("#", "");
|
133
136
|
},
|
134
137
|
|
135
|
-
get
|
136
|
-
return
|
138
|
+
get textColorHex() {
|
139
|
+
return this.textColor.replace("#", "");
|
137
140
|
},
|
138
141
|
|
139
|
-
|
140
|
-
|
142
|
+
get isDarkBg() {
|
143
|
+
return this.hasBgImage || this.bgColor ? isDark(this.bgColor) : false;
|
144
|
+
},
|
141
145
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
screenshotSlide.style[key] = value;
|
146
|
-
}
|
146
|
+
get _fields() {
|
147
|
+
return Object.keys(initialData);
|
148
|
+
},
|
147
149
|
|
150
|
+
async _generateThumbnail() {
|
148
151
|
this.thumbnailFile = await captureElementScreenshot(
|
149
|
-
|
150
|
-
"slide-thumbnail"
|
151
|
-
thumbnailOpts
|
152
|
+
this.$refs.thumbnail.firstElementChild,
|
153
|
+
"slide-thumbnail"
|
152
154
|
);
|
153
155
|
|
154
156
|
console.info(
|
@@ -157,22 +159,42 @@ export default CocoComponent("appSlideEditor", (data) => {
|
|
157
159
|
);
|
158
160
|
},
|
159
161
|
|
160
|
-
// Called whenever a new thumbnail is generated,
|
161
|
-
// adds the thumbnail file to the hidden thumbnail file
|
162
|
-
// input upload field.
|
163
|
-
_syncThumbnailField() {
|
164
|
-
const dataTransfer = new DataTransfer();
|
165
|
-
if (this.thumbnailFile instanceof File) {
|
166
|
-
dataTransfer.items.add(this.thumbnailFile);
|
167
|
-
}
|
168
|
-
this.$refs.thumbnail.files = dataTransfer.files;
|
169
|
-
},
|
170
|
-
|
171
162
|
_handleSaveError(message = "Error saving slide", context = {}) {
|
172
163
|
this.errors.push({ message, context });
|
173
164
|
this.saving = false;
|
174
165
|
|
175
166
|
this.$dispatch("slide:save-end", { success: false });
|
176
167
|
},
|
168
|
+
|
169
|
+
/* bindings */
|
170
|
+
|
171
|
+
slide: {
|
172
|
+
[":style"]() {
|
173
|
+
return {
|
174
|
+
backgroundColor: this.bgColor,
|
175
|
+
color: this.textColor,
|
176
|
+
backgroundImage: this.hasBgImage
|
177
|
+
? `url('${this.bgImage.data}')`
|
178
|
+
: "none",
|
179
|
+
};
|
180
|
+
},
|
181
|
+
[":class"]() {
|
182
|
+
return {
|
183
|
+
"slide-bg-dark": this.isDarkBg,
|
184
|
+
"slide-bg-light": !this.isDarkBg,
|
185
|
+
};
|
186
|
+
},
|
187
|
+
},
|
188
|
+
|
189
|
+
input: {
|
190
|
+
layout: { "x-model.fill": "layout" },
|
191
|
+
title: { "x-model.fill": "title" },
|
192
|
+
text1: { "x-model.fill": "text1" },
|
193
|
+
bgColor: { "x-model.fill": "bgColorHex" },
|
194
|
+
textColor: { "x-model.fill": "textColorHex" },
|
195
|
+
bgImage: { "x-effect": "syncFileInput($el, bgImage.file)" },
|
196
|
+
bgImagePurge: { ":checked": "!hasBgImage" },
|
197
|
+
thumbnailImage: { "x-effect": "syncFileInput($el, thumbnailFile)" },
|
198
|
+
},
|
177
199
|
};
|
178
200
|
});
|
@@ -6,119 +6,70 @@ module Coco
|
|
6
6
|
include Coco::BookHelper
|
7
7
|
include Coco::FormatHelper
|
8
8
|
|
9
|
-
renders_one :
|
10
|
-
|
9
|
+
renders_one :title, ->(value = nil, **textarea_options) do
|
10
|
+
Coco::App::Elements::SeamlessTextarea.new(**textarea_options,
|
11
|
+
value: value,
|
12
|
+
multiline: false,
|
13
|
+
focus: true,
|
14
|
+
data: {role: "title-editor"},
|
15
|
+
x: {modelable: "value", model: "title"})
|
11
16
|
end
|
12
17
|
|
13
|
-
renders_one :
|
14
|
-
|
18
|
+
renders_one :text_1, ->(value = nil, **textarea_options) do
|
19
|
+
Coco::App::Elements::SeamlessTextarea.new(**textarea_options,
|
20
|
+
value: value,
|
21
|
+
multiline: false,
|
22
|
+
data: {role: "text-1-editor"},
|
23
|
+
x: {modelable: "value", model: "text1"})
|
15
24
|
end
|
16
25
|
|
17
|
-
renders_one :
|
18
|
-
|
26
|
+
renders_one :bg_color, ->(value = nil, **input_options) do
|
27
|
+
@bg_color_options = {selected: value, **input_options}
|
19
28
|
end
|
20
29
|
|
21
|
-
renders_one :
|
22
|
-
|
30
|
+
renders_one :text_color, ->(value = nil, **input_options) do
|
31
|
+
@text_color_options = {selected: value, **input_options}
|
23
32
|
end
|
24
33
|
|
25
|
-
renders_one :
|
26
|
-
|
34
|
+
renders_one :bg_image, ->(src = nil, **input_options) do
|
35
|
+
@bg_image_options = {src: src, **input_options}
|
27
36
|
end
|
28
37
|
|
29
|
-
|
30
|
-
|
38
|
+
def initialize(generate_thumbnail: true, **kwargs)
|
39
|
+
@generate_thumbnail = generate_thumbnail
|
31
40
|
end
|
32
41
|
|
33
|
-
|
34
|
-
add_field(:layout, name, input_options)
|
35
|
-
end
|
36
|
-
|
37
|
-
before_render do
|
38
|
-
with_layout(value: "basic") unless layout?
|
39
|
-
end
|
40
|
-
|
41
|
-
attr_reader :fields
|
42
|
-
|
43
|
-
def initialize(form: nil, **kwargs)
|
44
|
-
@form_args = form.to_h
|
45
|
-
@model = @form_args[:model]
|
46
|
-
@fields = {}
|
47
|
-
end
|
48
|
-
|
49
|
-
def form_args(attrs = {})
|
50
|
-
html_args = @form_args.fetch(:html, {})
|
51
|
-
attrs[:class] = class_names(html_args[:class], attrs[:class])
|
52
|
-
html_args.merge!(attrs)
|
53
|
-
@form_args.merge({html: html_args})
|
54
|
-
end
|
42
|
+
def generate_thumbnail? = @generate_thumbnail
|
55
43
|
|
56
44
|
def slide_args
|
57
45
|
{
|
58
|
-
bg_image:
|
59
|
-
bg_color_hex:
|
60
|
-
text_color_hex:
|
61
|
-
layout: layout_value,
|
46
|
+
bg_image: bg_image_src,
|
47
|
+
bg_color_hex: bg_color_css,
|
48
|
+
text_color_hex: text_color_css,
|
62
49
|
render_empty: true
|
63
50
|
}
|
64
51
|
end
|
65
52
|
|
66
53
|
def alpine_props
|
67
54
|
{
|
68
|
-
bg_color:
|
69
|
-
text_color:
|
70
|
-
title:
|
71
|
-
text_1:
|
72
|
-
|
73
|
-
bg_image: {
|
74
|
-
name: ((field?(:bg_image) && bg_image_value.present?) && File.basename(bg_image_value)),
|
75
|
-
data: (bg_image_value if field?(:bg_image))
|
76
|
-
}
|
55
|
+
bg_color: bg_color_css,
|
56
|
+
text_color: text_color_css,
|
57
|
+
title: title&.value,
|
58
|
+
text_1: text_1&.value,
|
59
|
+
bg_image: bg_image_src
|
77
60
|
}
|
78
61
|
end
|
79
62
|
|
80
|
-
def
|
81
|
-
|
82
|
-
field_name = name.to_s.gsub(matches[0], "").to_sym
|
83
|
-
method = matches[1].to_sym
|
84
|
-
if field?(field_name)
|
85
|
-
fields[field_name].public_send(method)
|
86
|
-
end
|
87
|
-
end
|
63
|
+
def bg_color_css
|
64
|
+
@bg_color_options[:selected] if @bg_color_options
|
88
65
|
end
|
89
66
|
|
90
|
-
def
|
91
|
-
|
92
|
-
field?(field_name.to_sym)
|
67
|
+
def text_color_css
|
68
|
+
@text_color_options[:selected] if @text_color_options
|
93
69
|
end
|
94
70
|
|
95
|
-
def
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
def add_field(field_name, name, options = {})
|
102
|
-
model_value = @model&.public_send(name) if @model&.respond_to?(name)
|
103
|
-
fields[field_name] = SlideInput.new(name, model_value, options)
|
104
|
-
end
|
105
|
-
|
106
|
-
SlideInput = Data.define(:name, :model_value, :input_options) do
|
107
|
-
def value
|
108
|
-
input_options.fetch(:value, model_value)
|
109
|
-
end
|
110
|
-
|
111
|
-
def options
|
112
|
-
input_options.tap do |options|
|
113
|
-
options[:value] = value
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def to_json
|
118
|
-
"\"#{value}\"" if value.present?
|
119
|
-
end
|
120
|
-
|
121
|
-
alias_method :to_s, :value
|
71
|
+
def bg_image_src
|
72
|
+
@bg_image_options[:src] if @bg_image_options
|
122
73
|
end
|
123
74
|
end
|
124
75
|
end
|
@@ -6,7 +6,7 @@
|
|
6
6
|
@apply transition-all overflow-hidden;
|
7
7
|
border-radius: 44px;
|
8
8
|
|
9
|
-
.button-content {
|
9
|
+
& > .button-element .button-content {
|
10
10
|
@apply font-semibold;
|
11
11
|
}
|
12
12
|
}
|
@@ -23,7 +23,7 @@
|
|
23
23
|
@apply bg-primary-700;
|
24
24
|
}
|
25
25
|
|
26
|
-
&[data-state="loading"] .button-icon {
|
26
|
+
&[data-state="loading"] > .button-element .button-icon {
|
27
27
|
@apply text-positive-300;
|
28
28
|
}
|
29
29
|
|
@@ -44,7 +44,7 @@
|
|
44
44
|
@apply bg-gray-blend-100;
|
45
45
|
}
|
46
46
|
|
47
|
-
&[data-state="loading"] .button-icon {
|
47
|
+
&[data-state="loading"] > .button-element .button-icon {
|
48
48
|
@apply text-positive-300;
|
49
49
|
}
|
50
50
|
|
@@ -65,7 +65,7 @@
|
|
65
65
|
@apply bg-gray-blend-100;
|
66
66
|
}
|
67
67
|
|
68
|
-
&[data-state="loading"] .button-icon {
|
68
|
+
&[data-state="loading"] > .button-element .button-icon {
|
69
69
|
@apply text-positive-300;
|
70
70
|
}
|
71
71
|
|
@@ -86,7 +86,7 @@
|
|
86
86
|
@apply bg-gray-blend-100;
|
87
87
|
}
|
88
88
|
|
89
|
-
&[data-state="loading"] .button-icon {
|
89
|
+
&[data-state="loading"] > .button-element .button-icon {
|
90
90
|
@apply text-positive-300;
|
91
91
|
}
|
92
92
|
|
@@ -106,7 +106,7 @@
|
|
106
106
|
@apply bg-negative-800;
|
107
107
|
}
|
108
108
|
|
109
|
-
&[data-state="loading"] .button-icon {
|
109
|
+
&[data-state="loading"] > .button-element .button-icon {
|
110
110
|
@apply text-negative-300;
|
111
111
|
}
|
112
112
|
|
@@ -126,7 +126,7 @@
|
|
126
126
|
@apply bg-gray-blend-100;
|
127
127
|
}
|
128
128
|
|
129
|
-
&[data-state="loading"] .button-icon {
|
129
|
+
&[data-state="loading"] > .button-element .button-icon {
|
130
130
|
@apply text-negative-300;
|
131
131
|
}
|
132
132
|
|
@@ -146,7 +146,7 @@
|
|
146
146
|
@apply bg-warning-800;
|
147
147
|
}
|
148
148
|
|
149
|
-
&[data-state="loading"] .button-icon {
|
149
|
+
&[data-state="loading"] > .button-element .button-icon {
|
150
150
|
@apply text-warning-300;
|
151
151
|
}
|
152
152
|
|
@@ -166,7 +166,7 @@
|
|
166
166
|
@apply bg-gray-blend-100;
|
167
167
|
}
|
168
168
|
|
169
|
-
&[data-state="loading"] .button-icon {
|
169
|
+
&[data-state="loading"] > .button-element .button-icon {
|
170
170
|
@apply text-warning-300;
|
171
171
|
}
|
172
172
|
|
@@ -186,7 +186,7 @@
|
|
186
186
|
@apply bg-info-800;
|
187
187
|
}
|
188
188
|
|
189
|
-
&[data-state="loading"] .button-icon {
|
189
|
+
&[data-state="loading"] > .button-element .button-icon {
|
190
190
|
@apply text-info-300;
|
191
191
|
}
|
192
192
|
|
@@ -206,7 +206,7 @@
|
|
206
206
|
@apply bg-gray-blend-100;
|
207
207
|
}
|
208
208
|
|
209
|
-
&[data-state="loading"] .button-icon {
|
209
|
+
&[data-state="loading"] > .button-element .button-icon {
|
210
210
|
@apply text-info-300;
|
211
211
|
}
|
212
212
|
|
@@ -1,23 +1,24 @@
|
|
1
1
|
<%= render component_tag(x: {
|
2
2
|
data: x_data("appColorPickerButton", {selected: selected}),
|
3
|
-
"@dropdown:mount": "onDropdownMount
|
3
|
+
"@dropdown:mount": "onDropdownMount"
|
4
4
|
}) do %>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
<div x-effect="$dispatch('color-picker:change', {color: selectedColor})">
|
6
|
+
<%= render button do |btn| %>
|
7
|
+
<% btn.with_icon do %>
|
8
|
+
<%= icon %>
|
9
|
+
<% end %>
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
<% btn.with_dropdown do %>
|
12
|
+
<div
|
13
|
+
@color-picker:select="selectedColor = $event.detail.selectedColor;"
|
14
|
+
x-cloak>
|
15
|
+
<div data-role="color-picker">
|
16
|
+
<%= picker %>
|
17
|
+
</div>
|
17
18
|
</div>
|
18
|
-
|
19
|
-
<% end %>
|
19
|
+
<% end %>
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
<%= button_text %>
|
22
|
+
<% end %>
|
23
|
+
</div>
|
23
24
|
<% end %>
|
@@ -12,14 +12,6 @@ export default CocoComponent("appColorPickerButton", ({ selected }) => {
|
|
12
12
|
);
|
13
13
|
},
|
14
14
|
|
15
|
-
get selectedColorHex() {
|
16
|
-
return this.selectedColor && this.selectedColor.replace("#", "");
|
17
|
-
},
|
18
|
-
|
19
|
-
set selectedColorHex(value) {
|
20
|
-
this.selectedColor == "#" + value.replace("#", "");
|
21
|
-
},
|
22
|
-
|
23
15
|
onDropdownMount() {
|
24
16
|
this.getPicker().setSelectedColor(this.selectedColor);
|
25
17
|
},
|
@@ -26,7 +26,7 @@ module Coco
|
|
26
26
|
end
|
27
27
|
|
28
28
|
renders_one :icon, ->(&block) do
|
29
|
-
tag.span class: "picker-icon", ":style": "{color: selectedColor}", &block
|
29
|
+
tag.span class: "picker-icon", style: "color: #{@selected}", ":style": "{color: selectedColor}", &block
|
30
30
|
end
|
31
31
|
|
32
32
|
before_render do
|
@@ -40,26 +40,13 @@ module Coco
|
|
40
40
|
|
41
41
|
attr_reader :selected
|
42
42
|
|
43
|
-
def initialize(selected: nil,
|
43
|
+
def initialize(selected: nil, **kwargs)
|
44
44
|
@selected = selected
|
45
45
|
|
46
46
|
@size = kwargs.fetch(:size, :default)&.to_sym
|
47
47
|
if @size.in?(Coco::App::Elements::Button::SIZE_ALIASES.keys) && !kwargs.key?(:resize)
|
48
48
|
@size, @resize = Coco::App::Elements::Button::SIZE_ALIASES.fetch(@size)
|
49
49
|
end
|
50
|
-
|
51
|
-
@form_builder = form_builder
|
52
|
-
@input_name = input_name
|
53
|
-
end
|
54
|
-
|
55
|
-
def input
|
56
|
-
if @input_name
|
57
|
-
if @form_builder
|
58
|
-
@form_builder.hidden_field(@input_name, value: @selected, "x-model": "selectedColorHex")
|
59
|
-
else
|
60
|
-
hidden_field_tag(@input_name, @selected, "x-model": "selectedColorHex")
|
61
|
-
end
|
62
|
-
end
|
63
50
|
end
|
64
51
|
|
65
52
|
def button_text
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%= render component_tag(x: {data: x_data("appImagePicker",
|
1
|
+
<%= render component_tag(x: {data: x_data("appImagePicker", {src: src}), ":class": "hasImage && 'has-image'"}) do %>
|
2
2
|
<div class="picker-file-name" x-show="hasImage">
|
3
3
|
<label x-text="name"></label>
|
4
4
|
</div>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<%= render Coco::ImageUploader.new(
|
7
7
|
**uploader_args,
|
8
8
|
click: false) do %>
|
9
|
-
<div class="picker-blank-state" x-show="
|
9
|
+
<div class="picker-blank-state" x-show="!$parent.src">
|
10
10
|
<% if blank_state_text? %>
|
11
11
|
<div class="picker-placeholder-text">
|
12
12
|
<%= blank_state_text %>
|
@@ -18,8 +18,8 @@
|
|
18
18
|
<% end %>
|
19
19
|
</div>
|
20
20
|
</div>
|
21
|
-
<div x-show="
|
22
|
-
<img :src="
|
21
|
+
<div x-show="$parent.src" x-cloak>
|
22
|
+
<img :src="$parent.src" class="picker-thumbnail">
|
23
23
|
</div>
|
24
24
|
<% end %>
|
25
25
|
</div>
|
@@ -1,17 +1,23 @@
|
|
1
1
|
import { CocoComponent } from "@js/coco";
|
2
2
|
import { getComponent } from "@helpers/alpine";
|
3
|
+
import { basename } from "@helpers/path";
|
3
4
|
|
4
|
-
export default CocoComponent("appImagePicker", ({
|
5
|
+
export default CocoComponent("appImagePicker", ({ src }) => {
|
5
6
|
return {
|
6
7
|
use: [],
|
7
|
-
|
8
|
+
|
9
|
+
image: {
|
10
|
+
name: basename(src),
|
11
|
+
file: null,
|
12
|
+
data: src,
|
13
|
+
},
|
8
14
|
|
9
15
|
get name() {
|
10
|
-
return this.image
|
16
|
+
return this.image.name;
|
11
17
|
},
|
12
18
|
|
13
19
|
get src() {
|
14
|
-
return this.image
|
20
|
+
return this.image.data;
|
15
21
|
},
|
16
22
|
|
17
23
|
get hasImage() {
|
@@ -23,8 +29,14 @@ export default CocoComponent("appImagePicker", ({ image }) => {
|
|
23
29
|
},
|
24
30
|
|
25
31
|
setImage(file, silent = false) {
|
26
|
-
this.image =
|
32
|
+
this.image = {
|
33
|
+
file: file.file,
|
34
|
+
name: file.name,
|
35
|
+
data: file.data,
|
36
|
+
};
|
37
|
+
|
27
38
|
this.uploader.setFile(file);
|
39
|
+
|
28
40
|
if (silent === false) {
|
29
41
|
this.$dispatch("image-picker:select", { image: this.image });
|
30
42
|
}
|
@@ -32,7 +44,7 @@ export default CocoComponent("appImagePicker", ({ image }) => {
|
|
32
44
|
|
33
45
|
clearImage(silent = false) {
|
34
46
|
this.uploader.clear();
|
35
|
-
this.image = null;
|
47
|
+
this.image = { name: null, data: null, file: null };
|
36
48
|
if (silent === false) {
|
37
49
|
this.$dispatch("image-picker:clear");
|
38
50
|
}
|