formstrap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +33 -0
  4. data/CHANGELOG.md +1 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +28 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +118 -0
  9. data/Rakefile +10 -0
  10. data/app/assets/config/headmin_manifest.js +2 -0
  11. data/app/assets/images/avatar.jpg +0 -0
  12. data/app/assets/images/document.docx +0 -0
  13. data/app/assets/images/document.pdf +0 -0
  14. data/app/assets/images/image.jpg +0 -0
  15. data/app/assets/images/spreadsheet.xls +0 -0
  16. data/app/assets/images/video.mp4 +0 -0
  17. data/app/assets/javascripts/formstrap/config/i18n.js +11 -0
  18. data/app/assets/javascripts/formstrap/controllers/autocomplete_controller.js +318 -0
  19. data/app/assets/javascripts/formstrap/controllers/date_range_controller.js +38 -0
  20. data/app/assets/javascripts/formstrap/controllers/dropzone_controller.js +31 -0
  21. data/app/assets/javascripts/formstrap/controllers/file_preview_controller.js +244 -0
  22. data/app/assets/javascripts/formstrap/controllers/flatpickr_controller.js +35 -0
  23. data/app/assets/javascripts/formstrap/controllers/infinite_scroller_controller.js +28 -0
  24. data/app/assets/javascripts/formstrap/controllers/media_controller.js +252 -0
  25. data/app/assets/javascripts/formstrap/controllers/media_modal_controller.js +147 -0
  26. data/app/assets/javascripts/formstrap/controllers/redactorx_controller.js +40 -0
  27. data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +148 -0
  28. data/app/assets/javascripts/formstrap/controllers/select_controller.js +49 -0
  29. data/app/assets/javascripts/formstrap/controllers/textarea_controller.js +48 -0
  30. data/app/assets/javascripts/formstrap/index.js +32 -0
  31. data/app/assets/javascripts/formstrap.js +11515 -0
  32. data/app/assets/stylesheets/formstrap/forms/autocomplete.scss +27 -0
  33. data/app/assets/stylesheets/formstrap/forms/file.scss +83 -0
  34. data/app/assets/stylesheets/formstrap/forms/media.scss +10 -0
  35. data/app/assets/stylesheets/formstrap/forms/repeater.scss +62 -0
  36. data/app/assets/stylesheets/formstrap/forms/search.scss +12 -0
  37. data/app/assets/stylesheets/formstrap/forms.scss +12 -0
  38. data/app/assets/stylesheets/formstrap/general.scss +18 -0
  39. data/app/assets/stylesheets/formstrap/media/index.scss +9 -0
  40. data/app/assets/stylesheets/formstrap/media.scss +1 -0
  41. data/app/assets/stylesheets/formstrap/utilities/buttons.scss +27 -0
  42. data/app/assets/stylesheets/formstrap/utilities/dropzone.scss +72 -0
  43. data/app/assets/stylesheets/formstrap/utilities.scss +2 -0
  44. data/app/assets/stylesheets/formstrap/vendor/flatpickr.css +903 -0
  45. data/app/assets/stylesheets/formstrap/vendor/tom-select-bootstrap.scss +535 -0
  46. data/app/assets/stylesheets/formstrap.css +1559 -0
  47. data/app/assets/stylesheets/formstrap.scss +11 -0
  48. data/app/controllers/concerns/formstrap/pagination.rb +27 -0
  49. data/app/controllers/formstrap/media_controller.rb +68 -0
  50. data/app/controllers/formstrap_controller.rb +2 -0
  51. data/app/models/concerns/formstrap/autocompletable.rb +36 -0
  52. data/app/models/concerns/formstrap/hintable.rb +22 -0
  53. data/app/models/concerns/formstrap/input_groupable.rb +21 -0
  54. data/app/models/concerns/formstrap/labelable.rb +31 -0
  55. data/app/models/concerns/formstrap/listable.rb +26 -0
  56. data/app/models/concerns/formstrap/placeholderable.rb +11 -0
  57. data/app/models/concerns/formstrap/validatable.rb +38 -0
  58. data/app/models/concerns/formstrap/wrappable.rb +19 -0
  59. data/app/models/formstrap/.DS_Store +0 -0
  60. data/app/models/formstrap/association_view.rb +100 -0
  61. data/app/models/formstrap/blocks_view.rb +43 -0
  62. data/app/models/formstrap/checkbox_view.rb +50 -0
  63. data/app/models/formstrap/color_view.rb +45 -0
  64. data/app/models/formstrap/date_range_view.rb +23 -0
  65. data/app/models/formstrap/date_view.rb +43 -0
  66. data/app/models/formstrap/datetime_range_view.rb +23 -0
  67. data/app/models/formstrap/datetime_view.rb +43 -0
  68. data/app/models/formstrap/email_view.rb +46 -0
  69. data/app/models/formstrap/file_view.rb +106 -0
  70. data/app/models/formstrap/flatpickr_range_view.rb +89 -0
  71. data/app/models/formstrap/flatpickr_view.rb +27 -0
  72. data/app/models/formstrap/hidden_view.rb +8 -0
  73. data/app/models/formstrap/hint_view.rb +4 -0
  74. data/app/models/formstrap/input_group_view.rb +17 -0
  75. data/app/models/formstrap/label_view.rb +22 -0
  76. data/app/models/formstrap/media_item_view.rb +41 -0
  77. data/app/models/formstrap/media_view.rb +143 -0
  78. data/app/models/formstrap/number_view.rb +47 -0
  79. data/app/models/formstrap/password_view.rb +42 -0
  80. data/app/models/formstrap/redactorx_view.rb +57 -0
  81. data/app/models/formstrap/search_view.rb +46 -0
  82. data/app/models/formstrap/select_view.rb +61 -0
  83. data/app/models/formstrap/switch_view.rb +21 -0
  84. data/app/models/formstrap/text_view.rb +46 -0
  85. data/app/models/formstrap/textarea_view.rb +47 -0
  86. data/app/models/formstrap/url_view.rb +46 -0
  87. data/app/models/formstrap/wrapper_view.rb +17 -0
  88. data/app/models/formstrap/wysiwyg_view.rb +15 -0
  89. data/app/models/view_model.rb +62 -0
  90. data/app/views/formstrap/_association.html.erb +30 -0
  91. data/app/views/formstrap/_autocomplete.html.erb +11 -0
  92. data/app/views/formstrap/_blocks.html.erb +45 -0
  93. data/app/views/formstrap/_checkbox.html.erb +34 -0
  94. data/app/views/formstrap/_color.html.erb +32 -0
  95. data/app/views/formstrap/_datalist.html.erb +3 -0
  96. data/app/views/formstrap/_date.html.erb +41 -0
  97. data/app/views/formstrap/_date_range.html.erb +40 -0
  98. data/app/views/formstrap/_datetime.html.erb +41 -0
  99. data/app/views/formstrap/_datetime_range.html.erb +40 -0
  100. data/app/views/formstrap/_email.html.erb +43 -0
  101. data/app/views/formstrap/_errors.html.erb +19 -0
  102. data/app/views/formstrap/_file.html.erb +94 -0
  103. data/app/views/formstrap/_flatpickr.html.erb +33 -0
  104. data/app/views/formstrap/_flatpickr_range.html.erb +40 -0
  105. data/app/views/formstrap/_hidden.html.erb +23 -0
  106. data/app/views/formstrap/_hint.html.erb +21 -0
  107. data/app/views/formstrap/_input_group.html.erb +21 -0
  108. data/app/views/formstrap/_label.html.erb +22 -0
  109. data/app/views/formstrap/_media.html.erb +60 -0
  110. data/app/views/formstrap/_number.html.erb +41 -0
  111. data/app/views/formstrap/_password.html.erb +39 -0
  112. data/app/views/formstrap/_redactorx.html.erb +31 -0
  113. data/app/views/formstrap/_repeater.html.erb +128 -0
  114. data/app/views/formstrap/_search.html.erb +43 -0
  115. data/app/views/formstrap/_select.html.erb +43 -0
  116. data/app/views/formstrap/_switch.html.erb +29 -0
  117. data/app/views/formstrap/_text.html.erb +42 -0
  118. data/app/views/formstrap/_textarea.html.erb +39 -0
  119. data/app/views/formstrap/_to_ary.html.erb +0 -0
  120. data/app/views/formstrap/_url.html.erb +43 -0
  121. data/app/views/formstrap/_validation.html.erb +18 -0
  122. data/app/views/formstrap/_wrapper.html.erb +8 -0
  123. data/app/views/formstrap/_wysiwyg.html.erb +28 -0
  124. data/app/views/formstrap/autocomplete/_item.html.erb +3 -0
  125. data/app/views/formstrap/autocomplete/_list.html.erb +3 -0
  126. data/app/views/formstrap/blocks/_modal.html.erb +20 -0
  127. data/app/views/formstrap/fields/_base.html.erb +25 -0
  128. data/app/views/formstrap/fields/_file.html.erb +17 -0
  129. data/app/views/formstrap/fields/_files.html.erb +17 -0
  130. data/app/views/formstrap/fields/_group.html.erb +52 -0
  131. data/app/views/formstrap/fields/_list.html.erb +31 -0
  132. data/app/views/formstrap/fields/_text.html.erb +17 -0
  133. data/app/views/formstrap/media/_item.html.erb +38 -0
  134. data/app/views/formstrap/media/_media_item_modal.html.erb +77 -0
  135. data/app/views/formstrap/media/_modal.html.erb +40 -0
  136. data/app/views/formstrap/media/_thumbnail.html.erb +20 -0
  137. data/app/views/formstrap/media/_validation.html.erb +10 -0
  138. data/app/views/formstrap/media/create.turbo_stream.erb +5 -0
  139. data/app/views/formstrap/media/index.html.erb +3 -0
  140. data/app/views/formstrap/media/index.turbo_stream.erb +11 -0
  141. data/app/views/formstrap/media/show.html.erb +9 -0
  142. data/app/views/formstrap/media/thumbnail.html.erb +3 -0
  143. data/app/views/formstrap/media/update.turbo_stream.erb +3 -0
  144. data/app/views/formstrap/pagination/_infinite.html.erb +7 -0
  145. data/app/views/formstrap/repeater/_row.html.erb +53 -0
  146. data/app/views/formstrap/shared/_notifications.html.erb +20 -0
  147. data/app/views/formstrap/shared/_popup.html.erb +32 -0
  148. data/app/views/formstrap/shared/_thumbnail.html.erb +35 -0
  149. data/bin/console +14 -0
  150. data/bin/setup +8 -0
  151. data/config/importmap.rb +2 -0
  152. data/config/locales/activerecord/en.yml +12 -0
  153. data/config/locales/activerecord/nl.yml +13 -0
  154. data/config/locales/defaults/en.yml +215 -0
  155. data/config/locales/defaults/nl.yml +213 -0
  156. data/config/locales/devise/en.yml +65 -0
  157. data/config/locales/devise/nl.yml +85 -0
  158. data/config/locales/en.yml +6 -0
  159. data/config/locales/formstrap/forms/en.yml +39 -0
  160. data/config/locales/formstrap/forms/nl.yml +39 -0
  161. data/config/locales/formstrap/media/en.yml +24 -0
  162. data/config/locales/formstrap/media/nl.yml +24 -0
  163. data/config/locales/formstrap/thumbnail/en.yml +4 -0
  164. data/config/locales/formstrap/thumbnail/nl.yml +4 -0
  165. data/config/locales/nl.yml +6 -0
  166. data/config/routes.rb +11 -0
  167. data/esbuild-css.js +25 -0
  168. data/esbuild-js.js +11 -0
  169. data/formstrap.gemspec +37 -0
  170. data/formstrap.iml +34 -0
  171. data/lib/formstrap/engine.rb +27 -0
  172. data/lib/formstrap/form_builder.rb +177 -0
  173. data/lib/formstrap/form_helper.rb +19 -0
  174. data/lib/formstrap/version.rb +3 -0
  175. data/lib/formstrap.rb +6 -0
  176. data/package.json +54 -0
  177. data/src/js/formstrap.js +1 -0
  178. data/src/scss/formstrap.scss +1 -0
  179. data/yarn.lock +1998 -0
  180. 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>&hellip;</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 &hellip;</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
+ }