formstrap 0.1.3 → 0.2.0

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -1
  3. data/app/assets/javascripts/formstrap/controllers/file_preview_controller.js +5 -5
  4. data/app/assets/javascripts/formstrap/controllers/media_controller.js +4 -2
  5. data/app/assets/javascripts/formstrap/controllers/media_modal_controller.js +38 -2
  6. data/app/assets/javascripts/formstrap/controllers/popup_controller.js +75 -0
  7. data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +4 -4
  8. data/app/assets/javascripts/formstrap/index.js +2 -0
  9. data/app/assets/javascripts/formstrap.js +1790 -129
  10. data/app/assets/stylesheets/formstrap/{forms/autocomplete.scss → autocomplete.scss} +1 -1
  11. data/app/assets/stylesheets/formstrap/{forms/file.scss → file.scss} +10 -10
  12. data/app/assets/stylesheets/formstrap/general.scss +8 -1
  13. data/app/assets/stylesheets/formstrap/media/_modal.scss +9 -0
  14. data/app/assets/stylesheets/formstrap/{forms/media.scss → media/_validation.scss} +2 -2
  15. data/app/assets/stylesheets/formstrap/media.scss +2 -1
  16. data/app/assets/stylesheets/formstrap/{forms/repeater.scss → repeater.scss} +8 -8
  17. data/app/assets/stylesheets/formstrap/shared/popup.scss +17 -0
  18. data/app/assets/stylesheets/formstrap/shared/thumbnail.scss +23 -0
  19. data/app/assets/stylesheets/formstrap/shared.scss +2 -0
  20. data/app/assets/stylesheets/formstrap/utilities/dropzone.scss +2 -2
  21. data/app/assets/stylesheets/formstrap/utilities.scss +0 -1
  22. data/app/assets/stylesheets/formstrap.css +155 -143
  23. data/app/assets/stylesheets/formstrap.scss +5 -1
  24. data/app/controllers/formstrap/media_controller.rb +6 -12
  25. data/app/helpers/application_helper.rb +20 -0
  26. data/app/models/concerns/formstrap/attachment.rb +16 -0
  27. data/app/models/concerns/formstrap/blob.rb +56 -0
  28. data/app/models/formstrap/file_view.rb +3 -3
  29. data/app/models/formstrap/media_view.rb +12 -1
  30. data/app/models/formstrap/thumbnail_view.rb +108 -0
  31. data/app/views/formstrap/_autocomplete.html.erb +1 -1
  32. data/app/views/formstrap/_file.html.erb +8 -8
  33. data/app/views/formstrap/_media.html.erb +5 -4
  34. data/app/views/formstrap/_repeater.html.erb +3 -3
  35. data/app/views/formstrap/media/_item.html.erb +10 -7
  36. data/app/views/formstrap/media/_modal.html.erb +35 -8
  37. data/app/views/formstrap/media/_thumbnail.html.erb +12 -8
  38. data/app/views/formstrap/media/_validation.html.erb +1 -1
  39. data/app/views/formstrap/media/create.turbo_stream.erb +3 -1
  40. data/app/views/formstrap/media/index.html.erb +1 -1
  41. data/app/views/formstrap/repeater/_row.html.erb +4 -4
  42. data/app/views/formstrap/shared/_popup.html.erb +1 -1
  43. data/app/views/formstrap/shared/_thumbnail.html.erb +5 -3
  44. data/config/locales/formstrap/forms/en.yml +0 -14
  45. data/config/locales/formstrap/forms/nl.yml +1 -15
  46. data/config/locales/formstrap/media/en.yml +2 -10
  47. data/config/locales/formstrap/media/nl.yml +2 -10
  48. data/config/routes.rb +0 -2
  49. data/lib/formstrap/version.rb +1 -1
  50. metadata +16 -29
  51. data/app/assets/stylesheets/formstrap/forms.scss +0 -12
  52. data/app/assets/stylesheets/formstrap/media/index.scss +0 -9
  53. data/app/assets/stylesheets/formstrap/utilities/buttons.scss +0 -27
  54. data/app/models/formstrap/blocks_view.rb +0 -43
  55. data/app/views/formstrap/_blocks.html.erb +0 -45
  56. data/app/views/formstrap/_to_ary.html.erb +0 -0
  57. data/app/views/formstrap/blocks/_modal.html.erb +0 -20
  58. data/app/views/formstrap/fields/_base.html.erb +0 -25
  59. data/app/views/formstrap/fields/_file.html.erb +0 -17
  60. data/app/views/formstrap/fields/_files.html.erb +0 -17
  61. data/app/views/formstrap/fields/_group.html.erb +0 -52
  62. data/app/views/formstrap/fields/_list.html.erb +0 -31
  63. data/app/views/formstrap/fields/_text.html.erb +0 -17
  64. data/app/views/formstrap/media/_media_item_modal.html.erb +0 -77
  65. data/app/views/formstrap/media/show.html.erb +0 -9
  66. data/app/views/formstrap/media/update.turbo_stream.erb +0 -3
  67. data/config/locales/activerecord/en.yml +0 -12
  68. data/config/locales/activerecord/nl.yml +0 -13
  69. data/config/locales/defaults/en.yml +0 -215
  70. data/config/locales/defaults/nl.yml +0 -213
  71. data/config/locales/devise/en.yml +0 -65
  72. data/config/locales/devise/nl.yml +0 -85
  73. /data/app/assets/stylesheets/formstrap/{forms/search.scss → search.scss} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7107b5857d9ac041c0adfb899049c93cbda09ecb46b0740a848cc3d7ebb881e
4
- data.tar.gz: 56f553925e6d00e5ca9e2c22ea02596a64023739304eef1af6f1a35e058046a3
3
+ metadata.gz: 07cc257d4684759a070f1d259212a1e0c10a0edf4f0c9f9dd275b5ef0a3708be
4
+ data.tar.gz: 626e3625647a1d0e897f03e3c4ed712eb847e20761a112cc8b3dff667d82d072
5
5
  SHA512:
6
- metadata.gz: 30d0714a40e7de9ea0572ba8892c53d909d9f627e09a4a1eee04bc938ed6cf35423312186f0803dd2d04170209ac3dbc9c494d87f94fdb2ce801488bf90687aa
7
- data.tar.gz: da87804184cae4b9234232b37750c9f6e21ab87e9426320ae78bdeeac583b944cfe30fd23f045e10474c43b194736f8cdf6591ab6627d29f35b66b782778cbe8
6
+ metadata.gz: 16262a9223b70aeb5f6e5ae830e124fd329226fd794c070e2b8afb5ebe1ce2f260c944da356fd8bae5246c1f931bc1b09652dab85f1c9ed8a16335b55978a678
7
+ data.tar.gz: 4d0d78db261a85d8cba1dd449b24f206ecc0aac2b3f3287e25bdc4f6c14b57a764bb17322ec7d8523279ee31135cf20fe0aa312182e894398135243a57afe1e4
data/CHANGELOG.md CHANGED
@@ -1 +1,38 @@
1
- # Formstap Changelog
1
+ # Formstrap Changelog
2
+
3
+ ## 0.2
4
+ - FEATURE: Search added to media modal
5
+ - FEATURE: Thumbnail supports SVG previews now
6
+ - FIX: You can upload new media and select it right away
7
+ - FIX: Flickering heights of thumbnails in media modal is fixed
8
+ - BREAK: All partials in `formstrap/fields/` are removed.
9
+
10
+ Copy the partials from `formstrap/fields/` to your project and customize them.
11
+
12
+ - BREAK: The partial `formstrap/blocks` is removed.
13
+
14
+ Copy `formstrap/blocks.html.erb` to your project and customize it.
15
+
16
+ - BREAK: Default filtering logic for ActiveStorage models is included in Formstrap.
17
+
18
+ To remove redundant code in your project, you can add these initializers.
19
+
20
+ config/initializers/extend_active_storage_attachment.rb
21
+ ```ruby
22
+ Rails.configuration.to_prepare do
23
+ ActiveStorage::Attachment.include Formstrap::Attachment
24
+
25
+ # Feel free to extend ActiveStorage::Attachment with your own concerns
26
+ # ActiveStorage::Attachment.include Admin::Attachment
27
+ end
28
+ ```
29
+
30
+ config/initializers/extend_active_storage_blob.rb.rb
31
+ ```ruby
32
+ Rails.configuration.to_prepare do
33
+ ActiveStorage::Blob.include Formstrap::Blob
34
+
35
+ # Feel free to extend ActiveStorage::Blob with your own concerns
36
+ # ActiveStorage::Blob.include Admin::Blob
37
+ end
38
+ ```
@@ -165,7 +165,7 @@ export default class extends Controller {
165
165
  }
166
166
 
167
167
  updateThumbnailRemoveButton (thumbnail, fileName) {
168
- const removeButton = thumbnail.querySelector('.h-form-file-thumbnail-remove')
168
+ const removeButton = thumbnail.querySelector('.formstrap-file-thumbnail-remove')
169
169
  if (removeButton) {
170
170
  removeButton.dataset.filePreviewNameParam = fileName
171
171
  }
@@ -176,16 +176,16 @@ export default class extends Controller {
176
176
  }
177
177
 
178
178
  updateThumbnailBackground (thumbnail, url) {
179
- const thumbnailBackground = thumbnail.querySelector('.h-thumbnail-bg')
179
+ const thumbnailBackground = thumbnail.querySelector('.formstrap-thumbnail-bg')
180
180
  thumbnailBackground.style.backgroundImage = `url('${url}')`
181
181
  }
182
182
 
183
183
  removeThumbnailIcon (thumbnail) {
184
- thumbnail.querySelector('.h-thumbnail-bg').innerHTML = ''
184
+ thumbnail.querySelector('.formstrap-thumbnail-bg').innerHTML = ''
185
185
  }
186
186
 
187
187
  updateThumbnailIcon (thumbnail, icon) {
188
- thumbnail.querySelector('.h-thumbnail-bg').innerHTML = icon
188
+ thumbnail.querySelector('.formstrap-thumbnail-bg').innerHTML = icon
189
189
  }
190
190
 
191
191
  iconForMimeType (mimeType) {
@@ -206,7 +206,7 @@ export default class extends Controller {
206
206
  const iconName = Object.keys(typeMap).find(key => typeMap[key].includes(mimeType))
207
207
  const fullIconName = ['bi', 'file', 'earmark', iconName].filter(e => typeof e === 'string' && e !== '').join('-')
208
208
 
209
- return `<i class="bi ${fullIconName} h-thumbnail-icon"></i>`
209
+ return `<i class="bi ${fullIconName} formstrap-thumbnail-icon"></i>`
210
210
  }
211
211
 
212
212
  isImage (file) {
@@ -187,10 +187,12 @@ export default class extends Controller {
187
187
 
188
188
  // Update edit button url
189
189
  const editButton = newItem.querySelector('[data-media-target="editButton"]')
190
- editButton.setAttribute('href', editButton.getAttribute('href').replace('$1', item.blobId))
190
+ if (editButton) {
191
+ editButton.setAttribute('href', editButton.getAttribute('href').replace(':id', item.blobId))
192
+ }
191
193
 
192
194
  // Copy thumbnail
193
- const oldThumbnail = newItem.querySelector('.h-thumbnail')
195
+ const oldThumbnail = newItem.querySelector('.formstrap-thumbnail')
194
196
  const newThumbnail = item.thumbnail.cloneNode(true)
195
197
  oldThumbnail.parentNode.replaceChild(newThumbnail, oldThumbnail)
196
198
  }
@@ -3,7 +3,7 @@ import { Controller } from '@hotwired/stimulus'
3
3
 
4
4
  export default class extends Controller {
5
5
  static get targets () {
6
- return ['idCheckbox', 'item', 'form', 'selectButton', 'placeholder', 'count']
6
+ return ['idCheckbox', 'item', 'form', 'selectButton', 'placeholder', 'count', 'search', 'searchId']
7
7
  }
8
8
 
9
9
  static get values () {
@@ -22,6 +22,7 @@ export default class extends Controller {
22
22
 
23
23
  submitForm () {
24
24
  this.hidePlaceholder()
25
+ this.search('')
25
26
  this.triggerFormSubmission()
26
27
  }
27
28
 
@@ -35,6 +36,13 @@ export default class extends Controller {
35
36
  this.placeholderTarget.classList.add('d-none')
36
37
  }
37
38
 
39
+ search (string) {
40
+ const search = this.searchTarget.querySelector("input[name='search']")
41
+ search.value = string
42
+
43
+ this.searchTarget.requestSubmit()
44
+ }
45
+
38
46
  handleIdsUpdate (element) {
39
47
  if (element.checked) {
40
48
  const arr = this.idsValue
@@ -45,6 +53,8 @@ export default class extends Controller {
45
53
  return element.value !== value
46
54
  })
47
55
  }
56
+
57
+ this.handleSearchdIdsUpdate()
48
58
  }
49
59
 
50
60
  itemTargetConnected (element) {
@@ -97,7 +107,7 @@ export default class extends Controller {
97
107
 
98
108
  return {
99
109
  blobId: id,
100
- thumbnail: element ? element.querySelector('.h-thumbnail') : ''
110
+ thumbnail: element ? element.querySelector('.formstrap-thumbnail') : ''
101
111
  }
102
112
  }
103
113
 
@@ -144,4 +154,30 @@ export default class extends Controller {
144
154
  updateCount () {
145
155
  this.countTarget.innerHTML = this.selectedItemsCount()
146
156
  }
157
+
158
+ handleSearchdIdsUpdate () {
159
+ this.deleteSearchIdInputs()
160
+ this.createSearchIdInputs()
161
+ }
162
+
163
+ deleteSearchIdInputs () {
164
+ for (const searchId of this.searchIdTargets) {
165
+ searchId.remove()
166
+ }
167
+ }
168
+
169
+ createSearchIdInputs () {
170
+ for (const id of this.idsValue) {
171
+ this.createSearchIdInput(id)
172
+ }
173
+ }
174
+
175
+ createSearchIdInput (value) {
176
+ const input = document.createElement('input')
177
+ input.type = 'hidden'
178
+ input.name = 'ids[]'
179
+ input.setAttribute('data-media-modal-target', 'searchId')
180
+ input.value = value
181
+ this.searchTarget.appendChild(input)
182
+ }
147
183
  }
@@ -0,0 +1,75 @@
1
+ /* global HTMLInputElement */
2
+ import {Controller} from '@hotwired/stimulus'
3
+ import {createPopper} from '@popperjs/core'
4
+
5
+ export default class extends Controller {
6
+ static get targets () {
7
+ return ['popup', 'button']
8
+ }
9
+
10
+ static get values () {
11
+ return { id: String }
12
+ }
13
+
14
+ connect () {
15
+ // Clicked outside popup
16
+ document.addEventListener('click', (event) => {
17
+ this.handleOutsideClick(event)
18
+ })
19
+ }
20
+
21
+ handleOutsideClick (event) {
22
+ const itemRemoved = !document.body.contains(event.target) // Ignore items that were removed from DOM (else this triggers a close)
23
+ if (itemRemoved) return
24
+
25
+ const inPopup = event.target.closest('[data-popup-target="popup"]') !== null
26
+ const inButton = event.target.closest('[data-popup-target="button"]') !== null
27
+ const openPopup = document.querySelector('[data-popup-target="popup"]:not(.closed)')
28
+
29
+ if (!inButton && !inPopup && openPopup) {
30
+ this.closePopup(openPopup)
31
+ }
32
+ }
33
+
34
+ open (event) {
35
+ const button = event.target.closest('[data-popup-target="button"]')
36
+ const popup = this.popupById(button.dataset.popupId)
37
+ const passThru = button.dataset.popupPassThru
38
+
39
+ // Open the popup
40
+ createPopper(button, popup)
41
+ this.openPopup(popup)
42
+
43
+ if (passThru) {
44
+ // Pass click event to an element inside the popup
45
+ const passThruElement = popup.querySelector(passThru)
46
+ passThruElement.click()
47
+
48
+ // Focus and select value if it's an input element
49
+ if (passThruElement instanceof HTMLInputElement) {
50
+ passThruElement.focus()
51
+ passThruElement.select()
52
+ }
53
+ }
54
+ }
55
+
56
+ close (event) {
57
+ const button = event.target.closest('[data-popup-target="button"]')
58
+ const popup = this.popupById(button.dataset.popupId)
59
+ this.closePopup(popup)
60
+ }
61
+
62
+ popupById (id) {
63
+ return this.popupTargets.find((popup) => {
64
+ return popup.dataset.popupId === id
65
+ })
66
+ }
67
+
68
+ openPopup (popup) {
69
+ popup.classList.remove('closed')
70
+ }
71
+
72
+ closePopup (popup) {
73
+ popup.classList.add('closed')
74
+ }
75
+ }
@@ -16,8 +16,8 @@ export default class extends Controller {
16
16
  Sortable.create(this.listTarget, {
17
17
  animation: 150,
18
18
  ghostClass: 'list-group-item-dark',
19
- draggable: '.repeater-row',
20
- handle: '.repeater-row-handle',
19
+ draggable: '.formstrap-repeater-row',
20
+ handle: '.formstrap-repeater-row-handle',
21
21
  onEnd: () => {
22
22
  this.resetIndices()
23
23
  this.resetPositions()
@@ -28,7 +28,7 @@ export default class extends Controller {
28
28
  }
29
29
 
30
30
  resetButtonIndices (event) {
31
- const row = event.target.closest('.repeater-row')
31
+ const row = event.target.closest('.formstrap-repeater-row')
32
32
  const index = this.containsRow(row) ? row.dataset.rowIndex : ''
33
33
  this.updatePopupButtonIndices(index)
34
34
  }
@@ -73,7 +73,7 @@ export default class extends Controller {
73
73
  removeRow (event) {
74
74
  event.preventDefault()
75
75
 
76
- const row = event.target.closest('.repeater-row')
76
+ const row = event.target.closest('.formstrap-repeater-row')
77
77
 
78
78
  if (row.dataset.newRecord === 'true') {
79
79
  // New records are simply removed from the page
@@ -8,6 +8,7 @@ import FlatpickrController from './controllers/flatpickr_controller'
8
8
  import InfiniteScrollerController from './controllers/infinite_scroller_controller'
9
9
  import MediaController from './controllers/media_controller'
10
10
  import MediaModalController from './controllers/media_modal_controller'
11
+ import PopupController from './controllers/popup_controller'
11
12
  import RedactorxController from './controllers/redactorx_controller'
12
13
  import RepeaterController from './controllers/repeater_controller'
13
14
  import SelectController from './controllers/select_controller'
@@ -24,6 +25,7 @@ export class Formstrap {
24
25
  Stimulus.register('infinite-scroller', InfiniteScrollerController)
25
26
  Stimulus.register('media', MediaController)
26
27
  Stimulus.register('media-modal', MediaModalController)
28
+ Stimulus.register('popup', PopupController)
27
29
  Stimulus.register('redactorx', RedactorxController)
28
30
  Stimulus.register('repeater', RepeaterController)
29
31
  Stimulus.register('select', SelectController)