formstrap 0.1.3 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) 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. data/package.json +1 -1
  51. metadata +16 -29
  52. data/app/assets/stylesheets/formstrap/forms.scss +0 -12
  53. data/app/assets/stylesheets/formstrap/media/index.scss +0 -9
  54. data/app/assets/stylesheets/formstrap/utilities/buttons.scss +0 -27
  55. data/app/models/formstrap/blocks_view.rb +0 -43
  56. data/app/views/formstrap/_blocks.html.erb +0 -45
  57. data/app/views/formstrap/_to_ary.html.erb +0 -0
  58. data/app/views/formstrap/blocks/_modal.html.erb +0 -20
  59. data/app/views/formstrap/fields/_base.html.erb +0 -25
  60. data/app/views/formstrap/fields/_file.html.erb +0 -17
  61. data/app/views/formstrap/fields/_files.html.erb +0 -17
  62. data/app/views/formstrap/fields/_group.html.erb +0 -52
  63. data/app/views/formstrap/fields/_list.html.erb +0 -31
  64. data/app/views/formstrap/fields/_text.html.erb +0 -17
  65. data/app/views/formstrap/media/_media_item_modal.html.erb +0 -77
  66. data/app/views/formstrap/media/show.html.erb +0 -9
  67. data/app/views/formstrap/media/update.turbo_stream.erb +0 -3
  68. data/config/locales/activerecord/en.yml +0 -12
  69. data/config/locales/activerecord/nl.yml +0 -13
  70. data/config/locales/defaults/en.yml +0 -215
  71. data/config/locales/defaults/nl.yml +0 -213
  72. data/config/locales/devise/en.yml +0 -65
  73. data/config/locales/devise/nl.yml +0 -85
  74. /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: a60b106c9ba61e0e8e4bcfbff6922ee35ca94e0f9d6b323f2536bbdeb4b159dc
4
+ data.tar.gz: b0df7426b57f7212e0fb4ba958feafab579fecffbd170a0b7519ea899a71846f
5
5
  SHA512:
6
- metadata.gz: 30d0714a40e7de9ea0572ba8892c53d909d9f627e09a4a1eee04bc938ed6cf35423312186f0803dd2d04170209ac3dbc9c494d87f94fdb2ce801488bf90687aa
7
- data.tar.gz: da87804184cae4b9234232b37750c9f6e21ab87e9426320ae78bdeeac583b944cfe30fd23f045e10474c43b194736f8cdf6591ab6627d29f35b66b782778cbe8
6
+ metadata.gz: 4bee48dada36d1789b7d913af8e22a4805bf2ffff7883f3e9a280d28799556abd9525045ff7395165255dfa84f5782bbde9c29d9855f61e8677b4d8cfa89120b
7
+ data.tar.gz: 8c36be158e08f4fdfa85e990f5e74c0cf5e355a172303e7eb2c65214b13267ab5882ac0d873c77b2cb119f01e761c47a0761bd9b97f7d0a47a9d192d733e478f
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)