headmin 0.6.1 → 0.6.2

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 (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)