formstrap 0.3.1 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d52dd192e41d84f75bc6be46186bd8a5739ab34f72b8a03a7f57a2518024356
4
- data.tar.gz: 21985d08ac6f4837e06385a0de891e0785366322f8504a537f22a89d397903aa
3
+ metadata.gz: dc7c89e02028fbd50ff04f97b36ad9ef3bcbc3a1e270d025a664a721a9755e7d
4
+ data.tar.gz: 2375b43a0db037bcb58be9bac12d47079d7f4e5baaa03b08a5ea839053a3aaad
5
5
  SHA512:
6
- metadata.gz: d4e86b30d1f485050c98c72ef426d3590624544e86675b41efe409382595d8f121878b437cf5fdb7ff8dc7aef71dd11c5f689b3bae73d805d1011f6c68cd965b
7
- data.tar.gz: 1f04bdc357480984893120ef11166a2c62ade2e80ad793d49814a550f5e35099fd559ca05f57070ce51a166691a1c4c9a916fedd357b29f38c41b563e3c15601
6
+ metadata.gz: c888b852f01b98134dae97d0a5573ac695c4525020a9fef792af903b9c7a894c5b85dce0172633246e78a0e5ed2825c89659ecf2ff2a663f5aea9b10d844002b
7
+ data.tar.gz: 30ae20de2ddf908f64a82ca167bbc62fa6c0c6b744403e48ff053a9b6e1f293a323f3070947f3d29dd5e4509ef7cee20d42ec9db18d76adb53c8eddb79ee65aa
@@ -114,11 +114,11 @@ export default class extends Controller {
114
114
  }
115
115
 
116
116
  minActiveItems () {
117
- return parseInt(this.element.dataset.min, 10) || 0
117
+ return parseInt(this.validationInputTarget.dataset.min, 10) || 0
118
118
  }
119
119
 
120
120
  maxActiveItems () {
121
- return parseInt(this.element.dataset.max, 10) || Infinity
121
+ return parseInt(this.validationInputTarget.dataset.max, 10) || Infinity
122
122
  }
123
123
 
124
124
  resetPositions () {
@@ -131,13 +131,7 @@ export default class extends Controller {
131
131
  }
132
132
 
133
133
  addNewItems (items) {
134
- const itemTargetIds = this.itemTargets.map((i) => { return parseInt(i.querySelectorAll('input')[1].value) })
135
134
  items.forEach((item) => {
136
- if (itemTargetIds.includes(item.blobId)) {
137
- // Do not add this item (as it is already present)
138
- return
139
- }
140
-
141
135
  this.addItem(item)
142
136
  })
143
137
  }
@@ -211,7 +205,7 @@ export default class extends Controller {
211
205
  const returnedBlobIds = elements.map((e) => { return e.blobId })
212
206
 
213
207
  items.forEach((item) => {
214
- const blobId = parseInt(item.querySelectorAll('input')[1].value)
208
+ const blobId = parseInt(item.querySelectorAll('input')[1].value, 10)
215
209
  if (returnedBlobIds.includes(blobId)) {
216
210
  // Do not delete this one
217
211
  return
@@ -237,7 +231,7 @@ export default class extends Controller {
237
231
 
238
232
  itemByBlobId (blobId) {
239
233
  return this.itemTargets.find((item) => {
240
- return item.querySelector('input[name*=\'blob_id\']').value === blobId
234
+ return parseInt(item.querySelector('input[name*=\'blob_id\']').value, 10) === blobId
241
235
  })
242
236
  }
243
237
 
@@ -249,7 +243,7 @@ export default class extends Controller {
249
243
 
250
244
  activeIds () {
251
245
  return this.activeItems().map((item) => {
252
- return item.querySelector('input[name$=\'[blob_id]\']').value
246
+ return parseInt(item.querySelector('input[name$=\'[blob_id]\']').value, 10)
253
247
  })
254
248
  }
255
249
  }
@@ -1,3 +1,4 @@
1
+ /* global XMLHttpRequest, MutationObserver */
1
2
  import { Controller } from '@hotwired/stimulus'
2
3
 
3
4
  export default class extends Controller {
@@ -30,7 +31,6 @@ export default class extends Controller {
30
31
  }
31
32
 
32
33
  autoResizeIframe () {
33
- // eslint-disable-next-line no-undef
34
34
  const observer = new MutationObserver((mutations) => {
35
35
  mutations.forEach((mutation) => {
36
36
  this.resizeIframe()
@@ -77,7 +77,6 @@ export default class extends Controller {
77
77
 
78
78
  requestPreview () {
79
79
  // Create an AJAX request
80
- // eslint-disable-next-line no-undef
81
80
  const xhr = new XMLHttpRequest()
82
81
  xhr.open('POST', this.urlValue, true)
83
82
 
@@ -109,7 +108,7 @@ export default class extends Controller {
109
108
  validateFields () {
110
109
  let allValid = true
111
110
  const fields = this.fieldsTarget
112
- const formElements = fields.querySelectorAll('input[name], select[name], textarea[name]')
111
+ const formElements = fields.querySelectorAll('input:not([type="hidden"]), select[name], textarea[name]')
113
112
  formElements.forEach(function (element) {
114
113
  const isValid = element.reportValidity()
115
114
  if (!isValid) {
@@ -127,12 +126,15 @@ export default class extends Controller {
127
126
  const formData = new FormData()
128
127
 
129
128
  // Replace all occurrences of "page[blocks_attributes][0]" with "block"
130
- const regex = /\w+\[([^\]]+)s_attributes\]\[\d+\]/g
129
+ const regex = /\w+\[([^\]]+)s_attributes]\[\d+]/g
131
130
  const formElements = fields.querySelectorAll('input[name]:not([name$="[id]"]), select[name]:not([name$="[id]"]), textarea[name]:not([name$="[id]"]), button[name]:not([name$="[id]"])')
132
- formElements.forEach(function (element) {
131
+ formElements.forEach((element) => {
133
132
  const currentName = element.getAttribute('name')
134
133
  const newName = currentName.replace(regex, '$1')
135
- formData.append(newName, element.value)
134
+ const values = this.readValues(element)
135
+ values.forEach((value) => {
136
+ formData.append(newName, value)
137
+ })
136
138
  })
137
139
 
138
140
  // Add authenticity token
@@ -141,6 +143,15 @@ export default class extends Controller {
141
143
  return formData
142
144
  }
143
145
 
146
+ readValues (element) {
147
+ // Check if the element is a select with multiple selection
148
+ if (element.tagName.toLowerCase() === 'select' && element.multiple) {
149
+ return [...element.selectedOptions].map(option => option.value)
150
+ } else {
151
+ return [element.value]
152
+ }
153
+ }
154
+
144
155
  // Prepare the iFrame for rendering
145
156
  // Objective: render the iframe content at the scale of the browser window, but resize it to fit the preview container
146
157
  prepareIframe () {
@@ -110,6 +110,7 @@ export default class extends Controller {
110
110
  replaceIdsWithTimestamps (template) {
111
111
  const pattern = 'rrrrrrrrr'
112
112
  const replacement = new Date().getTime().toString()
113
+ const regex = new RegExp(pattern, 'g')
113
114
 
114
115
  // Replace ids
115
116
  template.querySelectorAll(`input[id*="${pattern}"], select[id*="${pattern}"], textarea[id*="${pattern}"], button[id*="${pattern}"]`).forEach((node) => {
@@ -117,6 +118,11 @@ export default class extends Controller {
117
118
  node.setAttribute('id', idValue.replace(pattern, replacement))
118
119
  })
119
120
 
121
+ // Search and replace pattern in templates
122
+ template.querySelectorAll('template').forEach((node) => {
123
+ node.innerHTML = node.innerHTML.replace(regex, replacement)
124
+ })
125
+
120
126
  // Replace labels
121
127
  template.querySelectorAll(`label[for*="${pattern}"]`).forEach((node) => {
122
128
  const forValue = node.getAttribute('for')
@@ -11035,10 +11035,10 @@ var media_controller_default = class extends Controller {
11035
11035
  }
11036
11036
  }
11037
11037
  minActiveItems() {
11038
- return parseInt(this.element.dataset.min, 10) || 0;
11038
+ return parseInt(this.validationInputTarget.dataset.min, 10) || 0;
11039
11039
  }
11040
11040
  maxActiveItems() {
11041
- return parseInt(this.element.dataset.max, 10) || Infinity;
11041
+ return parseInt(this.validationInputTarget.dataset.max, 10) || Infinity;
11042
11042
  }
11043
11043
  resetPositions() {
11044
11044
  this.activeItems().forEach((item, index2) => {
@@ -11049,13 +11049,7 @@ var media_controller_default = class extends Controller {
11049
11049
  });
11050
11050
  }
11051
11051
  addNewItems(items) {
11052
- const itemTargetIds = this.itemTargets.map((i) => {
11053
- return parseInt(i.querySelectorAll("input")[1].value);
11054
- });
11055
11052
  items.forEach((item) => {
11056
- if (itemTargetIds.includes(item.blobId)) {
11057
- return;
11058
- }
11059
11053
  this.addItem(item);
11060
11054
  });
11061
11055
  }
@@ -11112,7 +11106,7 @@ var media_controller_default = class extends Controller {
11112
11106
  return e.blobId;
11113
11107
  });
11114
11108
  items.forEach((item) => {
11115
- const blobId = parseInt(item.querySelectorAll("input")[1].value);
11109
+ const blobId = parseInt(item.querySelectorAll("input")[1].value, 10);
11116
11110
  if (returnedBlobIds.includes(blobId)) {
11117
11111
  return;
11118
11112
  }
@@ -11128,7 +11122,7 @@ var media_controller_default = class extends Controller {
11128
11122
  }
11129
11123
  itemByBlobId(blobId) {
11130
11124
  return this.itemTargets.find((item) => {
11131
- return item.querySelector("input[name*='blob_id']").value === blobId;
11125
+ return parseInt(item.querySelector("input[name*='blob_id']").value, 10) === blobId;
11132
11126
  });
11133
11127
  }
11134
11128
  activeItems() {
@@ -11138,7 +11132,7 @@ var media_controller_default = class extends Controller {
11138
11132
  }
11139
11133
  activeIds() {
11140
11134
  return this.activeItems().map((item) => {
11141
- return item.querySelector("input[name$='[blob_id]']").value;
11135
+ return parseInt(item.querySelector("input[name$='[blob_id]']").value, 10);
11142
11136
  });
11143
11137
  }
11144
11138
  };
@@ -11374,7 +11368,7 @@ var nested_preview_controller_default = class extends Controller {
11374
11368
  validateFields() {
11375
11369
  let allValid = true;
11376
11370
  const fields = this.fieldsTarget;
11377
- const formElements = fields.querySelectorAll("input[name], select[name], textarea[name]");
11371
+ const formElements = fields.querySelectorAll('input:not([type="hidden"]), select[name], textarea[name]');
11378
11372
  formElements.forEach(function(element) {
11379
11373
  const isValid = element.reportValidity();
11380
11374
  if (!isValid) {
@@ -11386,16 +11380,26 @@ var nested_preview_controller_default = class extends Controller {
11386
11380
  buildFormData() {
11387
11381
  const fields = this.fieldsTarget;
11388
11382
  const formData = new FormData();
11389
- const regex = /\w+\[([^\]]+)s_attributes\]\[\d+\]/g;
11383
+ const regex = /\w+\[([^\]]+)s_attributes]\[\d+]/g;
11390
11384
  const formElements = fields.querySelectorAll('input[name]:not([name$="[id]"]), select[name]:not([name$="[id]"]), textarea[name]:not([name$="[id]"]), button[name]:not([name$="[id]"])');
11391
- formElements.forEach(function(element) {
11385
+ formElements.forEach((element) => {
11392
11386
  const currentName = element.getAttribute("name");
11393
11387
  const newName = currentName.replace(regex, "$1");
11394
- formData.append(newName, element.value);
11388
+ const values = this.readValues(element);
11389
+ values.forEach((value) => {
11390
+ formData.append(newName, value);
11391
+ });
11395
11392
  });
11396
11393
  formData.append("authenticity_token", this.getAuthenticityToken());
11397
11394
  return formData;
11398
11395
  }
11396
+ readValues(element) {
11397
+ if (element.tagName.toLowerCase() === "select" && element.multiple) {
11398
+ return [...element.selectedOptions].map((option2) => option2.value);
11399
+ } else {
11400
+ return [element.value];
11401
+ }
11402
+ }
11399
11403
  prepareIframe() {
11400
11404
  const scaleFactor = this.scaleFactor();
11401
11405
  const style = `
@@ -13219,10 +13223,14 @@ var repeater_controller_default = class extends Controller {
13219
13223
  replaceIdsWithTimestamps(template) {
13220
13224
  const pattern = "rrrrrrrrr";
13221
13225
  const replacement = new Date().getTime().toString();
13226
+ const regex = new RegExp(pattern, "g");
13222
13227
  template.querySelectorAll(`input[id*="${pattern}"], select[id*="${pattern}"], textarea[id*="${pattern}"], button[id*="${pattern}"]`).forEach((node) => {
13223
13228
  const idValue = node.getAttribute("id");
13224
13229
  node.setAttribute("id", idValue.replace(pattern, replacement));
13225
13230
  });
13231
+ template.querySelectorAll("template").forEach((node) => {
13232
+ node.innerHTML = node.innerHTML.replace(regex, replacement);
13233
+ });
13226
13234
  template.querySelectorAll(`label[for*="${pattern}"]`).forEach((node) => {
13227
13235
  const forValue = node.getAttribute("for");
13228
13236
  node.setAttribute("for", forValue.replace(pattern, replacement));
@@ -1,14 +1,14 @@
1
1
  class ApplicationHelper
2
2
  def show_svg(attachment, options = {})
3
- return if !attachment.content_type.include?('svg')
3
+ return if !attachment.content_type.include?("svg")
4
4
 
5
5
  attachment.open do |file|
6
6
  content = file.read
7
7
  doc = Nokogiri::HTML::DocumentFragment.parse content
8
- svg = doc.at_css 'svg'
8
+ svg = doc.at_css "svg"
9
9
 
10
10
  # for security
11
- doc.search('script').each do |src|
11
+ doc.search("script").each do |src|
12
12
  src.remove
13
13
  end
14
14
 
@@ -17,4 +17,4 @@ class ApplicationHelper
17
17
  doc.to_html.html_safe
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -19,8 +19,6 @@ module Formstrap
19
19
  data: {
20
20
  controller: "media",
21
21
  name: name,
22
- min: min,
23
- max: max,
24
22
  sort: sort,
25
23
  accept: accept,
26
24
  required: required.nil? ? 0 : required
@@ -31,7 +29,7 @@ module Formstrap
31
29
  def item_options
32
30
  options = {
33
31
  sort: sort,
34
- url: modal_url,
32
+ url: modal_url
35
33
  }
36
34
 
37
35
  # Don't pass width or height if it was not defined
@@ -108,10 +106,10 @@ module Formstrap
108
106
  end
109
107
 
110
108
  def min
111
- if @required
112
- (@min.to_i < 1) ? 1 : @min.to_i
109
+ if @min.to_i < 1
110
+ @required ? 1 : 0
113
111
  else
114
- (@min.to_i < 1) ? 0 : @min.to_i
112
+ @min.to_i
115
113
  end
116
114
  end
117
115
 
@@ -124,7 +122,14 @@ module Formstrap
124
122
  end
125
123
 
126
124
  def modal_url
127
- formstrap_media_path(name: name, ids: blob_ids, min: min, max: max, mimetype: accept, exclude_models: exclude_models)
125
+ formstrap_media_path(
126
+ name: name,
127
+ ids: blob_ids,
128
+ min: min,
129
+ max: max,
130
+ mimetype: accept,
131
+ exclude_models: exclude_models
132
+ )
128
133
  end
129
134
 
130
135
  def edit_modal_url(attachment)
@@ -92,10 +92,10 @@ module Formstrap
92
92
  blob.open do |file|
93
93
  content = file.read
94
94
  doc = Nokogiri::HTML::DocumentFragment.parse content
95
- svg = doc.at_css 'svg'
95
+ svg = doc.at_css "svg"
96
96
 
97
97
  # for security
98
- doc.search('script').each do |src|
98
+ doc.search("script").each do |src|
99
99
  src.remove
100
100
  end
101
101
 
@@ -5,6 +5,8 @@
5
5
  class: "formstrap-media-validation",
6
6
  data: {
7
7
  "media-target": "validationInput",
8
+ "min": min,
9
+ "max": max,
8
10
  "min-message": t(".min", count: min),
9
11
  "max-message": t(".max", count: max),
10
12
  } %>
@@ -15,7 +15,7 @@ module Formstrap
15
15
  default_options = {
16
16
  data: {
17
17
  controller: "preview",
18
- "preview-url-value": options[:url]
18
+ "preview-url-value": options[:url]
19
19
  },
20
20
  type: nil
21
21
  }
@@ -1,3 +1,3 @@
1
1
  module Formstrap
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.3"
3
3
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontierdotbe/formstrap",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Bootstrap-powered Form Helpers",
5
5
  "module": "app/assets/javascripts/formstrap.js",
6
6
  "main": "app/assets/javascripts/formstrap.js",
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.3.1
4
+ version: 0.3.3
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-03-18 00:00:00.000000000 Z
11
+ date: 2024-04-02 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:
@@ -211,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
211
  - !ruby/object:Gem::Version
212
212
  version: '0'
213
213
  requirements: []
214
- rubygems_version: 3.4.22
214
+ rubygems_version: 3.5.6
215
215
  signing_key:
216
216
  specification_version: 4
217
217
  summary: Bootstrap-powered Form Helpers