formstrap 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|