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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -1
- data/app/assets/javascripts/formstrap/controllers/file_preview_controller.js +5 -5
- data/app/assets/javascripts/formstrap/controllers/media_controller.js +4 -2
- data/app/assets/javascripts/formstrap/controllers/media_modal_controller.js +38 -2
- data/app/assets/javascripts/formstrap/controllers/popup_controller.js +75 -0
- data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +4 -4
- data/app/assets/javascripts/formstrap/index.js +2 -0
- data/app/assets/javascripts/formstrap.js +1790 -129
- data/app/assets/stylesheets/formstrap/{forms/autocomplete.scss → autocomplete.scss} +1 -1
- data/app/assets/stylesheets/formstrap/{forms/file.scss → file.scss} +10 -10
- data/app/assets/stylesheets/formstrap/general.scss +8 -1
- data/app/assets/stylesheets/formstrap/media/_modal.scss +9 -0
- data/app/assets/stylesheets/formstrap/{forms/media.scss → media/_validation.scss} +2 -2
- data/app/assets/stylesheets/formstrap/media.scss +2 -1
- data/app/assets/stylesheets/formstrap/{forms/repeater.scss → repeater.scss} +8 -8
- data/app/assets/stylesheets/formstrap/shared/popup.scss +17 -0
- data/app/assets/stylesheets/formstrap/shared/thumbnail.scss +23 -0
- data/app/assets/stylesheets/formstrap/shared.scss +2 -0
- data/app/assets/stylesheets/formstrap/utilities/dropzone.scss +2 -2
- data/app/assets/stylesheets/formstrap/utilities.scss +0 -1
- data/app/assets/stylesheets/formstrap.css +155 -143
- data/app/assets/stylesheets/formstrap.scss +5 -1
- data/app/controllers/formstrap/media_controller.rb +6 -12
- data/app/helpers/application_helper.rb +20 -0
- data/app/models/concerns/formstrap/attachment.rb +16 -0
- data/app/models/concerns/formstrap/blob.rb +56 -0
- data/app/models/formstrap/file_view.rb +3 -3
- data/app/models/formstrap/media_view.rb +12 -1
- data/app/models/formstrap/thumbnail_view.rb +108 -0
- data/app/views/formstrap/_autocomplete.html.erb +1 -1
- data/app/views/formstrap/_file.html.erb +8 -8
- data/app/views/formstrap/_media.html.erb +5 -4
- data/app/views/formstrap/_repeater.html.erb +3 -3
- data/app/views/formstrap/media/_item.html.erb +10 -7
- data/app/views/formstrap/media/_modal.html.erb +35 -8
- data/app/views/formstrap/media/_thumbnail.html.erb +12 -8
- data/app/views/formstrap/media/_validation.html.erb +1 -1
- data/app/views/formstrap/media/create.turbo_stream.erb +3 -1
- data/app/views/formstrap/media/index.html.erb +1 -1
- data/app/views/formstrap/repeater/_row.html.erb +4 -4
- data/app/views/formstrap/shared/_popup.html.erb +1 -1
- data/app/views/formstrap/shared/_thumbnail.html.erb +5 -3
- data/config/locales/formstrap/forms/en.yml +0 -14
- data/config/locales/formstrap/forms/nl.yml +1 -15
- data/config/locales/formstrap/media/en.yml +2 -10
- data/config/locales/formstrap/media/nl.yml +2 -10
- data/config/routes.rb +0 -2
- data/lib/formstrap/version.rb +1 -1
- metadata +16 -29
- data/app/assets/stylesheets/formstrap/forms.scss +0 -12
- data/app/assets/stylesheets/formstrap/media/index.scss +0 -9
- data/app/assets/stylesheets/formstrap/utilities/buttons.scss +0 -27
- data/app/models/formstrap/blocks_view.rb +0 -43
- data/app/views/formstrap/_blocks.html.erb +0 -45
- data/app/views/formstrap/_to_ary.html.erb +0 -0
- data/app/views/formstrap/blocks/_modal.html.erb +0 -20
- data/app/views/formstrap/fields/_base.html.erb +0 -25
- data/app/views/formstrap/fields/_file.html.erb +0 -17
- data/app/views/formstrap/fields/_files.html.erb +0 -17
- data/app/views/formstrap/fields/_group.html.erb +0 -52
- data/app/views/formstrap/fields/_list.html.erb +0 -31
- data/app/views/formstrap/fields/_text.html.erb +0 -17
- data/app/views/formstrap/media/_media_item_modal.html.erb +0 -77
- data/app/views/formstrap/media/show.html.erb +0 -9
- data/app/views/formstrap/media/update.turbo_stream.erb +0 -3
- data/config/locales/activerecord/en.yml +0 -12
- data/config/locales/activerecord/nl.yml +0 -13
- data/config/locales/defaults/en.yml +0 -215
- data/config/locales/defaults/nl.yml +0 -213
- data/config/locales/devise/en.yml +0 -65
- data/config/locales/devise/nl.yml +0 -85
- /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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07cc257d4684759a070f1d259212a1e0c10a0edf4f0c9f9dd275b5ef0a3708be
|
4
|
+
data.tar.gz: 626e3625647a1d0e897f03e3c4ed712eb847e20761a112cc8b3dff667d82d072
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16262a9223b70aeb5f6e5ae830e124fd329226fd794c070e2b8afb5ebe1ce2f260c944da356fd8bae5246c1f931bc1b09652dab85f1c9ed8a16335b55978a678
|
7
|
+
data.tar.gz: 4d0d78db261a85d8cba1dd449b24f206ecc0aac2b3f3287e25bdc4f6c14b57a764bb17322ec7d8523279ee31135cf20fe0aa312182e894398135243a57afe1e4
|
data/CHANGELOG.md
CHANGED
@@ -1 +1,38 @@
|
|
1
|
-
#
|
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('.
|
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('.
|
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('.
|
184
|
+
thumbnail.querySelector('.formstrap-thumbnail-bg').innerHTML = ''
|
185
185
|
}
|
186
186
|
|
187
187
|
updateThumbnailIcon (thumbnail, icon) {
|
188
|
-
thumbnail.querySelector('.
|
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}
|
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
|
-
|
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('.
|
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('.
|
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)
|