headmin 0.5.1 → 0.5.4
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/CHANGELOG.md +2 -2
- data/Gemfile +14 -0
- data/Gemfile.lock +79 -2
- data/app/assets/javascripts/headmin/controllers/media_controller.js +237 -0
- data/app/assets/javascripts/headmin/controllers/media_modal_controller.js +110 -0
- data/app/assets/javascripts/headmin/controllers/remote_modal_controller.js +9 -0
- data/app/assets/javascripts/headmin/controllers/textarea_controller.js +34 -0
- data/app/assets/javascripts/headmin/index.js +8 -0
- data/app/assets/javascripts/headmin.js +294 -0
- data/app/assets/stylesheets/headmin/forms/file.scss +40 -5
- data/app/assets/stylesheets/headmin/forms/media.scss +10 -0
- data/app/assets/stylesheets/headmin/forms.scss +1 -0
- data/app/assets/stylesheets/headmin/layout/sidebar.scss +0 -1
- data/app/assets/stylesheets/headmin/media/index.scss +9 -0
- data/app/assets/stylesheets/headmin/media.scss +1 -0
- data/app/assets/stylesheets/headmin/overrides/redactorx.scss +1 -1
- data/app/assets/stylesheets/headmin/table.scss +8 -0
- data/app/assets/stylesheets/headmin/vendor/{tom-select-bootstrap.css → tom-select-bootstrap.scss} +0 -1
- data/app/assets/stylesheets/headmin.css +61 -8
- data/app/assets/stylesheets/headmin.scss +1 -0
- data/app/controllers/headmin/media_controller.rb +52 -0
- data/app/controllers/headmin_controller.rb +2 -0
- data/app/helpers/headmin/form_helper.rb +2 -2
- data/app/models/concerns/headmin/field.rb +2 -2
- data/app/models/concerns/headmin/fieldable.rb +19 -10
- data/app/models/concerns/headmin/form/hintable.rb +6 -1
- data/app/models/headmin/filter/association.rb +86 -0
- data/app/models/headmin/filter/association_view.rb +74 -0
- data/app/models/headmin/filter/base.rb +5 -2
- data/app/models/headmin/filter/boolean_view.rb +1 -0
- data/app/models/headmin/filter/date.rb +49 -1
- data/app/models/headmin/filter/date_view.rb +1 -0
- data/app/models/headmin/filter/flatpickr_view.rb +1 -0
- data/app/models/headmin/filter/number_view.rb +1 -0
- data/app/models/headmin/filter/operator_view.rb +3 -1
- data/app/models/headmin/filter/options_view.rb +1 -0
- data/app/models/headmin/filter/text_view.rb +1 -0
- data/app/models/headmin/form/association_view.rb +102 -0
- data/app/models/headmin/form/blocks_view.rb +4 -1
- data/app/models/headmin/form/file_view.rb +0 -8
- data/app/models/headmin/form/flatpickr_view.rb +2 -1
- data/app/models/headmin/form/media_item_view.rb +39 -0
- data/app/models/headmin/form/media_view.rb +137 -0
- data/app/models/headmin/form/select_view.rb +2 -1
- data/app/models/headmin/form/textarea_view.rb +6 -1
- data/app/models/headmin/thumbnail_view.rb +40 -19
- data/app/models/view_model.rb +4 -0
- data/app/views/examples/admin.html.erb +8 -8
- data/app/views/examples/auth.html.erb +2 -2
- data/app/views/headmin/_breadcrumbs.html.erb +2 -2
- data/app/views/headmin/_dropdown.html.erb +1 -1
- data/app/views/headmin/_filters.html.erb +12 -7
- data/app/views/headmin/_pagination.html.erb +2 -2
- data/app/views/headmin/_popup.html.erb +4 -4
- data/app/views/headmin/_table.html.erb +1 -1
- data/app/views/headmin/_thumbnail.html.erb +33 -9
- data/app/views/headmin/dropdown/_button.html.erb +2 -2
- data/app/views/headmin/dropdown/_item.html.erb +2 -2
- data/app/views/headmin/dropdown/_list.html.erb +3 -3
- data/app/views/headmin/dropdown/_locale.html.erb +5 -5
- data/app/views/headmin/filters/_association.html.erb +24 -0
- data/app/views/headmin/filters/_options.html.erb +1 -1
- data/app/views/headmin/filters/filter/_button.html.erb +2 -2
- data/app/views/headmin/filters/filter/_null_select.html.erb +2 -2
- data/app/views/headmin/forms/_association.html.erb +30 -0
- data/app/views/headmin/forms/_errors.html.erb +1 -1
- data/app/views/headmin/forms/_file.html.erb +10 -11
- data/app/views/headmin/forms/_hint.html.erb +6 -1
- data/app/views/headmin/forms/_media.html.erb +60 -0
- data/app/views/headmin/forms/_repeater.html.erb +18 -16
- data/app/views/headmin/forms/_textarea.html.erb +1 -1
- data/app/views/headmin/forms/_wrapper.html.erb +0 -1
- data/app/views/headmin/forms/fields/_list.html.erb +6 -4
- data/app/views/headmin/forms/media/_item.html.erb +38 -0
- data/app/views/headmin/forms/media/_validation.html.erb +10 -0
- data/app/views/headmin/forms/repeater/_row.html.erb +17 -15
- data/app/views/headmin/heading/_title.html.erb +2 -2
- data/app/views/headmin/layout/_main.html.erb +2 -0
- data/app/views/headmin/layout/_remote_modal.html.erb +1 -0
- data/app/views/headmin/layout/_sidebar.html.erb +1 -1
- data/app/views/headmin/media/_item.html.erb +16 -0
- data/app/views/headmin/media/_media_item_modal.html.erb +51 -0
- data/app/views/headmin/media/_modal.html.erb +35 -0
- data/app/views/headmin/media/create.turbo_stream.erb +5 -0
- data/app/views/headmin/media/index.html.erb +3 -0
- data/app/views/headmin/media/show.html.erb +9 -0
- data/app/views/headmin/media/update.turbo_stream.erb +3 -0
- data/app/views/headmin/nav/_dropdown.html.erb +7 -7
- data/app/views/headmin/nav/_item.html.erb +5 -5
- data/app/views/headmin/nav/item/_locale.html.erb +6 -6
- data/app/views/headmin/pagination/_per_page.html.erb +7 -7
- data/app/views/headmin/pagination/kaminari/_first_page.html.erb +2 -2
- data/app/views/headmin/pagination/kaminari/_gap.html.erb +1 -1
- data/app/views/headmin/pagination/kaminari/_last_page.html.erb +2 -2
- data/app/views/headmin/pagination/kaminari/_next_page.html.erb +3 -3
- data/app/views/headmin/pagination/kaminari/_page.html.erb +2 -2
- data/app/views/headmin/pagination/kaminari/_paginator.html.erb +1 -1
- data/app/views/headmin/pagination/kaminari/_prev_page.html.erb +2 -2
- data/app/views/headmin/table/_actions.html.erb +9 -9
- data/app/views/headmin/table/_body.html.erb +1 -1
- data/app/views/headmin/table/actions/_action.html.erb +4 -4
- data/app/views/headmin/table/actions/_export.html.erb +1 -1
- data/app/views/headmin/table/body/_association.html.erb +17 -3
- data/app/views/headmin/table/body/_boolean.erb +4 -4
- data/app/views/headmin/table/body/_date.html.erb +2 -2
- data/app/views/headmin/table/body/_image.html.erb +18 -0
- data/app/views/headmin/table/body/_string.html.erb +1 -1
- data/app/views/headmin/table/head/_cell.html.erb +1 -1
- data/app/views/headmin/table/head/cell/_asc.html.erb +2 -2
- data/app/views/headmin/table/head/cell/_default.html.erb +1 -1
- data/app/views/headmin/table/head/cell/_desc.html.erb +1 -1
- data/app/views/headmin/views/devise/confirmations/_new.html.erb +1 -1
- data/app/views/headmin/views/devise/passwords/_edit.html.erb +1 -1
- data/app/views/headmin/views/devise/passwords/_new.html.erb +1 -1
- data/app/views/headmin/views/devise/registrations/_edit.html.erb +5 -5
- data/app/views/headmin/views/devise/registrations/_new.html.erb +1 -1
- data/app/views/headmin/views/devise/sessions/_new.html.erb +1 -1
- data/app/views/headmin/views/devise/shared/_links.html.erb +14 -20
- data/app/views/headmin/views/devise/unlocks/_new.html.erb +1 -1
- data/config/locales/activerecord/en.yml +1 -0
- data/config/locales/activerecord/nl.yml +1 -0
- data/config/locales/devise/nl.yml +1 -1
- data/config/locales/headmin/filters/en.yml +3 -1
- data/config/locales/headmin/filters/nl.yml +2 -0
- data/config/locales/headmin/forms/en.yml +8 -0
- data/config/locales/headmin/forms/nl.yml +8 -0
- data/config/locales/headmin/media/en.yml +23 -0
- data/config/locales/headmin/media/nl.yml +23 -0
- data/config/locales/headmin/table/en.yml +2 -0
- data/config/locales/headmin/table/nl.yml +2 -0
- data/config/routes.rb +10 -0
- data/lib/generators/templates/views/layouts/auth.html.erb +2 -2
- data/lib/headmin/version.rb +1 -1
- data/package.json +1 -1
- metadata +34 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c163e16eb068c93256e3a73a0ac3b58f569cc72c36da590f084ecb13873b8866
|
4
|
+
data.tar.gz: 9297b7bfd0194980aa843c45e59f9180349bc17e571cfe8f9c75678738de7def
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b72bc6a71c864837e78619f8bfc7b0d455d4f57d108790f6d6619fbb3fe2acb882d2be87236b8a2d87204b55995fd4234c8617ee93be5a587b6eafa2616aaac4
|
7
|
+
data.tar.gz: 819ec0caa24dbfa82ac02dec7909d6c888550f87c71bf5eadae0b86b03a179c57b599c5a3af3b418e4435dfc569373a1b744ed5621380382bdf7cff4cb6d229d
|
data/CHANGELOG.md
CHANGED
@@ -32,10 +32,10 @@
|
|
32
32
|
- BREAK: filter concern (controllers) has been renamed to filterable
|
33
33
|
```erb
|
34
34
|
# Old
|
35
|
-
<%= render
|
35
|
+
<%= render 'headmin/forms/date_range', form: form, start_attribute: :start_date, end_attribute: :end_date %>
|
36
36
|
|
37
37
|
# New
|
38
|
-
<%= render
|
38
|
+
<%= render 'headmin/forms/date_range', form: form, start: {attribute: :start_date}, end: {attribute: :end_date} %>
|
39
39
|
```
|
40
40
|
|
41
41
|
## 0.3
|
data/Gemfile
CHANGED
@@ -6,9 +6,23 @@ gemspec
|
|
6
6
|
gem "capybara", ">= 3.26"
|
7
7
|
gem "minitest", "~> 5.0"
|
8
8
|
gem "minitest-spec-rails", "~> 6.1"
|
9
|
+
gem "puma", "~> 5.2"
|
9
10
|
gem "rails", "~> 7.0"
|
10
11
|
gem "rake", "~> 13.0"
|
11
12
|
gem "sprockets-rails", "~> 3.4"
|
12
13
|
gem "sqlite3", "~> 1.4"
|
13
14
|
gem "standard", "~> 1.7"
|
14
15
|
gem "debug"
|
16
|
+
|
17
|
+
# Dummy app
|
18
|
+
gem "devise", "~> 4.8"
|
19
|
+
gem "enumerize", "~> 2.3"
|
20
|
+
gem "sassc-rails", "~> 2.1"
|
21
|
+
gem "image_processing", "~> 1.2"
|
22
|
+
gem "inline_svg", "~> 1.7"
|
23
|
+
gem "kaminari", "~> 1.2"
|
24
|
+
gem "route_translator", "~> 12.0"
|
25
|
+
gem "acts_as_list", "~> 1.0"
|
26
|
+
gem "breadcrumbs_on_rails", "~> 4.1"
|
27
|
+
gem "importmap-rails", "~> 1.0"
|
28
|
+
gem "hotwire-rails", "~> 0.1"
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
headmin (0.5.
|
4
|
+
headmin (0.5.3)
|
5
5
|
closure_tree (~> 7.4)
|
6
6
|
inline_svg (~> 1.7)
|
7
7
|
redcarpet (~> 3.5)
|
@@ -75,9 +75,14 @@ GEM
|
|
75
75
|
i18n (>= 1.6, < 2)
|
76
76
|
minitest (>= 5.1)
|
77
77
|
tzinfo (~> 2.0)
|
78
|
+
acts_as_list (1.0.4)
|
79
|
+
activerecord (>= 4.2)
|
78
80
|
addressable (2.8.0)
|
79
81
|
public_suffix (>= 2.0.2, < 5.0)
|
80
82
|
ast (2.4.2)
|
83
|
+
bcrypt (3.1.18)
|
84
|
+
breadcrumbs_on_rails (4.1.0)
|
85
|
+
railties (>= 5.0)
|
81
86
|
builder (3.2.4)
|
82
87
|
capybara (3.36.0)
|
83
88
|
addressable
|
@@ -96,18 +101,49 @@ GEM
|
|
96
101
|
debug (1.5.0)
|
97
102
|
irb (>= 1.3.6)
|
98
103
|
reline (>= 0.2.7)
|
104
|
+
devise (4.8.1)
|
105
|
+
bcrypt (~> 3.0)
|
106
|
+
orm_adapter (~> 0.1)
|
107
|
+
railties (>= 4.1.0)
|
108
|
+
responders
|
109
|
+
warden (~> 1.2.3)
|
99
110
|
digest (3.1.0)
|
111
|
+
enumerize (2.5.0)
|
112
|
+
activesupport (>= 3.2)
|
100
113
|
erubi (1.10.0)
|
114
|
+
ffi (1.15.5)
|
101
115
|
globalid (1.0.0)
|
102
116
|
activesupport (>= 5.0)
|
117
|
+
hotwire-rails (0.1.3)
|
118
|
+
rails (>= 6.0.0)
|
119
|
+
stimulus-rails
|
120
|
+
turbo-rails
|
103
121
|
i18n (1.10.0)
|
104
122
|
concurrent-ruby (~> 1.0)
|
123
|
+
image_processing (1.12.2)
|
124
|
+
mini_magick (>= 4.9.5, < 5)
|
125
|
+
ruby-vips (>= 2.0.17, < 3)
|
126
|
+
importmap-rails (1.0.3)
|
127
|
+
actionpack (>= 6.0.0)
|
128
|
+
railties (>= 6.0.0)
|
105
129
|
inline_svg (1.8.0)
|
106
130
|
activesupport (>= 3.0)
|
107
131
|
nokogiri (>= 1.6)
|
108
132
|
io-console (0.5.11)
|
109
133
|
irb (1.4.1)
|
110
134
|
reline (>= 0.3.0)
|
135
|
+
kaminari (1.2.2)
|
136
|
+
activesupport (>= 4.1.0)
|
137
|
+
kaminari-actionview (= 1.2.2)
|
138
|
+
kaminari-activerecord (= 1.2.2)
|
139
|
+
kaminari-core (= 1.2.2)
|
140
|
+
kaminari-actionview (1.2.2)
|
141
|
+
actionview
|
142
|
+
kaminari-core (= 1.2.2)
|
143
|
+
kaminari-activerecord (1.2.2)
|
144
|
+
activerecord
|
145
|
+
kaminari-core (= 1.2.2)
|
146
|
+
kaminari-core (1.2.2)
|
111
147
|
loofah (2.16.0)
|
112
148
|
crass (~> 1.0.2)
|
113
149
|
nokogiri (>= 1.5.9)
|
@@ -116,6 +152,7 @@ GEM
|
|
116
152
|
marcel (1.0.2)
|
117
153
|
matrix (0.4.2)
|
118
154
|
method_source (1.0.0)
|
155
|
+
mini_magick (4.11.0)
|
119
156
|
mini_mime (1.1.2)
|
120
157
|
minitest (5.15.0)
|
121
158
|
minitest-spec-rails (6.1.0)
|
@@ -142,10 +179,13 @@ GEM
|
|
142
179
|
racc (~> 1.4)
|
143
180
|
nokogiri (1.13.4-x86_64-linux)
|
144
181
|
racc (~> 1.4)
|
182
|
+
orm_adapter (0.5.0)
|
145
183
|
parallel (1.22.1)
|
146
184
|
parser (3.1.2.0)
|
147
185
|
ast (~> 2.4.1)
|
148
186
|
public_suffix (4.0.7)
|
187
|
+
puma (5.6.4)
|
188
|
+
nio4r (~> 2.0)
|
149
189
|
racc (1.6.0)
|
150
190
|
rack (2.2.3)
|
151
191
|
rack-test (1.1.0)
|
@@ -182,8 +222,15 @@ GEM
|
|
182
222
|
regexp_parser (2.3.1)
|
183
223
|
reline (0.3.1)
|
184
224
|
io-console (~> 0.5)
|
225
|
+
responders (3.0.1)
|
226
|
+
actionpack (>= 5.0)
|
227
|
+
railties (>= 5.0)
|
185
228
|
rexml (3.2.5)
|
186
|
-
rouge (3.
|
229
|
+
rouge (3.29.0)
|
230
|
+
route_translator (12.1.0)
|
231
|
+
actionpack (>= 5.2, < 7.1)
|
232
|
+
activesupport (>= 5.2, < 7.1)
|
233
|
+
addressable (~> 2.7)
|
187
234
|
rubocop (1.28.2)
|
188
235
|
parallel (~> 1.10)
|
189
236
|
parser (>= 3.1.0.0)
|
@@ -199,6 +246,16 @@ GEM
|
|
199
246
|
rubocop (>= 1.7.0, < 2.0)
|
200
247
|
rubocop-ast (>= 0.4.0)
|
201
248
|
ruby-progressbar (1.11.0)
|
249
|
+
ruby-vips (2.1.4)
|
250
|
+
ffi (~> 1.12)
|
251
|
+
sassc (2.4.0)
|
252
|
+
ffi (~> 1.9)
|
253
|
+
sassc-rails (2.1.2)
|
254
|
+
railties (>= 4.0.0)
|
255
|
+
sassc (>= 2.0)
|
256
|
+
sprockets (> 3.0)
|
257
|
+
sprockets-rails
|
258
|
+
tilt
|
202
259
|
sprockets (4.0.3)
|
203
260
|
concurrent-ruby (~> 1.0)
|
204
261
|
rack (> 1, < 3)
|
@@ -210,12 +267,20 @@ GEM
|
|
210
267
|
standard (1.11.0)
|
211
268
|
rubocop (= 1.28.2)
|
212
269
|
rubocop-performance (= 1.13.3)
|
270
|
+
stimulus-rails (1.0.4)
|
271
|
+
railties (>= 6.0.0)
|
213
272
|
strscan (3.0.1)
|
214
273
|
thor (1.2.1)
|
274
|
+
tilt (2.0.10)
|
215
275
|
timeout (0.2.0)
|
276
|
+
turbo-rails (1.0.1)
|
277
|
+
actionpack (>= 6.0.0)
|
278
|
+
railties (>= 6.0.0)
|
216
279
|
tzinfo (2.0.4)
|
217
280
|
concurrent-ruby (~> 1.0)
|
218
281
|
unicode-display_width (2.1.0)
|
282
|
+
warden (1.2.9)
|
283
|
+
rack (>= 2.0.9)
|
219
284
|
websocket-driver (0.7.5)
|
220
285
|
websocket-extensions (>= 0.1.0)
|
221
286
|
websocket-extensions (0.1.5)
|
@@ -231,13 +296,25 @@ PLATFORMS
|
|
231
296
|
x86_64-linux
|
232
297
|
|
233
298
|
DEPENDENCIES
|
299
|
+
acts_as_list (~> 1.0)
|
300
|
+
breadcrumbs_on_rails (~> 4.1)
|
234
301
|
capybara (>= 3.26)
|
235
302
|
debug
|
303
|
+
devise (~> 4.8)
|
304
|
+
enumerize (~> 2.3)
|
236
305
|
headmin!
|
306
|
+
hotwire-rails (~> 0.1)
|
307
|
+
image_processing (~> 1.2)
|
308
|
+
importmap-rails (~> 1.0)
|
309
|
+
inline_svg (~> 1.7)
|
310
|
+
kaminari (~> 1.2)
|
237
311
|
minitest (~> 5.0)
|
238
312
|
minitest-spec-rails (~> 6.1)
|
313
|
+
puma (~> 5.2)
|
239
314
|
rails (~> 7.0)
|
240
315
|
rake (~> 13.0)
|
316
|
+
route_translator (~> 12.0)
|
317
|
+
sassc-rails (~> 2.1)
|
241
318
|
sprockets-rails (~> 3.4)
|
242
319
|
sqlite3 (~> 1.4)
|
243
320
|
standard (~> 1.7)
|
@@ -0,0 +1,237 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
import Sortable from 'sortablejs'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static get targets () {
|
6
|
+
return ['item', 'template', 'thumbnails', 'modalButton', 'placeholder', 'validationInput', 'count', 'editButton']
|
7
|
+
}
|
8
|
+
|
9
|
+
connect () {
|
10
|
+
document.addEventListener('mediaSelectionSubmitted', (event) => {
|
11
|
+
if (event.detail.name === this.element.dataset.name) {
|
12
|
+
this.selectItems(event.detail.items)
|
13
|
+
}
|
14
|
+
})
|
15
|
+
|
16
|
+
// Init sorting
|
17
|
+
if (this.hasSorting()) {
|
18
|
+
this.initSortable()
|
19
|
+
}
|
20
|
+
|
21
|
+
this.validate()
|
22
|
+
}
|
23
|
+
|
24
|
+
// Actions
|
25
|
+
destroy (event) {
|
26
|
+
const item = event.currentTarget.closest('[data-media-target=\'item\']')
|
27
|
+
this.destroyItem(item)
|
28
|
+
}
|
29
|
+
|
30
|
+
syncIds () {
|
31
|
+
const ids = this.activeIds()
|
32
|
+
|
33
|
+
this.modalButtonTargets.forEach((button) => {
|
34
|
+
const url = new URL(button.getAttribute('href'))
|
35
|
+
|
36
|
+
// Remove all ids[]
|
37
|
+
url.searchParams.delete('ids[]')
|
38
|
+
|
39
|
+
// Add new ids
|
40
|
+
ids.forEach((id) => {
|
41
|
+
url.searchParams.append('ids[]', id)
|
42
|
+
})
|
43
|
+
|
44
|
+
button.setAttribute('href', url.toString())
|
45
|
+
})
|
46
|
+
}
|
47
|
+
|
48
|
+
// Methods
|
49
|
+
initSortable () {
|
50
|
+
Sortable.create(this.thumbnailsTarget, {
|
51
|
+
handle: '.media-drag-sort-handle',
|
52
|
+
onEnd: (event) => {
|
53
|
+
this.resetPositions()
|
54
|
+
}
|
55
|
+
})
|
56
|
+
}
|
57
|
+
|
58
|
+
hasSorting () {
|
59
|
+
return this.element.dataset.sort === 'true'
|
60
|
+
}
|
61
|
+
|
62
|
+
destroyItem (item) {
|
63
|
+
this.removeItem(item)
|
64
|
+
this.postProcess()
|
65
|
+
}
|
66
|
+
|
67
|
+
selectItems (items) {
|
68
|
+
// Destroy all deselected items
|
69
|
+
this.removeAllItems()
|
70
|
+
|
71
|
+
// Add all selected items
|
72
|
+
this.addItems(items)
|
73
|
+
|
74
|
+
this.postProcess()
|
75
|
+
}
|
76
|
+
|
77
|
+
postProcess () {
|
78
|
+
// Reset positions
|
79
|
+
this.resetPositions()
|
80
|
+
|
81
|
+
// Sync Ids
|
82
|
+
this.syncIds()
|
83
|
+
|
84
|
+
// Toggle placeholder
|
85
|
+
this.togglePlaceholder()
|
86
|
+
|
87
|
+
// Validate
|
88
|
+
this.validate()
|
89
|
+
}
|
90
|
+
|
91
|
+
validate () {
|
92
|
+
this.clearValidation()
|
93
|
+
if (this.element.dataset.required === '0') return
|
94
|
+
this.validateMinimum()
|
95
|
+
this.validateMaximum()
|
96
|
+
}
|
97
|
+
|
98
|
+
clearValidation () {
|
99
|
+
this.validationInputTarget.setCustomValidity('')
|
100
|
+
}
|
101
|
+
|
102
|
+
validateMinimum () {
|
103
|
+
const count = this.activeItems().length
|
104
|
+
if (count < this.minActiveItems()) {
|
105
|
+
this.validationInputTarget.setCustomValidity(this.validationInputTarget.dataset.minMessage)
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
validateMaximum () {
|
110
|
+
const count = this.activeItems().length
|
111
|
+
if (count > this.maxActiveItems()) {
|
112
|
+
this.validationInputTarget.setCustomValidity(this.validationInputTarget.dataset.maxMessage)
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
minActiveItems () {
|
117
|
+
return parseInt(this.element.dataset.min, 10) || 0
|
118
|
+
}
|
119
|
+
|
120
|
+
maxActiveItems () {
|
121
|
+
return parseInt(this.element.dataset.max, 10) || Infinity
|
122
|
+
}
|
123
|
+
|
124
|
+
resetPositions () {
|
125
|
+
this.activeItems().forEach((item, index) => {
|
126
|
+
const positionInput = item.querySelector('input[name*=\'position\']')
|
127
|
+
if (positionInput) {
|
128
|
+
positionInput.value = index
|
129
|
+
}
|
130
|
+
})
|
131
|
+
}
|
132
|
+
|
133
|
+
addItems (items) {
|
134
|
+
items.forEach((item) => this.addItem(item))
|
135
|
+
}
|
136
|
+
|
137
|
+
addItem (item) {
|
138
|
+
const currentItem = this.itemByBlobId(item.blobId)
|
139
|
+
if (currentItem) {
|
140
|
+
// (re-)enable if already exists
|
141
|
+
this.enableItem(currentItem)
|
142
|
+
} else {
|
143
|
+
// Create if item doesn't exist yet
|
144
|
+
this.createItem(item)
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
togglePlaceholder () {
|
149
|
+
if (this.activeItems().length > 0) {
|
150
|
+
this.hidePlaceholder()
|
151
|
+
} else {
|
152
|
+
this.showPlaceholder()
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
showPlaceholder () {
|
157
|
+
this.placeholderTarget.classList.remove('d-none')
|
158
|
+
}
|
159
|
+
|
160
|
+
hidePlaceholder () {
|
161
|
+
this.placeholderTarget.classList.add('d-none')
|
162
|
+
}
|
163
|
+
|
164
|
+
enableItem (item) {
|
165
|
+
item.querySelector('input[name*=\'_destroy\']').value = false
|
166
|
+
item.classList.remove('d-none')
|
167
|
+
}
|
168
|
+
|
169
|
+
createItem (item) {
|
170
|
+
// Copy template
|
171
|
+
const template = this.templateTarget
|
172
|
+
const html = this.randomizeIds(template)
|
173
|
+
this.thumbnailsTarget.insertAdjacentHTML('beforeend', html)
|
174
|
+
|
175
|
+
// Set new values
|
176
|
+
const newItem = this.itemTargets.pop()
|
177
|
+
newItem.querySelector('input[name*="[blob_id]"]').value = item.blobId
|
178
|
+
newItem.querySelector('input[name*="[_destroy]"]').value = false
|
179
|
+
|
180
|
+
// Update edit button url
|
181
|
+
const editButton = newItem.querySelector('[data-media-target="editButton"]')
|
182
|
+
editButton.setAttribute('href', editButton.getAttribute('href').replace('$1', item.blobId))
|
183
|
+
|
184
|
+
// Copy thumbnail
|
185
|
+
const oldThumbnail = newItem.querySelector('.h-thumbnail')
|
186
|
+
const newThumbnail = item.thumbnail.cloneNode(true)
|
187
|
+
oldThumbnail.parentNode.replaceChild(newThumbnail, oldThumbnail)
|
188
|
+
}
|
189
|
+
|
190
|
+
randomizeIds (template) {
|
191
|
+
const regex = new RegExp(template.dataset.templateIdRegex, 'g')
|
192
|
+
const randomNumber = Math.floor(100000000 + Math.random() * 900000000)
|
193
|
+
return template.innerHTML.replace(regex, randomNumber)
|
194
|
+
}
|
195
|
+
|
196
|
+
removeAllItems () {
|
197
|
+
this.removeItems(this.itemTargets)
|
198
|
+
}
|
199
|
+
|
200
|
+
removeItems (items) {
|
201
|
+
items.forEach((item) => {
|
202
|
+
this.removeItem(item)
|
203
|
+
})
|
204
|
+
}
|
205
|
+
|
206
|
+
removeItem (item) {
|
207
|
+
item.querySelector('input[name*=\'_destroy\']').value = 1
|
208
|
+
item.classList.add('d-none')
|
209
|
+
|
210
|
+
// Reset positions
|
211
|
+
this.resetPositions()
|
212
|
+
|
213
|
+
// Sync ids
|
214
|
+
this.syncIds()
|
215
|
+
|
216
|
+
// Toggle placeholder
|
217
|
+
this.togglePlaceholder()
|
218
|
+
}
|
219
|
+
|
220
|
+
itemByBlobId (blobId) {
|
221
|
+
return this.itemTargets.find((item) => {
|
222
|
+
return item.querySelector('input[name*=\'blob_id\']').value === blobId
|
223
|
+
})
|
224
|
+
}
|
225
|
+
|
226
|
+
activeItems () {
|
227
|
+
return this.itemTargets.filter((item) => {
|
228
|
+
return item.querySelector('input[name$=\'[_destroy]\']').value === 'false'
|
229
|
+
})
|
230
|
+
}
|
231
|
+
|
232
|
+
activeIds () {
|
233
|
+
return this.activeItems().map((item) => {
|
234
|
+
return item.querySelector('input[name$=\'[blob_id]\']').value
|
235
|
+
})
|
236
|
+
}
|
237
|
+
}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
/* global CustomEvent */
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static get targets () {
|
6
|
+
return ['idCheckbox', 'item', 'form', 'selectButton', 'placeholder', 'count']
|
7
|
+
}
|
8
|
+
|
9
|
+
connect () {
|
10
|
+
this.validate()
|
11
|
+
this.updateCount()
|
12
|
+
}
|
13
|
+
|
14
|
+
// Actions
|
15
|
+
select () {
|
16
|
+
this.dispatchSelectionEvent()
|
17
|
+
}
|
18
|
+
|
19
|
+
submitForm () {
|
20
|
+
this.hidePlaceholder()
|
21
|
+
this.triggerFormSubmission()
|
22
|
+
}
|
23
|
+
|
24
|
+
inputChange () {
|
25
|
+
this.handleInputChange()
|
26
|
+
this.updateCount()
|
27
|
+
}
|
28
|
+
|
29
|
+
// Methods
|
30
|
+
hidePlaceholder () {
|
31
|
+
this.placeholderTarget.classList.add('d-none')
|
32
|
+
}
|
33
|
+
|
34
|
+
handleInputChange () {
|
35
|
+
this.validate()
|
36
|
+
}
|
37
|
+
|
38
|
+
dispatchSelectionEvent () {
|
39
|
+
document.dispatchEvent(
|
40
|
+
new CustomEvent(
|
41
|
+
'mediaSelectionSubmitted',
|
42
|
+
{
|
43
|
+
detail: {
|
44
|
+
name: this.element.dataset.name,
|
45
|
+
items: this.renderItemsForEvent(this.selectedItems())
|
46
|
+
}
|
47
|
+
}
|
48
|
+
)
|
49
|
+
)
|
50
|
+
}
|
51
|
+
|
52
|
+
triggerFormSubmission () {
|
53
|
+
this.formTarget.requestSubmit()
|
54
|
+
}
|
55
|
+
|
56
|
+
renderItemsForEvent (items) {
|
57
|
+
return items.map((item) => this.renderItemForEvent(item))
|
58
|
+
}
|
59
|
+
|
60
|
+
renderItemForEvent (item) {
|
61
|
+
return {
|
62
|
+
blobId: item.querySelector('input[type="checkbox"]').value,
|
63
|
+
thumbnail: item.querySelector('.h-thumbnail')
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
selectedItems () {
|
68
|
+
return this.itemTargets.filter((item) => {
|
69
|
+
const checkbox = item.querySelector('input[type="checkbox"]')
|
70
|
+
return checkbox.checked
|
71
|
+
})
|
72
|
+
}
|
73
|
+
|
74
|
+
selectedItemsCount () {
|
75
|
+
return this.selectedItems().length
|
76
|
+
}
|
77
|
+
|
78
|
+
minSelectedItems () {
|
79
|
+
return parseInt(this.element.dataset.min, 10) || 0
|
80
|
+
}
|
81
|
+
|
82
|
+
maxSelectedItems () {
|
83
|
+
return parseInt(this.element.dataset.max, 10) || Infinity
|
84
|
+
}
|
85
|
+
|
86
|
+
validate () {
|
87
|
+
if (this.isValid()) {
|
88
|
+
this.enableSelectButton()
|
89
|
+
} else {
|
90
|
+
this.disableSelectButton()
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
enableSelectButton () {
|
95
|
+
this.selectButtonTarget.removeAttribute('disabled')
|
96
|
+
}
|
97
|
+
|
98
|
+
disableSelectButton () {
|
99
|
+
this.selectButtonTarget.setAttribute('disabled', '')
|
100
|
+
}
|
101
|
+
|
102
|
+
isValid () {
|
103
|
+
const count = this.selectedItemsCount()
|
104
|
+
return count >= this.minSelectedItems() && count <= this.maxSelectedItems()
|
105
|
+
}
|
106
|
+
|
107
|
+
updateCount () {
|
108
|
+
this.countTarget.innerHTML = this.idCheckboxTargets.filter(checkbox => checkbox.checked).length
|
109
|
+
}
|
110
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets () {
|
5
|
+
return ['textarea', 'count']
|
6
|
+
}
|
7
|
+
|
8
|
+
connect () {
|
9
|
+
this.update()
|
10
|
+
}
|
11
|
+
|
12
|
+
update () {
|
13
|
+
this.resize()
|
14
|
+
this.updateCount()
|
15
|
+
}
|
16
|
+
|
17
|
+
resize () {
|
18
|
+
this.textareaTarget.style.height = 'auto'
|
19
|
+
this.textareaTarget.setAttribute('style', 'height:' + (this.textareaTarget.scrollHeight) + 'px;overflow-y:hidden;')
|
20
|
+
}
|
21
|
+
|
22
|
+
updateCount () {
|
23
|
+
if (this.textareaTarget.getAttribute('maxlength')) {
|
24
|
+
this.updateCountLength()
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
updateCountLength () {
|
29
|
+
const currentLength = this.textareaTarget.value.length
|
30
|
+
const maximumLength = this.textareaTarget.getAttribute('maxlength')
|
31
|
+
|
32
|
+
this.countTarget.textContent = `${currentLength}/${maximumLength}`
|
33
|
+
}
|
34
|
+
}
|
@@ -10,13 +10,17 @@ import FilterRowController from './controllers/filter_row_controller'
|
|
10
10
|
import FiltersController from './controllers/filters_controller'
|
11
11
|
import FlatpickrController from './controllers/flatpickr_controller'
|
12
12
|
import HelloController from './controllers/hello_controller'
|
13
|
+
import MediaController from './controllers/media_controller'
|
14
|
+
import MediaModalController from './controllers/media_modal_controller'
|
13
15
|
import NotificationController from './controllers/notification_controller'
|
14
16
|
import PopupController from './controllers/popup_controller'
|
15
17
|
import RedactorxController from './controllers/redactorx_controller'
|
18
|
+
import RemoteModalController from './controllers/remote_modal_controller'
|
16
19
|
import RepeaterController from './controllers/repeater_controller'
|
17
20
|
import SelectController from './controllers/select_controller'
|
18
21
|
import TableActionsController from './controllers/table_actions_controller'
|
19
22
|
import TableController from './controllers/table_controller'
|
23
|
+
import TextareaController from './controllers/textarea_controller'
|
20
24
|
|
21
25
|
export class Headmin {
|
22
26
|
static start () {
|
@@ -31,12 +35,16 @@ export class Headmin {
|
|
31
35
|
Stimulus.register('filters', FiltersController)
|
32
36
|
Stimulus.register('flatpickr', FlatpickrController)
|
33
37
|
Stimulus.register('hello', HelloController)
|
38
|
+
Stimulus.register('media', MediaController)
|
39
|
+
Stimulus.register('media-modal', MediaModalController)
|
34
40
|
Stimulus.register('notification', NotificationController)
|
35
41
|
Stimulus.register('popup', PopupController)
|
36
42
|
Stimulus.register('redactorx', RedactorxController)
|
43
|
+
Stimulus.register('remote-modal', RemoteModalController)
|
37
44
|
Stimulus.register('repeater', RepeaterController)
|
38
45
|
Stimulus.register('select', SelectController)
|
39
46
|
Stimulus.register('table', TableController)
|
40
47
|
Stimulus.register('table-actions', TableActionsController)
|
48
|
+
Stimulus.register('textarea', TextareaController)
|
41
49
|
}
|
42
50
|
}
|