headmin 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/assets/javascripts/headmin/controllers/filter_controller.js +15 -3
- data/app/assets/javascripts/headmin/controllers/filter_row_controller.js +75 -47
- data/app/assets/javascripts/headmin/controllers/infinite_scroller_controller.js +30 -0
- data/app/assets/javascripts/headmin/controllers/media_controller.js +24 -8
- data/app/assets/javascripts/headmin/controllers/media_modal_controller.js +142 -105
- data/app/assets/javascripts/headmin/index.js +2 -0
- data/app/assets/javascripts/headmin.js +122 -19
- data/app/assets/stylesheets/headmin.css +1 -1
- data/app/controllers/headmin/media_controller.rb +11 -0
- data/app/helpers/headmin/form_helper.rb +0 -11
- data/app/models/headmin/filter/association.rb +0 -12
- data/app/models/headmin/filter/association_count.rb +50 -0
- data/app/models/headmin/filter/association_count_view.rb +78 -0
- data/app/models/headmin/filter/base.rb +10 -0
- data/app/models/headmin/filter/date.rb +8 -1
- data/app/models/headmin/filter/date_view.rb +1 -1
- data/app/models/headmin/filter/number.rb +0 -12
- data/app/models/headmin/filter/operator_view.rb +3 -1
- data/app/views/headmin/_filters.html.erb +1 -1
- data/app/views/headmin/filters/_association_count.html.erb +28 -0
- data/app/views/headmin/filters/_date.html.erb +1 -0
- data/app/views/headmin/media/_modal.html.erb +8 -3
- data/app/views/headmin/media/_thumbnail.html.erb +20 -0
- data/app/views/headmin/media/create.turbo_stream.erb +1 -1
- data/app/views/headmin/media/index.turbo_stream.erb +11 -0
- data/app/views/headmin/media/thumbnail.html.erb +3 -0
- data/app/views/headmin/pagination/_infinite.html.erb +7 -0
- data/config/locales/headmin/filters/en.yml +2 -0
- data/config/locales/headmin/filters/nl.yml +2 -0
- data/config/locales/headmin/pagination/en.yml +2 -0
- data/config/locales/headmin/pagination/nl.yml +2 -0
- data/config/routes.rb +2 -1
- data/lib/headmin/version.rb +1 -1
- data/package.json +1 -1
- metadata +10 -3
- data/app/views/headmin/media/_item.html.erb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ec99ac85337bc096a16478a65e5434f37209281e861043a59f8cd4692197851
|
4
|
+
data.tar.gz: e3c9bf55658ce12c6d96ca0791ffa7e2dae14df3df9798e4ee21dc4b07b2f994
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0eaf4ed9453bbc2cf383c82d050c708f7f8e7ba930c58fb9a0c2432ada5f4e928644b2d2e26ba331bd793bd4d58e5cca261f8e61ee091e987c90b62d6d096da
|
7
|
+
data.tar.gz: 249378b6c61d2d02b4349af48080fc9c53d8bba28fe41da0d7492c725502f2932384e3b9dfde718be1eca5f1cbc17c4d0ef884ec6dd21be804f0592be10bef7f
|
data/Gemfile.lock
CHANGED
@@ -15,8 +15,6 @@ export default class extends Controller {
|
|
15
15
|
// This allows calling controller methods from the element in other controllers
|
16
16
|
connect () {
|
17
17
|
this.element.controller = this
|
18
|
-
|
19
|
-
this.updateHiddenValue()
|
20
18
|
}
|
21
19
|
|
22
20
|
toggle (event) {
|
@@ -89,7 +87,21 @@ export default class extends Controller {
|
|
89
87
|
for (const row of this.rowTargets) {
|
90
88
|
const conditional = row.previousElementSibling ? row.previousElementSibling.querySelector('[data-filter-target="conditional"]').value : null
|
91
89
|
const operator = row.querySelector('[data-filter-target="operator"]').value
|
92
|
-
|
90
|
+
let values = Array.from(row.querySelectorAll('[data-filter-target="value"]'))
|
91
|
+
|
92
|
+
// Only the visible elements are of interest
|
93
|
+
values = values.filter((element) => {
|
94
|
+
return element.style.display;
|
95
|
+
})
|
96
|
+
|
97
|
+
// Grab the value of each visible element
|
98
|
+
values = values.map((element) => {
|
99
|
+
return element.value
|
100
|
+
})
|
101
|
+
|
102
|
+
// Concatenate array to a string
|
103
|
+
const value = values.join(",")
|
104
|
+
|
93
105
|
string += `${conditional || ''}${operator}:${value}`
|
94
106
|
}
|
95
107
|
|
@@ -1,50 +1,78 @@
|
|
1
|
-
import {
|
1
|
+
import {Controller} from '@hotwired/stimulus'
|
2
2
|
|
3
3
|
export default class extends Controller {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
4
|
+
static get targets() {
|
5
|
+
return ['original', 'operator', 'null']
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.operatorTarget.addEventListener('change', () => this.handleOperatorChange())
|
10
|
+
this.handleOperatorChange()
|
11
|
+
}
|
12
|
+
|
13
|
+
handleOperatorChange() {
|
14
|
+
if (this.operatorTarget.value === 'is_null' || this.operatorTarget.value === 'is_not_null') {
|
15
|
+
this.toggleNullInput()
|
16
|
+
} else if (this.operatorTarget.value === 'between' || this.operatorTarget.value === 'not_between') {
|
17
|
+
this.toggleSecondaryInput()
|
18
|
+
} else {
|
19
|
+
this.toggleOriginalInput()
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
toggleNullInput() {
|
24
|
+
this.hideOriginal()
|
25
|
+
this.hideSecondary()
|
26
|
+
this.showNull()
|
27
|
+
}
|
28
|
+
|
29
|
+
toggleOriginalInput() {
|
30
|
+
this.showOriginal()
|
31
|
+
this.hideSecondary()
|
32
|
+
this.hideNull()
|
33
|
+
}
|
34
|
+
|
35
|
+
toggleSecondaryInput() {
|
36
|
+
this.showSecondary()
|
37
|
+
this.hideOriginal()
|
38
|
+
this.hideNull()
|
39
|
+
}
|
40
|
+
|
41
|
+
hideOriginal() {
|
42
|
+
this.originalTarget.style.display = 'none'
|
43
|
+
this.originalTarget.setAttribute('data-filter-target', 'value_original')
|
44
|
+
}
|
45
|
+
|
46
|
+
showOriginal() {
|
47
|
+
this.originalTarget.style.display = 'block'
|
48
|
+
this.originalTarget.setAttribute('data-filter-target', 'value')
|
49
|
+
}
|
50
|
+
|
51
|
+
hideSecondary() {
|
52
|
+
for (const [index, value] of this.originalTargets.entries()) {
|
53
|
+
if (index != 0) {
|
54
|
+
value.style.display = 'none'
|
55
|
+
value.setAttribute('data-filter-target', 'value_original')
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
showSecondary() {
|
61
|
+
for (const [index, value] of this.originalTargets.entries()) {
|
62
|
+
if (index != 0) {
|
63
|
+
value.style.display = 'block'
|
64
|
+
value.setAttribute('data-filter-target', 'value')
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
hideNull() {
|
70
|
+
this.nullTarget.style.display = 'none'
|
71
|
+
this.nullTarget.setAttribute('data-filter-target', 'value_null')
|
72
|
+
}
|
73
|
+
|
74
|
+
showNull() {
|
75
|
+
this.nullTarget.style.display = 'block'
|
76
|
+
this.nullTarget.setAttribute('data-filter-target', 'value')
|
77
|
+
}
|
50
78
|
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
/* global fetch, Event */
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
connect() {
|
6
|
+
this.clickWhenInViewport()
|
7
|
+
|
8
|
+
document.querySelector(".modal-body").addEventListener("scroll", () => {
|
9
|
+
this.clickWhenInViewport()
|
10
|
+
})
|
11
|
+
}
|
12
|
+
|
13
|
+
clickWhenInViewport() {
|
14
|
+
if (!this.isLoading() && this.isInViewport()) {
|
15
|
+
this.element.setAttribute("clicked", 1)
|
16
|
+
this.element.click()
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
isLoading() {
|
21
|
+
return this.element.hasAttribute("clicked")
|
22
|
+
}
|
23
|
+
|
24
|
+
isInViewport() {
|
25
|
+
const rect = this.element.getBoundingClientRect()
|
26
|
+
|
27
|
+
return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
|
28
|
+
}
|
29
|
+
|
30
|
+
}
|
@@ -66,10 +66,10 @@ export default class extends Controller {
|
|
66
66
|
|
67
67
|
selectItems (items) {
|
68
68
|
// Destroy all deselected items
|
69
|
-
this.
|
69
|
+
this.removeAllDeselectedItems(items)
|
70
70
|
|
71
|
-
// Add all selected items
|
72
|
-
this.
|
71
|
+
// Add all new selected items
|
72
|
+
this.addNewItems(items)
|
73
73
|
|
74
74
|
this.postProcess()
|
75
75
|
}
|
@@ -129,8 +129,16 @@ export default class extends Controller {
|
|
129
129
|
})
|
130
130
|
}
|
131
131
|
|
132
|
-
|
133
|
-
|
132
|
+
addNewItems (items) {
|
133
|
+
const itemTargetIds = this.itemTargets.map((i) => { return parseInt(i.querySelectorAll("input")[1].value)})
|
134
|
+
items.forEach((item) => {
|
135
|
+
if (itemTargetIds.includes(item.blobId)) {
|
136
|
+
// Do not add this item (as it is already present)
|
137
|
+
return
|
138
|
+
}
|
139
|
+
|
140
|
+
this.addItem(item)
|
141
|
+
})
|
134
142
|
}
|
135
143
|
|
136
144
|
addItem (item) {
|
@@ -192,12 +200,20 @@ export default class extends Controller {
|
|
192
200
|
return template.innerHTML.replace(regex, randomNumber)
|
193
201
|
}
|
194
202
|
|
195
|
-
|
196
|
-
this.
|
203
|
+
removeAllDeselectedItems (items) {
|
204
|
+
this.removeDeselectedItems(items, this.itemTargets)
|
197
205
|
}
|
198
206
|
|
199
|
-
|
207
|
+
removeDeselectedItems (elements, items) {
|
208
|
+
const returnedBlobIds = elements.map((e) => { return e.blobId})
|
209
|
+
|
200
210
|
items.forEach((item) => {
|
211
|
+
const blobId = parseInt(item.querySelectorAll("input")[1].value)
|
212
|
+
if (returnedBlobIds.includes(blobId)) {
|
213
|
+
// Do not delete this one
|
214
|
+
return;
|
215
|
+
}
|
216
|
+
|
201
217
|
this.removeItem(item)
|
202
218
|
})
|
203
219
|
}
|
@@ -1,110 +1,147 @@
|
|
1
1
|
/* global CustomEvent */
|
2
|
-
import {
|
2
|
+
import {Controller} from '@hotwired/stimulus'
|
3
3
|
|
4
4
|
export default class extends Controller {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
5
|
+
static get targets() {
|
6
|
+
return ['idCheckbox', 'item', 'form', 'selectButton', 'placeholder', 'count']
|
7
|
+
}
|
8
|
+
|
9
|
+
static get values () {
|
10
|
+
return { ids: Array }
|
11
|
+
}
|
12
|
+
|
13
|
+
connect() {
|
14
|
+
this.validate()
|
15
|
+
this.updateCount()
|
16
|
+
}
|
17
|
+
|
18
|
+
// Actions
|
19
|
+
select() {
|
20
|
+
this.dispatchSelectionEvent()
|
21
|
+
}
|
22
|
+
|
23
|
+
submitForm() {
|
24
|
+
this.hidePlaceholder()
|
25
|
+
this.triggerFormSubmission()
|
26
|
+
}
|
27
|
+
|
28
|
+
inputChange(event) {
|
29
|
+
this.handleIdsUpdate(event.target)
|
30
|
+
this.updateCount()
|
31
|
+
}
|
32
|
+
|
33
|
+
// Methods
|
34
|
+
hidePlaceholder() {
|
35
|
+
this.placeholderTarget.classList.add('d-none')
|
36
|
+
}
|
37
|
+
|
38
|
+
handleIdsUpdate(element) {
|
39
|
+
if (element.checked) {
|
40
|
+
let arr = this.idsValue
|
41
|
+
arr.push(element.value)
|
42
|
+
this.idsValue = arr
|
43
|
+
} else {
|
44
|
+
this.idsValue = this.idsValue.filter((value) => {
|
45
|
+
return element.value !== value
|
46
|
+
})
|
47
47
|
}
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
}
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
48
|
+
}
|
49
|
+
|
50
|
+
itemTargetConnected(element) {
|
51
|
+
this.updateItem(element.querySelector("input"))
|
52
|
+
}
|
53
|
+
|
54
|
+
updateItem(element) {
|
55
|
+
const arr = this.idsValue
|
56
|
+
|
57
|
+
if (arr.includes(element.value)) {
|
58
|
+
element.checked = true
|
59
|
+
} else {
|
60
|
+
element.checked = false
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
idsValueChanged() {
|
65
|
+
for (const item of this.itemTargets) {
|
66
|
+
this.updateItem(item.querySelector("input"))
|
67
|
+
}
|
68
|
+
this.validate()
|
69
|
+
}
|
70
|
+
|
71
|
+
dispatchSelectionEvent() {
|
72
|
+
document.dispatchEvent(
|
73
|
+
new CustomEvent(
|
74
|
+
'mediaSelectionSubmitted',
|
75
|
+
{
|
76
|
+
detail: {
|
77
|
+
name: this.element.dataset.name,
|
78
|
+
items: this.renderItemsForEvent()
|
79
|
+
}
|
80
|
+
}
|
81
|
+
)
|
82
|
+
)
|
83
|
+
}
|
84
|
+
|
85
|
+
triggerFormSubmission() {
|
86
|
+
this.formTarget.requestSubmit()
|
87
|
+
}
|
88
|
+
|
89
|
+
renderItemsForEvent() {
|
90
|
+
return this.idsValue.map((item) => this.renderItemForEvent(item)).filter((i) => { return i !== undefined })
|
91
|
+
}
|
92
|
+
|
93
|
+
renderItemForEvent(item) {
|
94
|
+
const id = parseInt(item)
|
95
|
+
const blob_id = `#blob_${id}`
|
96
|
+
const element = this.element.querySelector(blob_id)
|
97
|
+
|
98
|
+
return {
|
99
|
+
blobId: id,
|
100
|
+
thumbnail: element ? element.querySelector('.h-thumbnail') : ""
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
selectedItems() {
|
105
|
+
return this.itemTargets.filter((item) => {
|
106
|
+
const checkbox = item.querySelector('input[type="checkbox"]')
|
107
|
+
return checkbox.checked
|
108
|
+
})
|
109
|
+
}
|
110
|
+
|
111
|
+
selectedItemsCount() {
|
112
|
+
return this.idsValue.length
|
113
|
+
}
|
114
|
+
|
115
|
+
minSelectedItems() {
|
116
|
+
return parseInt(this.element.dataset.min, 10) || 0
|
117
|
+
}
|
118
|
+
|
119
|
+
maxSelectedItems() {
|
120
|
+
return parseInt(this.element.dataset.max, 10) || Infinity
|
121
|
+
}
|
122
|
+
|
123
|
+
validate() {
|
124
|
+
if (this.isValid()) {
|
125
|
+
this.enableSelectButton()
|
126
|
+
} else {
|
127
|
+
this.disableSelectButton()
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
enableSelectButton() {
|
132
|
+
this.selectButtonTarget.removeAttribute('disabled')
|
133
|
+
}
|
134
|
+
|
135
|
+
disableSelectButton() {
|
136
|
+
this.selectButtonTarget.setAttribute('disabled', '')
|
137
|
+
}
|
138
|
+
|
139
|
+
isValid() {
|
140
|
+
const count = this.selectedItemsCount()
|
141
|
+
return count >= this.minSelectedItems() && count <= this.maxSelectedItems()
|
142
|
+
}
|
143
|
+
|
144
|
+
updateCount() {
|
145
|
+
this.countTarget.innerHTML = this.selectedItemsCount()
|
146
|
+
}
|
110
147
|
}
|
@@ -10,6 +10,7 @@ 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 InfiniteScrollerController from './controllers/infinite_scroller_controller'
|
13
14
|
import MediaController from './controllers/media_controller'
|
14
15
|
import MediaModalController from './controllers/media_modal_controller'
|
15
16
|
import NotificationController from './controllers/notification_controller'
|
@@ -35,6 +36,7 @@ export class Headmin {
|
|
35
36
|
Stimulus.register('filters', FiltersController)
|
36
37
|
Stimulus.register('flatpickr', FlatpickrController)
|
37
38
|
Stimulus.register('hello', HelloController)
|
39
|
+
Stimulus.register('infinite-scroller', InfiniteScrollerController)
|
38
40
|
Stimulus.register('media', MediaController)
|
39
41
|
Stimulus.register('media-modal', MediaModalController)
|
40
42
|
Stimulus.register('notification', NotificationController)
|