coveragebook_components 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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 });