headmin 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/javascripts/headmin/controllers/filter_controller.js +15 -3
  4. data/app/assets/javascripts/headmin/controllers/filter_row_controller.js +75 -47
  5. data/app/assets/javascripts/headmin/controllers/infinite_scroller_controller.js +30 -0
  6. data/app/assets/javascripts/headmin/controllers/media_controller.js +24 -8
  7. data/app/assets/javascripts/headmin/controllers/media_modal_controller.js +142 -105
  8. data/app/assets/javascripts/headmin/index.js +2 -0
  9. data/app/assets/javascripts/headmin.js +122 -19
  10. data/app/assets/stylesheets/headmin.css +1 -1
  11. data/app/controllers/headmin/media_controller.rb +11 -0
  12. data/app/helpers/headmin/form_helper.rb +0 -11
  13. data/app/models/headmin/filter/association.rb +0 -12
  14. data/app/models/headmin/filter/association_count.rb +50 -0
  15. data/app/models/headmin/filter/association_count_view.rb +78 -0
  16. data/app/models/headmin/filter/base.rb +10 -0
  17. data/app/models/headmin/filter/date.rb +8 -1
  18. data/app/models/headmin/filter/date_view.rb +1 -1
  19. data/app/models/headmin/filter/number.rb +0 -12
  20. data/app/models/headmin/filter/operator_view.rb +3 -1
  21. data/app/views/headmin/_filters.html.erb +1 -1
  22. data/app/views/headmin/filters/_association_count.html.erb +28 -0
  23. data/app/views/headmin/filters/_date.html.erb +1 -0
  24. data/app/views/headmin/media/_modal.html.erb +8 -3
  25. data/app/views/headmin/media/_thumbnail.html.erb +20 -0
  26. data/app/views/headmin/media/create.turbo_stream.erb +1 -1
  27. data/app/views/headmin/media/index.turbo_stream.erb +11 -0
  28. data/app/views/headmin/media/thumbnail.html.erb +3 -0
  29. data/app/views/headmin/pagination/_infinite.html.erb +7 -0
  30. data/config/locales/headmin/filters/en.yml +2 -0
  31. data/config/locales/headmin/filters/nl.yml +2 -0
  32. data/config/locales/headmin/pagination/en.yml +2 -0
  33. data/config/locales/headmin/pagination/nl.yml +2 -0
  34. data/config/routes.rb +2 -1
  35. data/lib/headmin/version.rb +1 -1
  36. data/package.json +1 -1
  37. metadata +10 -3
  38. 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: 4f0d94377c6d3cf0d97bdffa08c34d9c95ef9b72036c8e7b1d0d1b89996ed52d
4
- data.tar.gz: 9b263a38b16dacace7769f9eb2cab3b90dc478cc229fead7ae157c4fd48e7f65
3
+ metadata.gz: 4ec99ac85337bc096a16478a65e5434f37209281e861043a59f8cd4692197851
4
+ data.tar.gz: e3c9bf55658ce12c6d96ca0791ffa7e2dae14df3df9798e4ee21dc4b07b2f994
5
5
  SHA512:
6
- metadata.gz: 3ee1ebb9dbc530154fd310c910dbab36e13272a0c3433b494588e6f552c5e8af6c6ee4f39b41f2737a7b2321805c02d9a5c22b7dfeea9c62455584ecc0cbc907
7
- data.tar.gz: e88897ec516a9b4f83188f8eb12e1b49d46da547d03b459942da6f77266faee9031f0f3f06c5300529183e6f6cd9aa2112e92ce1b30ba7902edcd919b0f9ceaf
6
+ metadata.gz: c0eaf4ed9453bbc2cf383c82d050c708f7f8e7ba930c58fb9a0c2432ada5f4e928644b2d2e26ba331bd793bd4d58e5cca261f8e61ee091e987c90b62d6d096da
7
+ data.tar.gz: 249378b6c61d2d02b4349af48080fc9c53d8bba28fe41da0d7492c725502f2932384e3b9dfde718be1eca5f1cbc17c4d0ef884ec6dd21be804f0592be10bef7f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- headmin (0.6.0)
4
+ headmin (0.6.1)
5
5
  closure_tree (~> 7.4)
6
6
  inline_svg (~> 1.7)
7
7
  redcarpet (~> 3.5)
@@ -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
- const value = row.querySelector('[data-filter-target="value"]').value
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 { Controller } from '@hotwired/stimulus'
1
+ import {Controller} from '@hotwired/stimulus'
2
2
 
3
3
  export default class extends Controller {
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 {
17
- this.toggleOriginalInput()
18
- }
19
- }
20
-
21
- toggleNullInput () {
22
- this.hideOriginal()
23
- this.showNull()
24
- }
25
-
26
- toggleOriginalInput () {
27
- this.showOriginal()
28
- this.hideNull()
29
- }
30
-
31
- hideOriginal () {
32
- this.originalTarget.style.display = 'none'
33
- this.originalTarget.setAttribute('data-filter-target', 'value_original')
34
- }
35
-
36
- showOriginal () {
37
- this.originalTarget.style.display = 'block'
38
- this.originalTarget.setAttribute('data-filter-target', 'value')
39
- }
40
-
41
- hideNull () {
42
- this.nullTarget.style.display = 'none'
43
- this.nullTarget.setAttribute('data-filter-target', 'value_null')
44
- }
45
-
46
- showNull () {
47
- this.nullTarget.style.display = 'block'
48
- this.nullTarget.setAttribute('data-filter-target', 'value')
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.removeAllItems()
69
+ this.removeAllDeselectedItems(items)
70
70
 
71
- // Add all selected items
72
- this.addItems(items)
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
- addItems (items) {
133
- items.forEach((item) => this.addItem(item))
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
- removeAllItems () {
196
- this.removeItems(this.itemTargets)
203
+ removeAllDeselectedItems (items) {
204
+ this.removeDeselectedItems(items, this.itemTargets)
197
205
  }
198
206
 
199
- removeItems (items) {
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 { Controller } from '@hotwired/stimulus'
2
+ import {Controller} from '@hotwired/stimulus'
3
3
 
4
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
- }
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
- 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
- }
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)