coveragebook_components 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/build/coco/app.css +28 -28
  3. data/app/assets/build/coco/app.js +8113 -974
  4. data/app/assets/build/coco/book.css +3 -0
  5. data/app/assets/build/coco/book.js +37 -4
  6. data/app/assets/js/base/polyfills/request-submit.js +39 -0
  7. data/app/assets/js/base/setup.js +2 -0
  8. data/app/assets/js/helpers/path.js +9 -1
  9. data/app/assets/js/helpers/screenshot.js +20 -4
  10. data/app/components/coco/app/blocks/slide_editor/slide_editor.css +8 -11
  11. data/app/components/coco/app/blocks/slide_editor/slide_editor.html.erb +155 -196
  12. data/app/components/coco/app/blocks/slide_editor/slide_editor.js +92 -70
  13. data/app/components/coco/app/blocks/slide_editor/slide_editor.rb +36 -85
  14. data/app/components/coco/app/elements/alert/alert.css +1 -1
  15. data/app/components/coco/app/elements/button/button.css +11 -11
  16. data/app/components/coco/app/elements/color_picker_button/color_picker_button.html.erb +17 -16
  17. data/app/components/coco/app/elements/color_picker_button/color_picker_button.js +0 -8
  18. data/app/components/coco/app/elements/color_picker_button/color_picker_button.rb +2 -15
  19. data/app/components/coco/app/elements/image_picker/image_picker.html.erb +4 -4
  20. data/app/components/coco/app/elements/image_picker/image_picker.js +18 -6
  21. data/app/components/coco/app/elements/image_picker/image_picker.rb +2 -11
  22. data/app/components/coco/app/elements/image_picker_button/image_picker_button.html.erb +9 -8
  23. data/app/components/coco/app/elements/image_picker_button/image_picker_button.js +10 -23
  24. data/app/components/coco/app/elements/image_picker_button/image_picker_button.rb +19 -35
  25. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.html.erb +7 -4
  26. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.js +9 -3
  27. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.rb +5 -20
  28. data/app/components/coco/base/button/button.css +3 -3
  29. data/app/components/coco/base/button/button.js +1 -3
  30. data/app/components/coco/base/image_uploader/image_uploader.html.erb +3 -2
  31. data/app/components/coco/base/image_uploader/image_uploader.rb +2 -12
  32. data/app/components/coco/book/blocks/slides/media_slide/media_slide.css +1 -1
  33. data/app/components/coco/component.rb +1 -0
  34. data/app/components/coco/concerns/has_name.rb +1 -1
  35. data/app/components/coco/concerns/translatable.rb +18 -0
  36. data/config/credentials/production.key +1 -0
  37. data/config/credentials/production.yml.enc +1 -0
  38. data/config/icons.json +886 -0
  39. data/config/locales/coco.en.yml +22 -0
  40. data/config/tailwind.app.config.cjs +21 -0
  41. data/config/tailwind.base.config.cjs +79 -0
  42. data/config/tailwind.book.config.cjs +20 -0
  43. data/config/tokens.cjs +229 -0
  44. data/config/utils.cjs +16 -0
  45. data/lib/coco.rb +1 -1
  46. metadata +13 -6
  47. data/app/assets/build/coco/app.dev.css +0 -3427
  48. data/app/assets/build/coco/app.dev.js +0 -24891
  49. data/app/assets/build/coco/book.dev.css +0 -992
  50. 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
- ...data,
20
- saved: { ...data },
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(() => (this.ready = true));
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.errors.length = 0;
74
+ if (this.saving) return;
75
+
76
+ this.clearErrors();
69
77
  this.saving = true;
70
78
 
71
- if (this.shouldGenerateThumbnail) {
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.requestSubmit();
83
- },
90
+ const form = this.$refs.form && this.$refs.form.querySelector("form");
84
91
 
85
- submitEnd(event) {
86
- handler = wasSuccessful(event) ? "submitSuccess" : "submitError";
87
- this[handler](event);
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("Custom slide saved");
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
- get slideStyles() {
110
- return {
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
- get slideClasses() {
119
- return {
120
- "slide-bg-dark": this.isDarkBg,
121
- "slide-bg-light": !this.isDarkBg,
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 isDarkBg() {
126
- return (this.bgImage && this.bgImage.data) || this.bgColor
127
- ? isDark(this.bgColor)
128
- : false;
130
+ get hasBgImage() {
131
+ return !!(this.bgImage && this.bgImage.data);
129
132
  },
130
133
 
131
- get shouldGenerateThumbnail() {
132
- return !!this.$refs.thumbnail;
134
+ get bgColorHex() {
135
+ return this.bgColor.replace("#", "");
133
136
  },
134
137
 
135
- get _fields() {
136
- return Object.keys(data);
138
+ get textColorHex() {
139
+ return this.textColor.replace("#", "");
137
140
  },
138
141
 
139
- async _generateThumbnail() {
140
- const screenshotSlide = this.$refs.screenshot.firstElementChild;
142
+ get isDarkBg() {
143
+ return this.hasBgImage || this.bgColor ? isDark(this.bgColor) : false;
144
+ },
141
145
 
142
- // The HTML-to-image library didn't like these styles being dynamically
143
- // bound in the usual Alpine way so we have to manually apply them instead.
144
- for (const [key, value] of Object.entries(this.slideStyles)) {
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
- screenshotSlide,
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 :bg_color, ->(name: :bg_color_hex, **input_options) do
10
- add_field(:bg_color, name, input_options)
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 :text_color, ->(name: :text_color_hex, **input_options) do
14
- add_field(:text_color, name, input_options)
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 :bg_image, ->(name: :bg_image, **input_options) do
18
- add_field(:bg_image, name, input_options)
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 :thumbnail, ->(name: :thumbnail_image, **input_options) do
22
- add_field(:thumbnail, name, input_options)
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 :title, ->(name: :title, **input_options) do
26
- add_field(:title, name, input_options)
34
+ renders_one :bg_image, ->(src = nil, **input_options) do
35
+ @bg_image_options = {src: src, **input_options}
27
36
  end
28
37
 
29
- renders_one :text_1, ->(name: :text_1, **input_options) do
30
- add_field(:text_1, name, input_options)
38
+ def initialize(generate_thumbnail: true, **kwargs)
39
+ @generate_thumbnail = generate_thumbnail
31
40
  end
32
41
 
33
- renders_one :layout, ->(name: :layout, **input_options) do
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: bg_image_value,
59
- bg_color_hex: bg_color_value,
60
- text_color_hex: text_color_value,
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: format_css_hex_color(bg_color_value),
69
- text_color: format_css_hex_color(text_color_value),
70
- title: title_value,
71
- text_1: text_1_value,
72
- layout: layout_value,
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 method_missing(name, *args, &block)
81
- name.match(/_(value|options|name)/) do |matches|
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 respond_to_missing?(name, include_private = false)
91
- field_name = name.to_s.gsub(/_(value|options|name)/, "")
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 field?(name)
96
- fields.key?(name)
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
@@ -34,7 +34,7 @@
34
34
  }
35
35
  }
36
36
 
37
- &:not(.with-title) .alert-message {
37
+ &.with-title .alert-message {
38
38
  @apply mt-1;
39
39
  }
40
40
 
@@ -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
- <%= input %>
6
- <%= render button do |btn| %>
7
- <% btn.with_icon do %>
8
- <%= icon %>
9
- <% end %>
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
- <% 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 %>
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
- </div>
19
- <% end %>
19
+ <% end %>
20
20
 
21
- <%= button_text %>
22
- <% end %>
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, input_name: nil, form_builder: nil, **kwargs)
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", alpine_props), ":class": "hasImage && 'has-image'"}) do %>
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="!fileData">
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="fileData" x-cloak>
22
- <img :src="fileData" class="picker-thumbnail">
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", ({ image }) => {
5
+ export default CocoComponent("appImagePicker", ({ src }) => {
5
6
  return {
6
7
  use: [],
7
- image: null,
8
+
9
+ image: {
10
+ name: basename(src),
11
+ file: null,
12
+ data: src,
13
+ },
8
14
 
9
15
  get name() {
10
- return this.image && this.image.name;
16
+ return this.image.name;
11
17
  },
12
18
 
13
19
  get src() {
14
- return this.image && this.image.data;
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 = file;
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
  }