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.
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
  }