formstrap 0.4.0 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/formstrap/controllers/media_controller.js +40 -20
- data/app/assets/javascripts/formstrap/controllers/nested_preview_controller.js +2 -1
- data/app/assets/javascripts/formstrap/controllers/repeater_controller.js +18 -39
- data/app/assets/javascripts/formstrap/controllers/select_controller.js +1 -1
- data/app/assets/javascripts/formstrap.js +50 -46
- data/app/assets/stylesheets/formstrap/vendor/overrides/redactor.scss +0 -1
- data/app/assets/stylesheets/formstrap.css +0 -1
- data/app/models/formstrap/media_view.rb +1 -2
- data/app/models/formstrap/redactor_view.rb +0 -2
- data/app/models/formstrap/wysiwyg_view.rb +5 -2
- data/app/views/formstrap/_repeater.html.erb +4 -3
- data/app/views/formstrap/repeater/_row.html.erb +1 -1
- data/lib/formstrap/version.rb +1 -1
- data/package.json +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee49a6e7a9f068d3403b4ebfd46c19cf07b6c293e8893f1e561e8ae20ed0af77
|
4
|
+
data.tar.gz: f85517d919e6c9360ac31c7f1aa7b8f2ff3705ea31a2a6a76f0b72ae82f58c30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab80f8fd25603f0c9ce6f5ea6c92dc025b62f7b932f0b0e7bd7867bd4432845f902a6f5f7ba6492a6455ae9f3eb93fe411497a9dc2cf346538e3a610a05fd253
|
7
|
+
data.tar.gz: a5db302d1730c5f0cb801ddf248d24a0c5111ec0f8ae6d7aaa24d5a36c0e50913f1939a71df275c47429016162b59e47024a4be0e186e23b661c3393321cb9e4
|
@@ -1,23 +1,22 @@
|
|
1
|
+
/* global crypto */
|
1
2
|
import { Controller } from '@hotwired/stimulus'
|
2
3
|
import Sortable from 'sortablejs'
|
3
4
|
|
4
5
|
export default class extends Controller {
|
6
|
+
static get values () {
|
7
|
+
return {
|
8
|
+
name: String
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
5
12
|
static get targets () {
|
6
13
|
return ['item', 'template', 'thumbnails', 'modalButton', 'placeholder', 'count', 'editButton', 'validationInput']
|
7
14
|
}
|
8
15
|
|
9
16
|
connect () {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
}
|
14
|
-
})
|
15
|
-
|
16
|
-
// Init sorting
|
17
|
-
if (this.hasSorting()) {
|
18
|
-
this.initSortable()
|
19
|
-
}
|
20
|
-
|
17
|
+
this.randomizeName()
|
18
|
+
this.listenForMediaSelection()
|
19
|
+
this.initializeSorting()
|
21
20
|
this.validate()
|
22
21
|
}
|
23
22
|
|
@@ -47,6 +46,34 @@ export default class extends Controller {
|
|
47
46
|
}
|
48
47
|
|
49
48
|
// Methods
|
49
|
+
randomizeName () {
|
50
|
+
this.nameValue = crypto.randomUUID().substring(0, 8)
|
51
|
+
this.updateModalButtonUrls()
|
52
|
+
}
|
53
|
+
|
54
|
+
updateModalButtonUrls () {
|
55
|
+
this.modalButtonTargets.forEach((button) => {
|
56
|
+
const url = new URL(button.getAttribute('href'))
|
57
|
+
url.searchParams.set('name', this.nameValue)
|
58
|
+
button.setAttribute('href', url.toString())
|
59
|
+
})
|
60
|
+
}
|
61
|
+
|
62
|
+
listenForMediaSelection () {
|
63
|
+
document.addEventListener('mediaSelectionSubmitted', (event) => {
|
64
|
+
if (event.detail.name === this.nameValue) {
|
65
|
+
this.selectItems(event.detail.items)
|
66
|
+
this.updateModalButtonUrls()
|
67
|
+
}
|
68
|
+
})
|
69
|
+
}
|
70
|
+
|
71
|
+
initializeSorting () {
|
72
|
+
if (this.hasSorting()) {
|
73
|
+
this.initSortable()
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
50
77
|
initSortable () {
|
51
78
|
Sortable.create(this.thumbnailsTarget, {
|
52
79
|
handle: '.media-drag-sort-handle',
|
@@ -170,9 +197,8 @@ export default class extends Controller {
|
|
170
197
|
|
171
198
|
createItem (item) {
|
172
199
|
// Copy template
|
173
|
-
const
|
174
|
-
|
175
|
-
this.thumbnailsTarget.insertAdjacentHTML('beforeend', html)
|
200
|
+
const templateHtml = this.templateTarget.innerHTML
|
201
|
+
this.thumbnailsTarget.insertAdjacentHTML('beforeend', templateHtml)
|
176
202
|
|
177
203
|
// Set new values
|
178
204
|
const newItem = this.itemTargets.pop()
|
@@ -191,12 +217,6 @@ export default class extends Controller {
|
|
191
217
|
oldThumbnail.parentNode.replaceChild(newThumbnail, oldThumbnail)
|
192
218
|
}
|
193
219
|
|
194
|
-
randomizeIds (template) {
|
195
|
-
const regex = new RegExp(template.dataset.templateIdRegex, 'g')
|
196
|
-
const randomNumber = Math.floor(100000000 + Math.random() * 900000000)
|
197
|
-
return template.innerHTML.replace(regex, randomNumber)
|
198
|
-
}
|
199
|
-
|
200
220
|
removeAllDeselectedItems (items) {
|
201
221
|
this.removeDeselectedItems(items, this.itemTargets)
|
202
222
|
}
|
@@ -126,7 +126,8 @@ export default class extends Controller {
|
|
126
126
|
const formData = new FormData()
|
127
127
|
|
128
128
|
// Replace all occurrences of "page[blocks_attributes][0]" with "block"
|
129
|
-
|
129
|
+
// Replace all occurrences of "form[fields_attributes][random]" with "field"
|
130
|
+
const regex = /\w+\[([^\]]+)s_attributes]\[[^\]]+]/g
|
130
131
|
const formElements = fields.querySelectorAll('input[name]:not([name$="[id]"]), select[name]:not([name$="[id]"]), textarea[name]:not([name$="[id]"]), button[name]:not([name$="[id]"])')
|
131
132
|
formElements.forEach((element) => {
|
132
133
|
const currentName = element.getAttribute('name')
|
@@ -1,3 +1,4 @@
|
|
1
|
+
/* global crypto */
|
1
2
|
import { Controller } from '@hotwired/stimulus'
|
2
3
|
import Sortable from 'sortablejs'
|
3
4
|
|
@@ -23,7 +24,6 @@ export default class extends Controller {
|
|
23
24
|
this.resetPositions()
|
24
25
|
}
|
25
26
|
})
|
26
|
-
|
27
27
|
this.toggleEmpty()
|
28
28
|
}
|
29
29
|
|
@@ -38,8 +38,7 @@ export default class extends Controller {
|
|
38
38
|
}
|
39
39
|
|
40
40
|
updatePopupButtonIndices (index) {
|
41
|
-
const
|
42
|
-
const buttons = popup.querySelectorAll('[data-popup-target="button"]')
|
41
|
+
const buttons = document.querySelectorAll(`[data-popup-target="button"][data-popup-id="repeater-buttons-${this.idValue}"]`)
|
43
42
|
buttons.forEach((button) => {
|
44
43
|
button.dataset.rowIndex = index
|
45
44
|
})
|
@@ -53,7 +52,7 @@ export default class extends Controller {
|
|
53
52
|
|
54
53
|
// Prepare html from template
|
55
54
|
let template = this.getTemplate(templateName).content.cloneNode(true)
|
56
|
-
template = this.
|
55
|
+
template = this.randomizeIds(template)
|
57
56
|
|
58
57
|
// Fallback to last row if no index is set
|
59
58
|
if (rowIndex) {
|
@@ -107,44 +106,24 @@ export default class extends Controller {
|
|
107
106
|
})[0]
|
108
107
|
}
|
109
108
|
|
110
|
-
|
111
|
-
const
|
112
|
-
const
|
109
|
+
randomizeIds (template) {
|
110
|
+
const randomNumber = crypto.randomUUID().substring(0, 8)
|
111
|
+
const pattern = `_${this.idValue}_`
|
113
112
|
const regex = new RegExp(pattern, 'g')
|
114
113
|
|
115
|
-
//
|
116
|
-
template.querySelectorAll(
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
node.innerHTML = node.innerHTML.replace(regex, replacement)
|
124
|
-
})
|
125
|
-
|
126
|
-
// Replace labels
|
127
|
-
template.querySelectorAll(`label[for*="${pattern}"]`).forEach((node) => {
|
128
|
-
const forValue = node.getAttribute('for')
|
129
|
-
node.setAttribute('for', forValue.replace(pattern, replacement))
|
130
|
-
})
|
131
|
-
|
132
|
-
// Replace names
|
133
|
-
template.querySelectorAll(`input[name*="${pattern}"], select[name*="${pattern}"], textarea[name*="${pattern}"], button[name*="${pattern}"]`).forEach((node) => {
|
134
|
-
const nameValue = node.getAttribute('name')
|
135
|
-
node.setAttribute('name', nameValue.replace(pattern, replacement))
|
136
|
-
})
|
137
|
-
|
138
|
-
// Replace offcanvas targets
|
139
|
-
template.querySelectorAll(`div[data-bs-target="#offcanvas-${pattern}"]`).forEach((node) => {
|
140
|
-
const targetValue = node.getAttribute('data-bs-target')
|
141
|
-
node.setAttribute('data-bs-target', targetValue.replace(pattern, replacement))
|
142
|
-
})
|
114
|
+
// Loop through each node in the template
|
115
|
+
template.querySelectorAll('*').forEach(node => {
|
116
|
+
// Replace attribute values
|
117
|
+
for (const attribute of node.attributes) {
|
118
|
+
if (attribute.value.includes(pattern)) {
|
119
|
+
attribute.value = attribute.value.replace(pattern, randomNumber)
|
120
|
+
}
|
121
|
+
}
|
143
122
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
123
|
+
// Replace template content
|
124
|
+
if (node.nodeName === 'TEMPLATE' && node.innerHTML.includes(pattern)) {
|
125
|
+
node.innerHTML = node.innerHTML.replace(regex, randomNumber)
|
126
|
+
}
|
148
127
|
})
|
149
128
|
|
150
129
|
return template
|
@@ -11076,18 +11076,18 @@ var sortable_esm_default = Sortable;
|
|
11076
11076
|
|
11077
11077
|
// app/assets/javascripts/formstrap/controllers/media_controller.js
|
11078
11078
|
var media_controller_default = class extends Controller {
|
11079
|
+
static get values() {
|
11080
|
+
return {
|
11081
|
+
name: String
|
11082
|
+
};
|
11083
|
+
}
|
11079
11084
|
static get targets() {
|
11080
11085
|
return ["item", "template", "thumbnails", "modalButton", "placeholder", "count", "editButton", "validationInput"];
|
11081
11086
|
}
|
11082
11087
|
connect() {
|
11083
|
-
|
11084
|
-
|
11085
|
-
|
11086
|
-
}
|
11087
|
-
});
|
11088
|
-
if (this.hasSorting()) {
|
11089
|
-
this.initSortable();
|
11090
|
-
}
|
11088
|
+
this.randomizeName();
|
11089
|
+
this.listenForMediaSelection();
|
11090
|
+
this.initializeSorting();
|
11091
11091
|
this.validate();
|
11092
11092
|
}
|
11093
11093
|
destroy(event) {
|
@@ -11106,6 +11106,30 @@ var media_controller_default = class extends Controller {
|
|
11106
11106
|
button.setAttribute("href", url.toString());
|
11107
11107
|
});
|
11108
11108
|
}
|
11109
|
+
randomizeName() {
|
11110
|
+
this.nameValue = crypto.randomUUID().substring(0, 8);
|
11111
|
+
this.updateModalButtonUrls();
|
11112
|
+
}
|
11113
|
+
updateModalButtonUrls() {
|
11114
|
+
this.modalButtonTargets.forEach((button) => {
|
11115
|
+
const url = new URL(button.getAttribute("href"));
|
11116
|
+
url.searchParams.set("name", this.nameValue);
|
11117
|
+
button.setAttribute("href", url.toString());
|
11118
|
+
});
|
11119
|
+
}
|
11120
|
+
listenForMediaSelection() {
|
11121
|
+
document.addEventListener("mediaSelectionSubmitted", (event) => {
|
11122
|
+
if (event.detail.name === this.nameValue) {
|
11123
|
+
this.selectItems(event.detail.items);
|
11124
|
+
this.updateModalButtonUrls();
|
11125
|
+
}
|
11126
|
+
});
|
11127
|
+
}
|
11128
|
+
initializeSorting() {
|
11129
|
+
if (this.hasSorting()) {
|
11130
|
+
this.initSortable();
|
11131
|
+
}
|
11132
|
+
}
|
11109
11133
|
initSortable() {
|
11110
11134
|
sortable_esm_default.create(this.thumbnailsTarget, {
|
11111
11135
|
handle: ".media-drag-sort-handle",
|
@@ -11197,9 +11221,8 @@ var media_controller_default = class extends Controller {
|
|
11197
11221
|
item.classList.remove("d-none");
|
11198
11222
|
}
|
11199
11223
|
createItem(item) {
|
11200
|
-
const
|
11201
|
-
|
11202
|
-
this.thumbnailsTarget.insertAdjacentHTML("beforeend", html);
|
11224
|
+
const templateHtml = this.templateTarget.innerHTML;
|
11225
|
+
this.thumbnailsTarget.insertAdjacentHTML("beforeend", templateHtml);
|
11203
11226
|
const newItem = this.itemTargets.pop();
|
11204
11227
|
newItem.querySelector('input[name*="[blob_id]"]').value = item.blobId;
|
11205
11228
|
newItem.querySelector('input[name*="[_destroy]"]').value = false;
|
@@ -11211,11 +11234,6 @@ var media_controller_default = class extends Controller {
|
|
11211
11234
|
const newThumbnail = item.thumbnail.cloneNode(true);
|
11212
11235
|
oldThumbnail.parentNode.replaceChild(newThumbnail, oldThumbnail);
|
11213
11236
|
}
|
11214
|
-
randomizeIds(template) {
|
11215
|
-
const regex = new RegExp(template.dataset.templateIdRegex, "g");
|
11216
|
-
const randomNumber = Math.floor(1e8 + Math.random() * 9e8);
|
11217
|
-
return template.innerHTML.replace(regex, randomNumber);
|
11218
|
-
}
|
11219
11237
|
removeAllDeselectedItems(items) {
|
11220
11238
|
this.removeDeselectedItems(items, this.itemTargets);
|
11221
11239
|
}
|
@@ -11498,7 +11516,7 @@ var nested_preview_controller_default = class extends Controller {
|
|
11498
11516
|
buildFormData() {
|
11499
11517
|
const fields = this.fieldsTarget;
|
11500
11518
|
const formData = new FormData();
|
11501
|
-
const regex = /\w+\[([^\]]+)s_attributes]\[
|
11519
|
+
const regex = /\w+\[([^\]]+)s_attributes]\[[^\]]+]/g;
|
11502
11520
|
const formElements = fields.querySelectorAll('input[name]:not([name$="[id]"]), select[name]:not([name$="[id]"]), textarea[name]:not([name$="[id]"]), button[name]:not([name$="[id]"])');
|
11503
11521
|
formElements.forEach((element) => {
|
11504
11522
|
const currentName = element.getAttribute("name");
|
@@ -13276,8 +13294,7 @@ var repeater_controller_default = class extends Controller {
|
|
13276
13294
|
return this.rowTargets.includes(row);
|
13277
13295
|
}
|
13278
13296
|
updatePopupButtonIndices(index2) {
|
13279
|
-
const
|
13280
|
-
const buttons = popup.querySelectorAll('[data-popup-target="button"]');
|
13297
|
+
const buttons = document.querySelectorAll(`[data-popup-target="button"][data-popup-id="repeater-buttons-${this.idValue}"]`);
|
13281
13298
|
buttons.forEach((button) => {
|
13282
13299
|
button.dataset.rowIndex = index2;
|
13283
13300
|
});
|
@@ -13288,7 +13305,7 @@ var repeater_controller_default = class extends Controller {
|
|
13288
13305
|
const templateName = button.dataset.templateName;
|
13289
13306
|
const rowIndex = button.dataset.rowIndex;
|
13290
13307
|
let template = this.getTemplate(templateName).content.cloneNode(true);
|
13291
|
-
template = this.
|
13308
|
+
template = this.randomizeIds(template);
|
13292
13309
|
if (rowIndex) {
|
13293
13310
|
const row = this.rowTargets[rowIndex];
|
13294
13311
|
this.listTarget.insertBefore(template, row.nextSibling);
|
@@ -13324,32 +13341,19 @@ var repeater_controller_default = class extends Controller {
|
|
13324
13341
|
return template.dataset.templateName === name;
|
13325
13342
|
})[0];
|
13326
13343
|
}
|
13327
|
-
|
13328
|
-
const
|
13329
|
-
const
|
13344
|
+
randomizeIds(template) {
|
13345
|
+
const randomNumber = crypto.randomUUID().substring(0, 8);
|
13346
|
+
const pattern = `_${this.idValue}_`;
|
13330
13347
|
const regex = new RegExp(pattern, "g");
|
13331
|
-
template.querySelectorAll(
|
13332
|
-
const
|
13333
|
-
|
13334
|
-
|
13335
|
-
|
13336
|
-
|
13337
|
-
|
13338
|
-
|
13339
|
-
|
13340
|
-
node.setAttribute("for", forValue.replace(pattern, replacement));
|
13341
|
-
});
|
13342
|
-
template.querySelectorAll(`input[name*="${pattern}"], select[name*="${pattern}"], textarea[name*="${pattern}"], button[name*="${pattern}"]`).forEach((node) => {
|
13343
|
-
const nameValue = node.getAttribute("name");
|
13344
|
-
node.setAttribute("name", nameValue.replace(pattern, replacement));
|
13345
|
-
});
|
13346
|
-
template.querySelectorAll(`div[data-bs-target="#offcanvas-${pattern}"]`).forEach((node) => {
|
13347
|
-
const targetValue = node.getAttribute("data-bs-target");
|
13348
|
-
node.setAttribute("data-bs-target", targetValue.replace(pattern, replacement));
|
13349
|
-
});
|
13350
|
-
template.querySelectorAll(`.offcanvas[id="offcanvas-${pattern}"]`).forEach((node) => {
|
13351
|
-
const idValue = node.getAttribute("id");
|
13352
|
-
node.setAttribute("id", idValue.replace(pattern, replacement));
|
13348
|
+
template.querySelectorAll("*").forEach((node) => {
|
13349
|
+
for (const attribute of node.attributes) {
|
13350
|
+
if (attribute.value.includes(pattern)) {
|
13351
|
+
attribute.value = attribute.value.replace(pattern, randomNumber);
|
13352
|
+
}
|
13353
|
+
}
|
13354
|
+
if (node.nodeName === "TEMPLATE" && node.innerHTML.includes(pattern)) {
|
13355
|
+
node.innerHTML = node.innerHTML.replace(regex, randomNumber);
|
13356
|
+
}
|
13353
13357
|
});
|
13354
13358
|
return template;
|
13355
13359
|
}
|
@@ -13388,7 +13392,7 @@ var repeater_controller_default = class extends Controller {
|
|
13388
13392
|
var import_tom_select = __toESM(require_tom_select_complete());
|
13389
13393
|
var select_controller_default = class extends Controller {
|
13390
13394
|
connect() {
|
13391
|
-
if (this.element.hasAttribute("multiple")) {
|
13395
|
+
if (this.element.hasAttribute("multiple") || this.element.dataset.tomSelect === "true") {
|
13392
13396
|
this.initTomSelect();
|
13393
13397
|
}
|
13394
13398
|
}
|
@@ -1299,7 +1299,6 @@ span.flatpickr-weekday {
|
|
1299
1299
|
--bs-input-focus-color: var(--bs-body-color);
|
1300
1300
|
}
|
1301
1301
|
.rx-container {
|
1302
|
-
overflow: hidden;
|
1303
1302
|
padding: 0 !important;
|
1304
1303
|
border: var(--bs-border-width) solid var(--bs-border-color);
|
1305
1304
|
border-radius: var(--bs-border-radius);
|
@@ -18,7 +18,6 @@ module Formstrap
|
|
18
18
|
class: ["mb-3", ("form-floating" if float)],
|
19
19
|
data: {
|
20
20
|
controller: "media",
|
21
|
-
name: name,
|
22
21
|
sort: sort,
|
23
22
|
accept: accept,
|
24
23
|
required: required.nil? ? 0 : required
|
@@ -122,7 +121,7 @@ module Formstrap
|
|
122
121
|
end
|
123
122
|
|
124
123
|
def modal_url
|
125
|
-
|
124
|
+
formstrap_media_url(
|
126
125
|
name: name,
|
127
126
|
ids: blob_ids,
|
128
127
|
min: min,
|
@@ -11,13 +11,16 @@ module Formstrap
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def default_options
|
14
|
-
{
|
14
|
+
options = {
|
15
15
|
redactor: {
|
16
16
|
context: !toolbar,
|
17
17
|
extrabar: toolbar,
|
18
|
-
toolbar: toolbar
|
19
18
|
}
|
20
19
|
}
|
20
|
+
|
21
|
+
options[:redactor][:toolbar] = false if @toolbar == false
|
22
|
+
|
23
|
+
options
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
@@ -106,8 +106,9 @@
|
|
106
106
|
class="btn btn-sm btn-outline-secondary"
|
107
107
|
data-repeater-target="addButton"
|
108
108
|
data-popup-target="button"
|
109
|
-
data-popup-id="
|
109
|
+
data-popup-id="repeater-buttons-<%= repeater_id %>"
|
110
110
|
data-popup-pass-thru="<%= pass_thru %>"
|
111
|
+
data-row-index=""
|
111
112
|
data-action="click->repeater#resetButtonIndices click->popup#open"
|
112
113
|
>
|
113
114
|
<%= bootstrap_icon("plus") %>
|
@@ -121,7 +122,7 @@
|
|
121
122
|
<div
|
122
123
|
class="btn btn-sm btn-outline-secondary"
|
123
124
|
data-popup-target="button"
|
124
|
-
data-popup-id="
|
125
|
+
data-popup-id="repeater-buttons-<%= repeater_id %>"
|
125
126
|
data-action="click->repeater#addRow click->popup#close"
|
126
127
|
data-row-index=""
|
127
128
|
data-template-name="<%= name %>"
|
@@ -135,7 +136,7 @@
|
|
135
136
|
<!-- Templates -->
|
136
137
|
<% template_names.each do |template_name| %>
|
137
138
|
<template data-repeater-target="template" data-template-name="<%= template_name %>">
|
138
|
-
<%= form.fields_for attribute, association_object, child_index: "
|
139
|
+
<%= form.fields_for attribute, association_object, child_index: "_#{repeater_id}_" do |ff| %>
|
139
140
|
<%= render "formstrap/repeater/row", row_options.merge(form: ff, pass_thru: pass_thru, repeater_id: repeater_id, index: nil, template_name: template_name) do %>
|
140
141
|
<%= yield(ff, template_name) %>
|
141
142
|
<% end %>
|
@@ -41,7 +41,7 @@
|
|
41
41
|
title="<%= t(".add") %>"
|
42
42
|
data-repeater-target="addButton"
|
43
43
|
data-popup-target="button"
|
44
|
-
data-popup-id="
|
44
|
+
data-popup-id="repeater-buttons-<%= repeater_id %>"
|
45
45
|
data-popup-pass-thru="<%= pass_thru %>"
|
46
46
|
data-action="click->repeater#resetButtonIndices click->popup#open"
|
47
47
|
>
|
data/lib/formstrap/version.rb
CHANGED
data/package.json
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: formstrap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jef Vlamings
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: An extensive Bootstrap form library to power your Ruby On Rails application.
|
14
14
|
email:
|