formstrap 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +33 -0
- data/CHANGELOG.md +1 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +28 -0
- data/LICENSE.txt +21 -0
- data/README.md +118 -0
- data/Rakefile +10 -0
- data/app/assets/config/headmin_manifest.js +2 -0
- data/app/assets/images/avatar.jpg +0 -0
- data/app/assets/images/document.docx +0 -0
- data/app/assets/images/document.pdf +0 -0
- data/app/assets/images/image.jpg +0 -0
- data/app/assets/images/spreadsheet.xls +0 -0
- data/app/assets/images/video.mp4 +0 -0
- data/app/assets/javascripts/formstrap/config/i18n.js +11 -0
- data/app/assets/javascripts/formstrap/controllers/autocomplete_controller.js +318 -0
- data/app/assets/javascripts/formstrap/controllers/date_range_controller.js +38 -0
- data/app/assets/javascripts/formstrap/controllers/dropzone_controller.js +31 -0
- data/app/assets/javascripts/formstrap/controllers/file_preview_controller.js +244 -0
- data/app/assets/javascripts/formstrap/controllers/flatpickr_controller.js +35 -0
- data/app/assets/javascripts/formstrap/controllers/infinite_scroller_controller.js +28 -0
- data/app/assets/javascripts/formstrap/controllers/media_controller.js +252 -0
- data/app/assets/javascripts/formstrap/controllers/media_modal_controller.js +147 -0
- data/app/assets/javascripts/formstrap/controllers/redactorx_controller.js +40 -0
- data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +148 -0
- data/app/assets/javascripts/formstrap/controllers/select_controller.js +49 -0
- data/app/assets/javascripts/formstrap/controllers/textarea_controller.js +48 -0
- data/app/assets/javascripts/formstrap/index.js +32 -0
- data/app/assets/javascripts/formstrap.js +11515 -0
- data/app/assets/stylesheets/formstrap/forms/autocomplete.scss +27 -0
- data/app/assets/stylesheets/formstrap/forms/file.scss +83 -0
- data/app/assets/stylesheets/formstrap/forms/media.scss +10 -0
- data/app/assets/stylesheets/formstrap/forms/repeater.scss +62 -0
- data/app/assets/stylesheets/formstrap/forms/search.scss +12 -0
- data/app/assets/stylesheets/formstrap/forms.scss +12 -0
- data/app/assets/stylesheets/formstrap/general.scss +18 -0
- data/app/assets/stylesheets/formstrap/media/index.scss +9 -0
- data/app/assets/stylesheets/formstrap/media.scss +1 -0
- data/app/assets/stylesheets/formstrap/utilities/buttons.scss +27 -0
- data/app/assets/stylesheets/formstrap/utilities/dropzone.scss +72 -0
- data/app/assets/stylesheets/formstrap/utilities.scss +2 -0
- data/app/assets/stylesheets/formstrap/vendor/flatpickr.css +903 -0
- data/app/assets/stylesheets/formstrap/vendor/tom-select-bootstrap.scss +535 -0
- data/app/assets/stylesheets/formstrap.css +1559 -0
- data/app/assets/stylesheets/formstrap.scss +11 -0
- data/app/controllers/concerns/formstrap/pagination.rb +27 -0
- data/app/controllers/formstrap/media_controller.rb +68 -0
- data/app/controllers/formstrap_controller.rb +2 -0
- data/app/models/concerns/formstrap/autocompletable.rb +36 -0
- data/app/models/concerns/formstrap/hintable.rb +22 -0
- data/app/models/concerns/formstrap/input_groupable.rb +21 -0
- data/app/models/concerns/formstrap/labelable.rb +31 -0
- data/app/models/concerns/formstrap/listable.rb +26 -0
- data/app/models/concerns/formstrap/placeholderable.rb +11 -0
- data/app/models/concerns/formstrap/validatable.rb +38 -0
- data/app/models/concerns/formstrap/wrappable.rb +19 -0
- data/app/models/formstrap/.DS_Store +0 -0
- data/app/models/formstrap/association_view.rb +100 -0
- data/app/models/formstrap/blocks_view.rb +43 -0
- data/app/models/formstrap/checkbox_view.rb +50 -0
- data/app/models/formstrap/color_view.rb +45 -0
- data/app/models/formstrap/date_range_view.rb +23 -0
- data/app/models/formstrap/date_view.rb +43 -0
- data/app/models/formstrap/datetime_range_view.rb +23 -0
- data/app/models/formstrap/datetime_view.rb +43 -0
- data/app/models/formstrap/email_view.rb +46 -0
- data/app/models/formstrap/file_view.rb +106 -0
- data/app/models/formstrap/flatpickr_range_view.rb +89 -0
- data/app/models/formstrap/flatpickr_view.rb +27 -0
- data/app/models/formstrap/hidden_view.rb +8 -0
- data/app/models/formstrap/hint_view.rb +4 -0
- data/app/models/formstrap/input_group_view.rb +17 -0
- data/app/models/formstrap/label_view.rb +22 -0
- data/app/models/formstrap/media_item_view.rb +41 -0
- data/app/models/formstrap/media_view.rb +143 -0
- data/app/models/formstrap/number_view.rb +47 -0
- data/app/models/formstrap/password_view.rb +42 -0
- data/app/models/formstrap/redactorx_view.rb +57 -0
- data/app/models/formstrap/search_view.rb +46 -0
- data/app/models/formstrap/select_view.rb +61 -0
- data/app/models/formstrap/switch_view.rb +21 -0
- data/app/models/formstrap/text_view.rb +46 -0
- data/app/models/formstrap/textarea_view.rb +47 -0
- data/app/models/formstrap/url_view.rb +46 -0
- data/app/models/formstrap/wrapper_view.rb +17 -0
- data/app/models/formstrap/wysiwyg_view.rb +15 -0
- data/app/models/view_model.rb +62 -0
- data/app/views/formstrap/_association.html.erb +30 -0
- data/app/views/formstrap/_autocomplete.html.erb +11 -0
- data/app/views/formstrap/_blocks.html.erb +45 -0
- data/app/views/formstrap/_checkbox.html.erb +34 -0
- data/app/views/formstrap/_color.html.erb +32 -0
- data/app/views/formstrap/_datalist.html.erb +3 -0
- data/app/views/formstrap/_date.html.erb +41 -0
- data/app/views/formstrap/_date_range.html.erb +40 -0
- data/app/views/formstrap/_datetime.html.erb +41 -0
- data/app/views/formstrap/_datetime_range.html.erb +40 -0
- data/app/views/formstrap/_email.html.erb +43 -0
- data/app/views/formstrap/_errors.html.erb +19 -0
- data/app/views/formstrap/_file.html.erb +94 -0
- data/app/views/formstrap/_flatpickr.html.erb +33 -0
- data/app/views/formstrap/_flatpickr_range.html.erb +40 -0
- data/app/views/formstrap/_hidden.html.erb +23 -0
- data/app/views/formstrap/_hint.html.erb +21 -0
- data/app/views/formstrap/_input_group.html.erb +21 -0
- data/app/views/formstrap/_label.html.erb +22 -0
- data/app/views/formstrap/_media.html.erb +60 -0
- data/app/views/formstrap/_number.html.erb +41 -0
- data/app/views/formstrap/_password.html.erb +39 -0
- data/app/views/formstrap/_redactorx.html.erb +31 -0
- data/app/views/formstrap/_repeater.html.erb +128 -0
- data/app/views/formstrap/_search.html.erb +43 -0
- data/app/views/formstrap/_select.html.erb +43 -0
- data/app/views/formstrap/_switch.html.erb +29 -0
- data/app/views/formstrap/_text.html.erb +42 -0
- data/app/views/formstrap/_textarea.html.erb +39 -0
- data/app/views/formstrap/_to_ary.html.erb +0 -0
- data/app/views/formstrap/_url.html.erb +43 -0
- data/app/views/formstrap/_validation.html.erb +18 -0
- data/app/views/formstrap/_wrapper.html.erb +8 -0
- data/app/views/formstrap/_wysiwyg.html.erb +28 -0
- data/app/views/formstrap/autocomplete/_item.html.erb +3 -0
- data/app/views/formstrap/autocomplete/_list.html.erb +3 -0
- data/app/views/formstrap/blocks/_modal.html.erb +20 -0
- data/app/views/formstrap/fields/_base.html.erb +25 -0
- data/app/views/formstrap/fields/_file.html.erb +17 -0
- data/app/views/formstrap/fields/_files.html.erb +17 -0
- data/app/views/formstrap/fields/_group.html.erb +52 -0
- data/app/views/formstrap/fields/_list.html.erb +31 -0
- data/app/views/formstrap/fields/_text.html.erb +17 -0
- data/app/views/formstrap/media/_item.html.erb +38 -0
- data/app/views/formstrap/media/_media_item_modal.html.erb +77 -0
- data/app/views/formstrap/media/_modal.html.erb +40 -0
- data/app/views/formstrap/media/_thumbnail.html.erb +20 -0
- data/app/views/formstrap/media/_validation.html.erb +10 -0
- data/app/views/formstrap/media/create.turbo_stream.erb +5 -0
- data/app/views/formstrap/media/index.html.erb +3 -0
- data/app/views/formstrap/media/index.turbo_stream.erb +11 -0
- data/app/views/formstrap/media/show.html.erb +9 -0
- data/app/views/formstrap/media/thumbnail.html.erb +3 -0
- data/app/views/formstrap/media/update.turbo_stream.erb +3 -0
- data/app/views/formstrap/pagination/_infinite.html.erb +7 -0
- data/app/views/formstrap/repeater/_row.html.erb +53 -0
- data/app/views/formstrap/shared/_notifications.html.erb +20 -0
- data/app/views/formstrap/shared/_popup.html.erb +32 -0
- data/app/views/formstrap/shared/_thumbnail.html.erb +35 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/importmap.rb +2 -0
- data/config/locales/activerecord/en.yml +12 -0
- data/config/locales/activerecord/nl.yml +13 -0
- data/config/locales/defaults/en.yml +215 -0
- data/config/locales/defaults/nl.yml +213 -0
- data/config/locales/devise/en.yml +65 -0
- data/config/locales/devise/nl.yml +85 -0
- data/config/locales/en.yml +6 -0
- data/config/locales/formstrap/forms/en.yml +39 -0
- data/config/locales/formstrap/forms/nl.yml +39 -0
- data/config/locales/formstrap/media/en.yml +24 -0
- data/config/locales/formstrap/media/nl.yml +24 -0
- data/config/locales/formstrap/thumbnail/en.yml +4 -0
- data/config/locales/formstrap/thumbnail/nl.yml +4 -0
- data/config/locales/nl.yml +6 -0
- data/config/routes.rb +11 -0
- data/esbuild-css.js +25 -0
- data/esbuild-js.js +11 -0
- data/formstrap.gemspec +37 -0
- data/formstrap.iml +34 -0
- data/lib/formstrap/engine.rb +27 -0
- data/lib/formstrap/form_builder.rb +177 -0
- data/lib/formstrap/form_helper.rb +19 -0
- data/lib/formstrap/version.rb +3 -0
- data/lib/formstrap.rb +6 -0
- data/package.json +54 -0
- data/src/js/formstrap.js +1 -0
- data/src/scss/formstrap.scss +1 -0
- data/yarn.lock +1998 -0
- metadata +224 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
/* global CustomEvent */
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
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
|
+
const 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
|
+
}
|
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 blobId = `#blob_${id}`
|
96
|
+
const element = this.element.querySelector(blobId)
|
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
|
+
}
|
147
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
/* global RedactorX */
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
connect () {
|
6
|
+
this.initRedactor()
|
7
|
+
}
|
8
|
+
|
9
|
+
initRedactor () {
|
10
|
+
if (typeof RedactorX === 'undefined') {
|
11
|
+
console.error('RedactorX is a paid module and is not included in Headmin. Please purchase it and import it as a JS module')
|
12
|
+
return false
|
13
|
+
}
|
14
|
+
|
15
|
+
const defaultOptions = {
|
16
|
+
editor: {
|
17
|
+
minHeight: '57px'
|
18
|
+
},
|
19
|
+
subscribe: {
|
20
|
+
'app.start': () => {
|
21
|
+
this.stylize()
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
const options = JSON.parse(this.element.getAttribute('data-redactor-options'))
|
26
|
+
RedactorX(this.element, { ...defaultOptions, ...options })
|
27
|
+
}
|
28
|
+
|
29
|
+
stylize () {
|
30
|
+
const container = this.element.nextElementSibling
|
31
|
+
|
32
|
+
// Copy textarea classes
|
33
|
+
const inputClasses = this.element.classList
|
34
|
+
container.classList.add(...inputClasses)
|
35
|
+
|
36
|
+
// Add top margin to input group
|
37
|
+
// const inputGroup = container.closest('.input-group')
|
38
|
+
// inputGroup.classList.add('mt-4')
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
import Sortable from 'sortablejs'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static get values () {
|
6
|
+
return {
|
7
|
+
id: String
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
static get targets () {
|
12
|
+
return ['repeater', 'footer', 'template', 'row', 'list', 'empty', 'addButton']
|
13
|
+
}
|
14
|
+
|
15
|
+
connect () {
|
16
|
+
Sortable.create(this.listTarget, {
|
17
|
+
animation: 150,
|
18
|
+
ghostClass: 'list-group-item-dark',
|
19
|
+
draggable: '.repeater-row',
|
20
|
+
handle: '.repeater-row-handle',
|
21
|
+
onEnd: () => {
|
22
|
+
this.resetIndices()
|
23
|
+
this.resetPositions()
|
24
|
+
}
|
25
|
+
})
|
26
|
+
|
27
|
+
this.toggleEmpty()
|
28
|
+
}
|
29
|
+
|
30
|
+
resetButtonIndices (event) {
|
31
|
+
const row = event.target.closest('.repeater-row')
|
32
|
+
const index = this.containsRow(row) ? row.dataset.rowIndex : ''
|
33
|
+
this.updatePopupButtonIndices(index)
|
34
|
+
}
|
35
|
+
|
36
|
+
containsRow (row) {
|
37
|
+
return this.rowTargets.includes(row)
|
38
|
+
}
|
39
|
+
|
40
|
+
updatePopupButtonIndices (index) {
|
41
|
+
const popup = document.querySelector(`[data-popup-target="popup"][data-popup-id="repeater-buttons-${this.idValue}"]`)
|
42
|
+
const buttons = popup.querySelectorAll('a')
|
43
|
+
buttons.forEach((button) => {
|
44
|
+
button.dataset.rowIndex = index
|
45
|
+
})
|
46
|
+
}
|
47
|
+
|
48
|
+
addRow (event) {
|
49
|
+
event.preventDefault()
|
50
|
+
const button = event.target
|
51
|
+
const templateName = button.dataset.templateName
|
52
|
+
const rowIndex = button.dataset.rowIndex
|
53
|
+
|
54
|
+
// Prepare html from template
|
55
|
+
const template = this.getTemplate(templateName)
|
56
|
+
const html = this.replaceIdsWithTimestamps(template)
|
57
|
+
|
58
|
+
// Fallback to last row if no index is set
|
59
|
+
if (rowIndex) {
|
60
|
+
// Insert new row after defined row
|
61
|
+
const row = this.rowTargets[rowIndex]
|
62
|
+
row.insertAdjacentHTML('afterend', html)
|
63
|
+
} else {
|
64
|
+
// Insert before footer
|
65
|
+
this.footerTarget.insertAdjacentHTML('beforebegin', html)
|
66
|
+
}
|
67
|
+
|
68
|
+
this.resetIndices()
|
69
|
+
this.resetPositions()
|
70
|
+
this.toggleEmpty()
|
71
|
+
}
|
72
|
+
|
73
|
+
removeRow (event) {
|
74
|
+
event.preventDefault()
|
75
|
+
|
76
|
+
const row = event.target.closest('.repeater-row')
|
77
|
+
|
78
|
+
if (row.dataset.newRecord === 'true') {
|
79
|
+
// New records are simply removed from the page
|
80
|
+
row.remove()
|
81
|
+
} else {
|
82
|
+
// Existing records are hidden and flagged for deletion
|
83
|
+
this.flagRowForDeletion(row)
|
84
|
+
row.remove()
|
85
|
+
}
|
86
|
+
|
87
|
+
this.resetIndices()
|
88
|
+
this.resetPositions()
|
89
|
+
this.toggleEmpty()
|
90
|
+
}
|
91
|
+
|
92
|
+
flagRowForDeletion (row) {
|
93
|
+
const destroyInput = row.querySelector('input[name*=\'_destroy\']')
|
94
|
+
const idInput = row.querySelector('input[name*=\'[id]\']')
|
95
|
+
|
96
|
+
// Update _destroy value
|
97
|
+
destroyInput.value = 1
|
98
|
+
|
99
|
+
// Move away from row
|
100
|
+
this.listTarget.parentNode.appendChild(destroyInput)
|
101
|
+
this.listTarget.parentNode.appendChild(idInput)
|
102
|
+
}
|
103
|
+
|
104
|
+
getTemplate (name) {
|
105
|
+
return this.templateTargets.filter((template) => {
|
106
|
+
return template.dataset.templateName === name
|
107
|
+
})[0]
|
108
|
+
}
|
109
|
+
|
110
|
+
replaceIdsWithTimestamps (template) {
|
111
|
+
const regex = new RegExp(template.dataset.templateIdRegex, 'g')
|
112
|
+
return template.innerHTML.replace(regex, new Date().getTime())
|
113
|
+
}
|
114
|
+
|
115
|
+
visibleRowsCount () {
|
116
|
+
return this.visibleRows().length
|
117
|
+
}
|
118
|
+
|
119
|
+
visibleRows () {
|
120
|
+
const rows = this.rowTargets
|
121
|
+
return rows.filter((row) => {
|
122
|
+
return row.querySelector('input[name*=\'_destroy\']').value !== '1'
|
123
|
+
})
|
124
|
+
}
|
125
|
+
|
126
|
+
toggleEmpty () {
|
127
|
+
if (this.visibleRowsCount() > 0) {
|
128
|
+
this.emptyTarget.classList.add('invisible')
|
129
|
+
} else {
|
130
|
+
this.emptyTarget.classList.remove('invisible')
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
resetPositions () {
|
135
|
+
this.visibleRows().forEach((row, index) => {
|
136
|
+
const positionInput = row.querySelector('input[name*=\'position\']')
|
137
|
+
if (positionInput) {
|
138
|
+
positionInput.value = index
|
139
|
+
}
|
140
|
+
})
|
141
|
+
}
|
142
|
+
|
143
|
+
resetIndices () {
|
144
|
+
this.visibleRows().forEach((row, index) => {
|
145
|
+
row.dataset.rowIndex = index
|
146
|
+
})
|
147
|
+
}
|
148
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
import TomSelect from 'tom-select'
|
3
|
+
import I18n from '../config/i18n'
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
connect () {
|
7
|
+
if (this.element.hasAttribute('multiple')) {
|
8
|
+
this.initTomSelect()
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
defaultOptions (locale) {
|
13
|
+
const defaultOptions = {
|
14
|
+
en: {
|
15
|
+
render: {
|
16
|
+
option_create: function (data, escape) {
|
17
|
+
return '<div class="create">Add <strong>' + escape(data.input) + '</strong>…</div>'
|
18
|
+
},
|
19
|
+
no_results: function (data, escape) {
|
20
|
+
return '<div class="no-results">No results found</div>'
|
21
|
+
}
|
22
|
+
}
|
23
|
+
},
|
24
|
+
nl: {
|
25
|
+
render: {
|
26
|
+
option_create: function (data, escape) {
|
27
|
+
return '<div class="create">Voeg <strong>' + escape(data.input) + '</strong> toe …</div>'
|
28
|
+
},
|
29
|
+
no_results: function (data, escape) {
|
30
|
+
return '<div class="no-results">Geen resultaten gevonden</div>'
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
return defaultOptions[locale]
|
36
|
+
}
|
37
|
+
|
38
|
+
hasTags () {
|
39
|
+
return this.element.dataset.tags === 'true'
|
40
|
+
}
|
41
|
+
|
42
|
+
initTomSelect () {
|
43
|
+
const defaultOptions = this.defaultOptions(I18n.locale)
|
44
|
+
const options = { create: this.hasTags() }
|
45
|
+
|
46
|
+
/* eslint-disable no-new */
|
47
|
+
new TomSelect(this.element, { ...defaultOptions, ...options })
|
48
|
+
}
|
49
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
/* global IntersectionObserver */
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static get targets () {
|
6
|
+
return ['textarea', 'count']
|
7
|
+
}
|
8
|
+
|
9
|
+
connect () {
|
10
|
+
onVisible(this.textareaTarget, () => { this.update() })
|
11
|
+
}
|
12
|
+
|
13
|
+
update () {
|
14
|
+
this.resize()
|
15
|
+
this.updateCount()
|
16
|
+
}
|
17
|
+
|
18
|
+
resize () {
|
19
|
+
this.textareaTarget.style.height = 'auto'
|
20
|
+
this.textareaTarget.setAttribute('style', 'height:' + (this.textareaTarget.scrollHeight) + 'px;overflow-y:hidden;')
|
21
|
+
}
|
22
|
+
|
23
|
+
updateCount () {
|
24
|
+
if (this.textareaTarget.getAttribute('maxlength')) {
|
25
|
+
this.updateCountLength()
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
updateCountLength () {
|
30
|
+
const currentLength = this.textareaTarget.value.length
|
31
|
+
const maximumLength = this.textareaTarget.getAttribute('maxlength')
|
32
|
+
|
33
|
+
this.countTarget.textContent = `${currentLength}/${maximumLength}`
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
// Custom callback event that triggers when an element becomes visible.
|
38
|
+
// Solves the bug where textarea fields are not properly sized when they (or their parent) or hidden.
|
39
|
+
function onVisible (element, callback) {
|
40
|
+
new IntersectionObserver((entries, observer) => {
|
41
|
+
entries.forEach(entry => {
|
42
|
+
if (entry.intersectionRatio > 0) {
|
43
|
+
callback(element)
|
44
|
+
observer.disconnect()
|
45
|
+
}
|
46
|
+
})
|
47
|
+
}).observe(element)
|
48
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/* global Stimulus */
|
2
|
+
import { Application } from '@hotwired/stimulus'
|
3
|
+
import AutocompleteController from './controllers/autocomplete_controller'
|
4
|
+
import DateRangeController from './controllers/date_range_controller'
|
5
|
+
import DropzoneController from './controllers/dropzone_controller'
|
6
|
+
import FilePreviewController from './controllers/file_preview_controller'
|
7
|
+
import FlatpickrController from './controllers/flatpickr_controller'
|
8
|
+
import InfiniteScrollerController from './controllers/infinite_scroller_controller'
|
9
|
+
import MediaController from './controllers/media_controller'
|
10
|
+
import MediaModalController from './controllers/media_modal_controller'
|
11
|
+
import RedactorxController from './controllers/redactorx_controller'
|
12
|
+
import RepeaterController from './controllers/repeater_controller'
|
13
|
+
import SelectController from './controllers/select_controller'
|
14
|
+
import TextareaController from './controllers/textarea_controller'
|
15
|
+
|
16
|
+
export class Formstrap {
|
17
|
+
static start () {
|
18
|
+
window.Stimulus = window.Stimulus || Application.start()
|
19
|
+
Stimulus.register('autocomplete', AutocompleteController)
|
20
|
+
Stimulus.register('date-range', DateRangeController)
|
21
|
+
Stimulus.register('dropzone', DropzoneController)
|
22
|
+
Stimulus.register('file-preview', FilePreviewController)
|
23
|
+
Stimulus.register('flatpickr', FlatpickrController)
|
24
|
+
Stimulus.register('infinite-scroller', InfiniteScrollerController)
|
25
|
+
Stimulus.register('media', MediaController)
|
26
|
+
Stimulus.register('media-modal', MediaModalController)
|
27
|
+
Stimulus.register('redactorx', RedactorxController)
|
28
|
+
Stimulus.register('repeater', RepeaterController)
|
29
|
+
Stimulus.register('select', SelectController)
|
30
|
+
Stimulus.register('textarea', TextareaController)
|
31
|
+
}
|
32
|
+
}
|