json-rules-filter 1.0.24 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-rules-filter",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "description": "para crear reglas avanzadas de filtrado sobre datasets JSON de forma dinámica y visual.",
5
5
  "main": "src/jquery.jsonRulesFilter.js",
6
6
  "scripts": {
@@ -42,6 +42,7 @@
42
42
  title: { text: "Reglas de filtrado", className: "fs-6 fw-semibold" },
43
43
  buttons: {
44
44
  reset: { text: "Resetear", className: "fw-semibold link-danger link-offset-2 link-underline link-underline-opacity-0" },
45
+ save: { text: "Guardar", className: "fw-semibold link-primary link-offset-2 link-underline link-underline-opacity-0" },
45
46
  dropdown: { text: "Añadir regla", className: "btn btn-secondary" },
46
47
  apply: { text: "Aplicar regla", className: "btn btn-primary" },
47
48
  },
@@ -82,12 +83,36 @@
82
83
  return result;
83
84
  },
84
85
 
86
+ // -------------------- PUBLIC FUNCTIONS ----------//
87
+
85
88
  // Updates dataset from external source and refreshes the UI
86
- updateData: function (newData) {
89
+ updateData: function (newData, callback) {
87
90
  this.data = newData;
88
- this.reset();
91
+ //Run callback del usuario
92
+ callback?.();
93
+ },
94
+
95
+ //Set pre-aplicated rules
96
+ preloadRules(rules) {
97
+ const self = this;
98
+ $.each(rules, function (clave, valor) {
99
+ console.log(valor);
100
+ self.addRuleRow(valor.typeFilter, valor.dataField, valor.dataName, valor.optionFilter, valor.searchValue);
101
+ });
102
+ this.applyRules();
103
+ },
104
+
105
+ // Resets rule container and re-runs filtering (show all)
106
+ reset: function (callback) {
107
+ const rules = this.getAplicatedRules();
108
+ $(".container-rules-filters").empty();
109
+ this.applyRules();
110
+ //Run callback del usuario
111
+ callback?.(rules);
89
112
  },
90
113
 
114
+ //--------------------------------------------------//
115
+
91
116
  // Collects all rules currently defined in the DOM
92
117
  getAplicatedRules: function () {
93
118
  const rules_items = $contenedor.find("select[name=select-rule-option]");
@@ -110,11 +135,26 @@
110
135
  return rulesAplicated;
111
136
  },
112
137
 
138
+ takeSnapshoot: function(){
139
+ const rules = this.getAplicatedRules();
140
+ localStorage.setItem("jsonRulesFilterData", JSON.stringify(rules));
141
+ console.log("Take snapshoot successfuly");
142
+ },
143
+
144
+ getSnapshoot: function(){
145
+ const rules = localStorage.getItem("jsonRulesFilterData");
146
+
147
+ if(rules != null){
148
+ this.preloadRules(JSON.parse(rules));
149
+ }
150
+ },
151
+
113
152
  // Initialize UI components
114
153
  init: function () {
115
154
  this.$contenedor.empty();
116
155
  this.render();
117
156
  this.bindEvents();
157
+ this.getSnapshoot();
118
158
  },
119
159
 
120
160
  // Render main plugin skeleton
@@ -143,7 +183,10 @@
143
183
  let template = `
144
184
  <div class="d-flex justify-content-between">
145
185
  <p class="${this.bs(this.settings.title.className)}">${this.settings.title.text}</p>
146
- <p><a class="${this.bs(this.settings.buttons.reset.className)} remove-rules-containers" href="#">${this.settings.buttons.reset.text}</a></p>
186
+ <div class="d-flex justify-content-between">
187
+ <p class="mr-2"><a class="${this.bs(this.settings.buttons.save.className)} save-rules-snapshot" href="#">${this.settings.buttons.save.text}</a></p>
188
+ <p><a class="${this.bs(this.settings.buttons.reset.className)} remove-rules-containers" href="#">${this.settings.buttons.reset.text}</a></p>
189
+ </div>
147
190
  </div>
148
191
  <div class="py-2 container-rules-filters"></div>
149
192
  <div class="mt-2">
@@ -159,18 +202,19 @@
159
202
  <button type="button" id="apply-rules-btn" class="${this.bs(this.settings.buttons.apply.className)}">${this.settings.buttons.apply.text}</button>
160
203
  </div>
161
204
  </div>`;
205
+
162
206
  this.$contenedor.append(template);
163
207
  if (this.settings.scrollY !== false) {
164
208
  const maxHeight = typeof this.settings.scrollY === 'number' ?
165
- this.settings.scrollY + 'px' :
166
- this.settings.scrollY;
209
+ this.settings.scrollY + 'px' :
210
+ this.settings.scrollY;
167
211
 
168
212
  this.$contenedor.find(".container-rules-filters").css({
169
213
  "max-height": maxHeight,
170
214
  "overflow-y": "auto",
171
215
  "overflow-x": "hidden",
172
216
  "margin-right": "-10px",
173
- "padding-right":"10px"
217
+ "padding-right": "10px"
174
218
  })
175
219
  }
176
220
  },
@@ -208,8 +252,10 @@
208
252
  // Add new rule row
209
253
  this.$contenedor.find(".dropdown-rules-item").on("click", function (e) {
210
254
  e.preventDefault();
211
- self.addRuleRow($(this).data("column-type"), $(this).data("column-data"), $(this).data("column-name"));
212
- $dropdown.dropdown("hide");
255
+ self.addRuleRow($(this).data("column-type"), $(this).data("column-data"), $(this).data("column-name"), "", "");
256
+ if (self.settings.bsVersion === 4) {
257
+ $dropdown.dropdown("hide");
258
+ }
213
259
  });
214
260
 
215
261
  // Clear all rules
@@ -218,6 +264,12 @@
218
264
  self.reset();
219
265
  });
220
266
 
267
+ // Save all rules
268
+ this.$contenedor.find(".save-rules-snapshot").on("click", function (e) {
269
+ e.preventDefault();
270
+ self.takeSnapshoot();
271
+ });
272
+
221
273
  // Apply current filters to data
222
274
  this.$contenedor.find("#apply-rules-btn").on("click", function (e) {
223
275
  e.preventDefault();
@@ -225,14 +277,8 @@
225
277
  });
226
278
  },
227
279
 
228
- // Resets rule container and re-runs filtering (show all)
229
- reset: function () {
230
- $(".container-rules-filters").empty();
231
- this.applyRules();
232
- },
233
-
234
280
  // Adds a new rule row dynamically
235
- addRuleRow: function (type, dataField, name) {
281
+ addRuleRow: function (type, dataField, name, selectedValues, searchValue) {
236
282
  const self = this;
237
283
  const $container = this.$contenedor.find(".container-rules-filters");
238
284
 
@@ -249,7 +295,7 @@
249
295
  const searchInput = `
250
296
  <div class="col-7">
251
297
  <label class="${labelClass}"><span class="text-danger">*</span>${this.settings.language.searchLabel[type]}</label>
252
- <input type="text" class="form-control" name="input-rule-search" id="input-rule-search-${id_select}">
298
+ <input type="text" class="form-control" value="${searchValue}" name="input-rule-search" id="input-rule-search-${id_select}">
253
299
  </div>`;
254
300
 
255
301
  const row = `
@@ -271,13 +317,15 @@
271
317
  $container.append(row);
272
318
  // Fetch custom render function from settings
273
319
  const render = self.settings.filters.find((e) => e.data === dataField).render;
274
- this.initSelect2(id_select, type, dataField, render);
320
+ // Initialice select2
321
+ this.initSelect2(id_select, type, dataField, selectedValues, render);
275
322
  },
276
323
 
277
324
  // Initialize Select2 plugin on the newly created dropdown
278
- initSelect2: function (id, type, dataField, render) {
325
+ initSelect2: function (id, type, dataField, selectedValues, render) {
279
326
  const self = this;
280
327
  const $select = this.$contenedor.find(`#select-rule-option-${id}`);
328
+ let data = type == 'select' ? this.getSelectValues(dataField, selectedValues) : this.getRuleOptions(type);
281
329
 
282
330
  $select.select2({
283
331
  width: '100%',
@@ -286,7 +334,7 @@
286
334
  templateSelection: render,
287
335
  templateResult: render,
288
336
  // Load dataset based on type: unique values for 'select', operators for others
289
- data: type == 'select' ? this.getSelectValues(dataField) : this.getRuleOptions(type)
337
+ data: data
290
338
  });
291
339
 
292
340
  // Remove rule event
@@ -294,6 +342,9 @@
294
342
  e.preventDefault();
295
343
  $(`#select-rule-container-${id}`).remove();
296
344
  });
345
+
346
+ // Preselected values
347
+ $select.val(selectedValues).trigger('change');
297
348
  },
298
349
 
299
350
  // Helper to map operator objects from language settings
@@ -305,16 +356,35 @@
305
356
  },
306
357
 
307
358
  // Extract unique values from data to populate 'select' type filters
308
- getSelectValues: function (dataField) {
359
+ getSelectValues: function (dataField, selectedValues) {
309
360
  const getValueByPath = (obj, path) => {
310
361
  return path.split('.').reduce((acc, part) => acc && acc[part], obj);
311
362
  };
312
363
 
313
- // Using Set + JSON stringify/parse to ensure unique objects in the dropdown
314
- return [...new Set(this.data.map(function (item) {
315
- const val = getValueByPath(item, dataField);
316
- return JSON.stringify({ id: val, text: val });
317
- }))].map((e) => JSON.parse(e));
364
+ // 1. Extraemos los valores únicos de la base de datos actual
365
+ let uniqueOptions = [...new Set(this.data.map(function (item) {
366
+ const val = getValueByPath(item, dataField)?.toString();
367
+ if (val !== undefined && val !== null) {
368
+ return JSON.stringify({ id: val, text: val });
369
+ }
370
+ return null;
371
+ }).filter(item => item !== null))].map((e) => JSON.parse(e));
372
+
373
+ // 2. Al ser siempre un array, iteramos directamente si tiene contenido
374
+ if (selectedValues && selectedValues.length > 0) {
375
+ selectedValues.forEach(val => {
376
+ // Si el valor no existe ya en nuestras opciones únicas, lo empujamos
377
+
378
+ if (val && !uniqueOptions.some(opt => opt.id == val)) {
379
+ uniqueOptions.push({
380
+ id: val,
381
+ text: val
382
+ });
383
+ }
384
+ });
385
+ }
386
+
387
+ return uniqueOptions;
318
388
  },
319
389
 
320
390
  // Core filtering engine