coveragebook_components 0.5.0 → 0.5.1

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/build/coco/app.css +7 -5
  3. data/app/assets/build/coco/app.js +95 -48
  4. data/app/assets/build/coco/book.css +3 -0
  5. data/app/assets/build/coco/book.js +2 -2
  6. data/app/assets/js/helpers/path.js +9 -1
  7. data/app/components/coco/app/blocks/slide_editor/slide_editor.css +4 -0
  8. data/app/components/coco/app/blocks/slide_editor/slide_editor.html.erb +145 -190
  9. data/app/components/coco/app/blocks/slide_editor/slide_editor.js +70 -17
  10. data/app/components/coco/app/blocks/slide_editor/slide_editor.rb +37 -86
  11. data/app/components/coco/app/elements/alert/alert.css +1 -1
  12. data/app/components/coco/app/elements/color_picker_button/color_picker_button.html.erb +17 -16
  13. data/app/components/coco/app/elements/color_picker_button/color_picker_button.js +0 -8
  14. data/app/components/coco/app/elements/color_picker_button/color_picker_button.rb +2 -15
  15. data/app/components/coco/app/elements/image_picker/image_picker.html.erb +4 -4
  16. data/app/components/coco/app/elements/image_picker/image_picker.js +18 -6
  17. data/app/components/coco/app/elements/image_picker/image_picker.rb +2 -11
  18. data/app/components/coco/app/elements/image_picker_button/image_picker_button.html.erb +9 -8
  19. data/app/components/coco/app/elements/image_picker_button/image_picker_button.js +10 -23
  20. data/app/components/coco/app/elements/image_picker_button/image_picker_button.rb +19 -35
  21. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.html.erb +7 -4
  22. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.js +9 -3
  23. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.rb +5 -20
  24. data/app/components/coco/base/image_uploader/image_uploader.html.erb +3 -2
  25. data/app/components/coco/base/image_uploader/image_uploader.rb +2 -12
  26. data/app/components/coco/book/blocks/slides/media_slide/media_slide.css +1 -1
  27. data/lib/coco.rb +1 -1
  28. metadata +2 -6
  29. data/app/assets/build/coco/app.dev.css +0 -3427
  30. data/app/assets/build/coco/app.dev.js +0 -24891
  31. data/app/assets/build/coco/book.dev.css +0 -992
  32. data/app/assets/build/coco/book.dev.js +0 -19840
@@ -1,218 +1,172 @@
1
1
  <%= render component_tag(x: {
2
2
  data: x_data("appSlideEditor", alpine_props),
3
- ":class": "{ready}",
4
- "@direct-upload:error": "directUploadError",
3
+ ":class": "{ready}"
5
4
  }) do %>
6
- <%= form_with(**form_args(
7
- class: "editor-form",
8
- "x-ref": "form",
9
- "@turbo:submit-end": "submitEnd",
10
- "@turbo:fetch-request-error": "submitError",
11
- "@direct-uploads:end": "save"
12
- )) do |f| %>
13
- <%= f.hidden_field(layout_name, **layout_options, "x-model": "layout") if layout? %>
14
- <%= f.file_field(thumbnail_name, accept: "image/*", class: "editor-thumbnail-file", "x-ref": "thumbnail") if thumbnail? %>
15
-
16
- <div class="editor-toolbar">
17
- <%= coco_toolbar do |toolbar| %>
18
- <% toolbar.with_section do |section| %>
19
- <% if bg_color? %>
20
- <% section.with_color_picker_button(
21
- input_name: bg_color_name,
22
- selected: bg_color_value,
23
- form_builder: f,
24
- wrapper: {
25
- data: {
26
- role: "bg-color-picker"
27
- },
28
- x: {
29
- modelable: "selectedColor",
30
- model: "bgColor"
31
- }
5
+ <div class="editor-toolbar">
6
+ <%= coco_toolbar do |toolbar| %>
7
+ <% toolbar.with_section do |section| %>
8
+ <% if bg_color? %>
9
+ <% section.with_color_picker_button(
10
+ **@bg_color_options.to_h,
11
+ wrapper: {
12
+ data: {
13
+ role: "bg-color-picker"
14
+ },
15
+ x: {
16
+ "@color-picker:change": "bgColor = $event.detail.color"
32
17
  }
33
- ) do |button| %>
34
- <% button.with_picker %>
35
- <% button.with_text { "Background color" } %>
36
- <% button.with_icon do %>
37
- <svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="editor-icon-bg-color">
38
- <path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z"></path>
39
- </svg>
40
- <% end %>
18
+ }
19
+ ) do |button| %>
20
+ <% button.with_picker %>
21
+ <% button.with_text { "Background colour" } %>
22
+ <% button.with_icon do %>
23
+ <svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="editor-icon-bg-color">
24
+ <path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z"></path>
25
+ </svg>
41
26
  <% end %>
42
27
  <% end %>
43
-
44
- <% if bg_image? %>
45
- <% section.with_image_picker_button(
46
- icon: :image_plus,
47
- input_name: bg_image_name,
48
- src: bg_image_value,
49
- direct_upload: bg_image_options[:direct_upload],
50
- form_builder: f,
51
- wrapper: {
52
- data: {
53
- role: "bg-image-picker"
54
- },
55
- x: {
56
- modelable: "selectedImage",
57
- model: "bgImage"
58
- }
28
+ <% end %>
29
+
30
+ <% if bg_image? %>
31
+ <% section.with_image_picker_button(
32
+ icon: :image_plus,
33
+ src: bg_image_src,
34
+ wrapper: {
35
+ data: {
36
+ role: "bg-image-picker"
37
+ },
38
+ x: {
39
+ "@image-picker:change": "bgImage = $event.detail.image"
59
40
  }
60
- ) do |button| %>
61
- <% button.with_picker do |picker| %>
62
- <% picker.with_blank_state_text do %>
63
- Drag a jpg, png or gif onto the slide area...or...
64
- <% end %>
41
+ }
42
+ ) do |button| %>
43
+ <% button.with_picker do |picker| %>
44
+ <% picker.with_blank_state_text do %>
45
+ Drag a jpg, png or gif onto the slide area or&hellip;
65
46
  <% end %>
66
-
67
- Background image
68
47
  <% end %>
48
+
49
+ Background image
69
50
  <% end %>
51
+ <% end %>
70
52
 
71
- <% if text_color? %>
72
- <% section.with_color_picker_button(
73
- input_name: text_color_name,
74
- selected: text_color_value,
75
- form_builder: f,
76
- wrapper: {
77
- data: {
78
- role: "text-color-picker"
79
- },
80
- x: {
81
- modelable: "selectedColor",
82
- model: "textColor"
83
- }
53
+ <% if text_color? %>
54
+ <% section.with_color_picker_button(
55
+ **@text_color_options.to_h,
56
+ wrapper: {
57
+ data: {
58
+ role: "text-color-picker"
59
+ },
60
+ x: {
61
+ "@color-picker:change": "textColor = $event.detail.color"
84
62
  }
85
- ) do |button| %>
86
- <% button.with_picker %>
87
- <% button.with_text { "Text color" } %>
88
- <% button.with_icon do %>
89
- <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="editor-icon-text-color">
90
- <path d="M4.99994 13.3333L9.99994 3.33334L14.9999 13.3333" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
91
- <path d="M6.66656 10H13.3332" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
92
- <rect x="2.25" y="15.25" width="15.5" height="2.5" rx="1.25" fill="currentColor" stroke="#111827" stroke-width="0.5"/>
93
- </svg>
94
- <% end %>
63
+ }
64
+ ) do |button| %>
65
+ <% button.with_picker %>
66
+ <% button.with_text { "Text colour" } %>
67
+ <% button.with_icon do %>
68
+ <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="editor-icon-text-color">
69
+ <path d="M4.99994 13.3333L9.99994 3.33334L14.9999 13.3333" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
70
+ <path d="M6.66656 10H13.3332" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
71
+ <rect x="2.25" y="15.25" width="15.5" height="2.5" rx="1.25" fill="currentColor" stroke="#111827" stroke-width="0.5"/>
72
+ </svg>
95
73
  <% end %>
96
74
  <% end %>
97
75
  <% end %>
76
+ <% end %>
98
77
 
99
- <% toolbar.with_section do |section| %>
100
- <% section.with_button(
101
- icon: :undo_2,
102
- click: "history.undo",
103
- tooltip: "Undo",
104
- theme: :text_secondary,
105
- disabled: true,
106
- x: { "effect": "disabled = !history.undoable" },
107
- data: {
108
- role: "undo"
109
- }
110
- ) %>
78
+ <% toolbar.with_section do |section| %>
79
+ <% section.with_button(
80
+ icon: :undo_2,
81
+ click: "history.undo",
82
+ tooltip: "Undo",
83
+ theme: :text_secondary,
84
+ disabled: true,
85
+ x: { "effect": "disabled = saving || !history.undoable" },
86
+ data: {
87
+ role: "undo"
88
+ }
89
+ ) %>
111
90
 
112
- <% section.with_button(
113
- icon: :redo_2,
114
- click: "history.redo",
115
- tooltip: "Redo",
116
- theme: :text_secondary,
117
- disabled: true,
118
- x: { "effect": "disabled = !history.redoable" },
119
- data: {
120
- role: "redo"
121
- }
122
- ) %>
123
-
124
- <% section.with_button(
125
- theme: :positive,
126
- icon: :check,
127
- collapsible: false,
128
- type: "submit",
129
- disabled: true,
130
- data: {
131
- role: "save"
132
- },
133
- x: {
134
- "@click.stop.prevent": "if(!loading) save()",
135
- "effect": "loading = saving; disabled = !history.undoable",
136
- ":class": "{'editor-saving': saving}"
137
- }
138
- ) do |button| %>
139
- <% button.with_state(:loading, text: "Saving...") %>
140
- Save
141
- <% end %>
91
+ <% section.with_button(
92
+ icon: :redo_2,
93
+ click: "history.redo",
94
+ tooltip: "Redo",
95
+ theme: :text_secondary,
96
+ disabled: true,
97
+ x: { "effect": "disabled = saving || !history.redoable" },
98
+ data: {
99
+ role: "redo"
100
+ }
101
+ ) %>
102
+
103
+ <% section.with_button(
104
+ theme: :positive,
105
+ icon: :check,
106
+ collapsible: false,
107
+ type: "submit",
108
+ disabled: true,
109
+ data: {
110
+ role: "save"
111
+ },
112
+ x: {
113
+ "@click.stop.prevent": "if(!loading) save()",
114
+ "effect": "loading = saving; disabled = !history.undoable",
115
+ ":class": "{'editor-saving': saving}"
116
+ }
117
+ ) do |button| %>
118
+ <% button.with_state(:loading, text: "Saving...") %>
119
+ Save
142
120
  <% end %>
143
- <% end %>
144
- </div>
145
-
146
- <%# Editable slide %>
147
- <div
148
- class="editor-slide"
149
- x-ref="slide"
150
- @dragenter.stop.prevent="dragging = true"
151
- @dragover.stop.prevent="dragging = true"
152
- @dragleave.stop.prevent="dragging = false"
153
- @drop.stop.prevent="handleImageDrop($event)">
154
- <%= coco_editable_slide(
155
- **slide_args,
156
- data: {
157
- role: "slide"
158
- },
159
- x: {
160
- ":style": "slideStyles",
161
- ":class": "slideClasses",
162
- }
163
- ) do |slide| %>
164
- <% if title? %>
165
- <% slide.with_title do %>
166
- <%= coco_seamless_textarea(
167
- name: title_name,
168
- multiline: false,
169
- form_builder: f,
170
- **title_options,
171
- wrapper: {
172
- data: {
173
- role: "title-editor"
174
- },
175
- x: {
176
- modelable: "value",
177
- model: "title"
178
- }
179
- }
180
- ) %>
181
- <% end %>
121
+ <% end %>
122
+ <% end %>
123
+ </div>
124
+
125
+ <%# Editable slide %>
126
+ <div
127
+ class="editor-slide"
128
+ x-ref="slide"
129
+ @dragenter.stop.prevent="dragging = true"
130
+ @dragover.stop.prevent="dragging = true"
131
+ @dragleave.stop.prevent="dragging = false"
132
+ @drop.stop.prevent="handleImageDrop($event)">
133
+ <%= coco_editable_slide(
134
+ **slide_args,
135
+ data: {
136
+ role: "slide"
137
+ },
138
+ x: {
139
+ ":style": "slideStyles",
140
+ ":class": "slideClasses",
141
+ }
142
+ ) do |slide| %>
143
+ <% if title? %>
144
+ <% slide.with_title do %>
145
+ <%= title %>
182
146
  <% end %>
147
+ <% end %>
183
148
 
184
- <% if text_1? %>
185
- <% slide.with_text_1 do %>
186
- <%= coco_seamless_textarea(
187
- name: text_1_name,
188
- multiline: true,
189
- form_builder: f,
190
- **text_1_options,
191
- wrapper: {
192
- data: {
193
- role: "text-1-editor"
194
- },
195
- x: {
196
- modelable: "value",
197
- model: "text1"
198
- }
199
- }
200
- ) %>
201
- <% end %>
149
+ <% if text_1? %>
150
+ <% slide.with_text_1 do %>
151
+ <%= text_1 %>
202
152
  <% end %>
203
153
  <% end %>
204
- </div>
205
-
206
- <% if content %>
207
- <%# Any additional fields required %>
208
- <div class="editor-additional-content">
209
- <%= content %>
210
- </div>
211
154
  <% end %>
155
+ </div>
212
156
 
157
+ <% if content %>
158
+ <div
159
+ class="editor-form"
160
+ x-ref="form"
161
+ @turbo:submit-end="submitSuccess"
162
+ @turbo:fetch-request-error="submitError"
163
+ @direct-upload:error="directUploadError"
164
+ @direct-uploads:end="save">
165
+ <%= content %>
166
+ </div>
213
167
  <% end %>
214
-
215
- <% if thumbnail? %>
168
+
169
+ <% if false && thumbnail? %>
216
170
  <%# Slide used for thumbnail generation %>
217
171
  <div class="editor-screenshot-wrapper">
218
172
  <div x-ref="screenshot" class="editor-screenshot">
@@ -232,4 +186,5 @@
232
186
  </div>
233
187
  </div>
234
188
  <% end %>
189
+
235
190
  <% end %>
@@ -13,15 +13,30 @@ const thumbnailOpts = {
13
13
  };
14
14
 
15
15
  export default CocoComponent("appSlideEditor", (data) => {
16
+ const initialData = {
17
+ layout: data.layout,
18
+ title: data.title,
19
+ text1: data.text1,
20
+ bgColor: data.bgColor,
21
+ textColor: data.textColor,
22
+ bgImage: {
23
+ name: data.bgImage,
24
+ data: data.bgImage,
25
+ },
26
+ };
27
+
16
28
  return {
17
29
  use: [withUndo()],
18
30
 
19
- ...data,
20
- saved: { ...data },
31
+ ...initialData,
32
+
33
+ saved: { ...initialData },
34
+
21
35
  saving: false,
22
36
  ready: false,
23
37
  dragging: false,
24
38
  errors: [],
39
+
25
40
  thumbnailFile: null,
26
41
 
27
42
  get bgImagePicker() {
@@ -29,13 +44,6 @@ export default CocoComponent("appSlideEditor", (data) => {
29
44
  },
30
45
 
31
46
  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
47
  // Update thumbnail field when new thumbnail is generated
40
48
  this.$watch("thumbnailFile", () => this._syncThumbnailField());
41
49
 
@@ -43,7 +51,16 @@ export default CocoComponent("appSlideEditor", (data) => {
43
51
  errors.forEach((error) => console.error(error.message)); // TODO display errors properly!
44
52
  });
45
53
 
46
- this.$nextTick(() => (this.ready = true));
54
+ this.$nextTick(() => {
55
+ // Add property changes to the undo/redo history
56
+ this._fields.forEach((name) => {
57
+ this.$watch(name, (value, oldValue) =>
58
+ this.history.add(name, value, oldValue)
59
+ );
60
+ });
61
+
62
+ this.ready = true;
63
+ });
47
64
  },
48
65
 
49
66
  undo(name, value) {
@@ -79,7 +96,11 @@ export default CocoComponent("appSlideEditor", (data) => {
79
96
  }
80
97
  }
81
98
 
82
- this.$refs.form.requestSubmit();
99
+ if (this.$refs.form) {
100
+ this.$refs.form.querySelector("form").requestSubmit();
101
+ } else {
102
+ this.submitSuccess();
103
+ }
83
104
  },
84
105
 
85
106
  submitEnd(event) {
@@ -106,12 +127,43 @@ export default CocoComponent("appSlideEditor", (data) => {
106
127
  this._handleSaveError($event.detail.error, { event: $event });
107
128
  },
108
129
 
130
+ syncImageField(el, image) {
131
+ const dataTransfer = new DataTransfer();
132
+ if (image.file && image.file instanceof File) {
133
+ dataTransfer.items.add(image.file);
134
+ }
135
+ el.files = dataTransfer.files;
136
+ },
137
+
138
+ input: {
139
+ layout: { "x-model.fill": "layout" },
140
+ title: { "x-model.fill": "title" },
141
+ text1: { "x-model.fill": "text1" },
142
+ bgColor: { "x-model.fill": "bgColorHex" },
143
+ textColor: { "x-model.fill": "textColorHex" },
144
+ bgImage: { "x-effect": "syncImageField($el, bgImage)" },
145
+ bgImagePurge: { ":checked": "!hasBgImage" },
146
+ },
147
+
148
+ get hasBgImage() {
149
+ return !!(this.bgImage && this.bgImage.data);
150
+ },
151
+
152
+ get bgColorHex() {
153
+ return this.bgColor.replace("#", "");
154
+ },
155
+
156
+ get textColorHex() {
157
+ return this.textColor.replace("#", "");
158
+ },
159
+
109
160
  get slideStyles() {
110
161
  return {
111
162
  backgroundColor: this.bgColor,
112
163
  color: this.textColor,
113
- backgroundImage:
114
- this.bgImage && this.bgImage.data && `url('${this.bgImage.data}')`,
164
+ backgroundImage: this.hasBgImage
165
+ ? `url('${this.bgImage.data}')`
166
+ : "none",
115
167
  };
116
168
  },
117
169
 
@@ -123,9 +175,7 @@ export default CocoComponent("appSlideEditor", (data) => {
123
175
  },
124
176
 
125
177
  get isDarkBg() {
126
- return (this.bgImage && this.bgImage.data) || this.bgColor
127
- ? isDark(this.bgColor)
128
- : false;
178
+ return this.hasBgImage || this.bgColor ? isDark(this.bgColor) : false;
129
179
  },
130
180
 
131
181
  get shouldGenerateThumbnail() {
@@ -133,7 +183,7 @@ export default CocoComponent("appSlideEditor", (data) => {
133
183
  },
134
184
 
135
185
  get _fields() {
136
- return Object.keys(data);
186
+ return Object.keys(initialData);
137
187
  },
138
188
 
139
189
  async _generateThumbnail() {
@@ -170,6 +220,9 @@ export default CocoComponent("appSlideEditor", (data) => {
170
220
 
171
221
  _handleSaveError(message = "Error saving slide", context = {}) {
172
222
  this.errors.push({ message, context });
223
+ this.errors.forEach((err) => {
224
+ console.log(err.message);
225
+ });
173
226
  this.saving = false;
174
227
 
175
228
  this.$dispatch("slide:save-end", { success: false });