ransack_search_element 0.0.2.pre.alpha → 0.1.0.pre.alpha

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6231963831824c4d45beb2710387ff159f2d8ceff31dfe15905678e95c8a62e
4
- data.tar.gz: 158c5cb3a5c577d2969030635ad93ff61c25d3820ba76629a147eaa9ad233f9d
3
+ metadata.gz: dd93e037848a8f700171aec719988067edbf515bbe1e512e4d27e66296d6f8b6
4
+ data.tar.gz: b6ef1f37b77de1ce0e78a07d2b16171c135a484596118c1c34aa611e75698d80
5
5
  SHA512:
6
- metadata.gz: e8e734658728bfc5cacafec2e3483191f761d1429f68ba2c44b7377b72b7817a1df39ba47fa1e70d9f778903589e32a9daabc73b490fae96c7369425bdbec11b
7
- data.tar.gz: 8ccae8b4e6d1c4b164d73f9ed88882bdcfc1c9ebaf95ab940f5bdb4a6dd5fabd8273babcaf71911dc6dcaf3c9ffe072b1af5cfc0d3b0e13334035f6f472b6aef
6
+ metadata.gz: 9caa070a79c203d9f6b69a6ebdf4c9a9f8cef79d33549cf25325ba9895d8ac3b954efb875f227cb96a05483ca89fedde1396e38feb618d0221a9cad927633731
7
+ data.tar.gz: 1dc72e8b176fae901a389a77eb2b5fd1d4712e1f7c8a16cfd1a4d1d05bb8d1381b59a290fa05958ac1f4a51634cbc769ad0a099ed905bdb8d8a0a9d7b88f1088
@@ -1,12 +1,51 @@
1
- import React, { useMemo, useState } from "react";
2
- import QueryBuilder, {
3
- AttributeInfo,
4
- Filter,
5
- options,
6
- } from "../utilities/QueryBuilder";
1
+ import React, { useState } from "react";
7
2
 
8
3
  import FilterRow from "./FilterRow";
9
4
 
5
+ const options = [
6
+ ["Contains", "cont"],
7
+ ["Equals", "eq"],
8
+ ["Less than", "lt"],
9
+ ["Greater than", "gt"],
10
+ ["Less than/equal", "lteq"],
11
+ ["Greater than/equal", "gteq"],
12
+ ];
13
+
14
+ const options_values = options.map((v) => v[1]);
15
+
16
+ /**
17
+ * Recursively iterate through relations with matching prefixes.
18
+ * Attempts to get the accompanying AttributeInfo given a full query.
19
+ * @param full_query
20
+ * @param ass
21
+ */
22
+ const get_association = (
23
+ full_query,
24
+ ass
25
+ ): {
26
+ [association: string]: AttributeInfo;
27
+ } => {
28
+ if (ass instanceof Array) {
29
+ return ass.find((dict) => {
30
+ return full_query.includes(Object.keys(dict)[0]);
31
+ });
32
+ }
33
+
34
+ const key = Object.keys(ass).find((k) => full_query.includes(k));
35
+
36
+ if (typeof key === "undefined") return null;
37
+
38
+ const remainder = full_query.slice(full_query.indexOf(key) + key.length + 1);
39
+
40
+ return get_association(remainder, ass[key]);
41
+ };
42
+
43
+ export type AttributeInfo = {
44
+ type: string;
45
+ label: string;
46
+ ass?: string;
47
+ };
48
+
10
49
  export type FilterDropdownProps = {
11
50
  columns: {
12
51
  root: {
@@ -39,13 +78,85 @@ export default (props: FilterDropdownProps): JSX.Element => {
39
78
  associations.map((v) => Object.keys(v)[0]).flat()
40
79
  );
41
80
 
42
- const queryBuilder = useMemo(() => {
43
- return new QueryBuilder(colNames, associations);
44
- }, [columns, queries]);
45
-
46
81
  const [filters, setFilters] = useState<{
47
- [key: string]: Filter[];
48
- }>(queryBuilder.parseQueries(queries));
82
+ [key: string]: {
83
+ label: string;
84
+ query: string;
85
+ type: string;
86
+ value: string | number;
87
+ ass?: string;
88
+ }[];
89
+ }>(
90
+ /**
91
+ * Here we initialize parameters from the query string,
92
+ * and do basic validation for options and attributes.
93
+ */
94
+ (() => {
95
+ let s = queries || {};
96
+
97
+ let f = {};
98
+
99
+ if (Object.entries(s)?.length > 0) {
100
+ for (const [k, v] of Object.entries(s)) {
101
+ // sorting parameter
102
+ if (k === "s") continue;
103
+
104
+ const query = options_values
105
+ .filter((v) => k.includes(v))
106
+ .reduce((x, y) => (x.length > y.length ? x : y), "");
107
+
108
+ const attribute = possible_values
109
+ .filter((v) => k.includes(v))
110
+ .reduce((x, y) => (x.length > y.length ? x : y), "");
111
+
112
+ const asses = get_association(
113
+ k,
114
+ associations.reduce((acc, cur) => ({ ...acc, ...cur }), {})
115
+ );
116
+
117
+ if (asses) {
118
+ const full_ass_key = Object.keys(asses)[0];
119
+ const full_ass_values = Object.values(asses)[0];
120
+
121
+ const t = {};
122
+ t["query"] = query;
123
+ t["value"] = v;
124
+ t["type"] = full_ass_values?.type;
125
+ t["label"] = full_ass_values?.label;
126
+ t["ass"] = attribute;
127
+
128
+ if (query.length > 0 && attribute.length > 0) {
129
+ if (f[`${attribute}_${full_ass_key}`] instanceof Array) {
130
+ f[`${attribute}_${full_ass_key}`].push(t);
131
+ } else {
132
+ f[`${attribute}_${full_ass_key}`] = [];
133
+ f[`${attribute}_${full_ass_key}`].push(t);
134
+ }
135
+ }
136
+ } else {
137
+ const t = {};
138
+ t["query"] = query;
139
+ t["value"] = v;
140
+ t["type"] = colNames[attribute]?.type;
141
+ t["label"] = colNames[attribute]?.label;
142
+
143
+ if (query.length > 0 && attribute.length > 0) {
144
+ if (f[`${attribute}`] instanceof Array) {
145
+ f[`${attribute}`].push(t);
146
+ } else {
147
+ f[`${attribute}`] = [];
148
+ f[`${attribute}`].push(t);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ } else {
154
+ return {};
155
+ }
156
+
157
+ return f;
158
+ })()
159
+ );
49
160
 
50
161
  /**
51
162
  * The constructed query string to replace the current location when
@@ -61,7 +172,7 @@ export default (props: FilterDropdownProps): JSX.Element => {
61
172
  .map(([k, v]) => {
62
173
  return v
63
174
  .map((x) => {
64
- if (x["ass"] && k.indexOf(x["ass"]) !== 0) {
175
+ if (x["ass"]) {
65
176
  return (
66
177
  `q[${x["ass"] + "_" + k + "_" + x["query"]}]` +
67
178
  "=" +
@@ -121,7 +232,12 @@ export default (props: FilterDropdownProps): JSX.Element => {
121
232
  onClick={(e) => {
122
233
  e.preventDefault();
123
234
  let copy = { ...filters };
124
- const t = {};
235
+ const t = {
236
+ query: "",
237
+ type: "",
238
+ label: "",
239
+ value: "",
240
+ };
125
241
  t["query"] = "eq";
126
242
  t["type"] = colNames[v]?.type || associations[v]?.type;
127
243
  t["label"] = colNames[v]?.label || associations[v]?.label;
@@ -178,7 +294,12 @@ export default (props: FilterDropdownProps): JSX.Element => {
178
294
  e.preventDefault();
179
295
  let copy = { ...filters };
180
296
 
181
- const t = {};
297
+ const t = {
298
+ query: "",
299
+ type: "",
300
+ label: "",
301
+ value: "",
302
+ };
182
303
  t["query"] = "eq";
183
304
  t["type"] = values.type;
184
305
  t["label"] = values.label;
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { ReactComponentElement } from "react";
2
2
  import FilterDropdown, {
3
3
  FilterDropdownProps,
4
4
  } from "./components/FilterDropdown";
@@ -1,65 +1,67 @@
1
- import { Dropdown } from "bootstrap";
1
+ import * as bootstrap from "bootstrap";
2
2
 
3
3
  export default function (root: Document | HTMLElement) {
4
- const CLASS_NAME = "has-child-dropdown-show";
5
- Dropdown.prototype.toggle = (function (_orginal) {
6
- return function () {
7
- root.querySelectorAll("." + CLASS_NAME).forEach(function (e) {
8
- e.classList.remove(CLASS_NAME);
9
- });
10
- let dd = this._element
11
- .closest(".dropdown")
12
- .parentNode.closest(".dropdown");
13
- for (; dd && dd !== root; dd = dd.parentNode.closest(".dropdown")) {
14
- dd.classList.add(CLASS_NAME);
15
- }
16
- return _orginal.call(this);
17
- };
18
- })(Dropdown.prototype.toggle);
19
-
20
- root.querySelectorAll(".dropdown").forEach(function (dd) {
21
- dd.addEventListener("hide.bs.dropdown", function (e) {
22
- if (this.classList.contains(CLASS_NAME)) {
23
- this.classList.remove(CLASS_NAME);
24
- e.preventDefault();
25
- }
26
- if (
27
- e.clickEvent &&
28
- e.clickEvent
29
- .composedPath()
30
- .some(
31
- (el) => el.classList && el.classList.contains("dropdown-toggle")
32
- )
33
- ) {
34
- e.preventDefault();
35
- }
36
- e.stopPropagation(); // do not need pop in multi level mode
37
- });
38
- });
39
-
40
- // for hover
41
- function getDropdown(element) {
42
- return Dropdown.getInstance(element) || new Dropdown(element);
43
- }
4
+ (function ($bs) {
5
+ const CLASS_NAME = "has-child-dropdown-show";
6
+ $bs.Dropdown.prototype.toggle = (function (_orginal) {
7
+ return function () {
8
+ root.querySelectorAll("." + CLASS_NAME).forEach(function (e) {
9
+ e.classList.remove(CLASS_NAME);
10
+ });
11
+ let dd = this._element
12
+ .closest(".dropdown")
13
+ .parentNode.closest(".dropdown");
14
+ for (; dd && dd !== root; dd = dd.parentNode.closest(".dropdown")) {
15
+ dd.classList.add(CLASS_NAME);
16
+ }
17
+ return _orginal.call(this);
18
+ };
19
+ })($bs.Dropdown.prototype.toggle);
44
20
 
45
- root
46
- .querySelectorAll(".dropdown-hover, .dropdown-hover-all .dropdown")
47
- .forEach(function (dd) {
48
- dd.addEventListener("mouseenter", function (e) {
49
- let toggle = e.target.querySelector(
50
- ':scope>[data-bs-toggle="dropdown"]'
51
- );
52
- if (!toggle.classList.contains("show")) {
53
- getDropdown(toggle).toggle();
21
+ root.querySelectorAll(".dropdown").forEach(function (dd) {
22
+ dd.addEventListener("hide.bs.dropdown", function (e) {
23
+ if (this.classList.contains(CLASS_NAME)) {
24
+ this.classList.remove(CLASS_NAME);
25
+ e.preventDefault();
54
26
  }
55
- });
56
- dd.addEventListener("mouseleave", function (e) {
57
- let toggle = e.target.querySelector(
58
- ':scope>[data-bs-toggle="dropdown"]'
59
- );
60
- if (toggle.classList.contains("show")) {
61
- getDropdown(toggle).toggle();
27
+ if (
28
+ e.clickEvent &&
29
+ e.clickEvent
30
+ .composedPath()
31
+ .some(
32
+ (el) => el.classList && el.classList.contains("dropdown-toggle")
33
+ )
34
+ ) {
35
+ e.preventDefault();
62
36
  }
37
+ e.stopPropagation(); // do not need pop in multi level mode
63
38
  });
64
39
  });
40
+
41
+ // for hover
42
+ function getDropdown(element) {
43
+ return $bs.Dropdown.getInstance(element) || new $bs.Dropdown(element);
44
+ }
45
+
46
+ root
47
+ .querySelectorAll(".dropdown-hover, .dropdown-hover-all .dropdown")
48
+ .forEach(function (dd) {
49
+ dd.addEventListener("mouseenter", function (e) {
50
+ let toggle = e.target.querySelector(
51
+ ':scope>[data-bs-toggle="dropdown"]'
52
+ );
53
+ if (!toggle.classList.contains("show")) {
54
+ getDropdown(toggle).toggle();
55
+ }
56
+ });
57
+ dd.addEventListener("mouseleave", function (e) {
58
+ let toggle = e.target.querySelector(
59
+ ':scope>[data-bs-toggle="dropdown"]'
60
+ );
61
+ if (toggle.classList.contains("show")) {
62
+ getDropdown(toggle).toggle();
63
+ }
64
+ });
65
+ });
66
+ })(bootstrap);
65
67
  }
@@ -1,3 +1,3 @@
1
1
  module RansackSearchElement
2
- VERSION = '0.0.2-alpha'
2
+ VERSION = '0.1.0-alpha'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ransack_search_element
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2.pre.alpha
4
+ version: 0.1.0.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arthur Dzieniszewski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-05 00:00:00.000000000 Z
11
+ date: 2021-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -42,7 +42,6 @@ files:
42
42
  - app/javascript/ransack-search-element/components/FilterRow.tsx
43
43
  - app/javascript/ransack-search-element/components/FormInput.tsx
44
44
  - app/javascript/ransack-search-element/index.ts
45
- - app/javascript/ransack-search-element/utilities/QueryBuilder.ts
46
45
  - app/javascript/ransack-search-element/utilities/dateConverter.ts
47
46
  - app/javascript/ransack-search-element/utilities/registerNestedDropdown.ts
48
47
  - app/jobs/ransack_search_element/application_job.rb
@@ -77,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
76
  - !ruby/object:Gem::Version
78
77
  version: 1.3.1
79
78
  requirements: []
80
- rubygems_version: 3.2.15
79
+ rubygems_version: 3.2.22
81
80
  signing_key:
82
81
  specification_version: 4
83
82
  summary: Generic, advanced search element for Ransack.
@@ -1,134 +0,0 @@
1
- export type Filter = {
2
- [key: string]: {
3
- label: string;
4
- query: string;
5
- type: string;
6
- value: string | number;
7
- ass?: string;
8
- };
9
- };
10
-
11
- export type AttributeInfo = {
12
- type: string;
13
- label: string;
14
- ass?: string;
15
- };
16
-
17
- export const options = [
18
- ["Contains", "cont"],
19
- ["Equals", "eq"],
20
- ["Less than", "lt"],
21
- ["Greater than", "gt"],
22
- ["Less than/equal", "lteq"],
23
- ["Greater than/equal", "gteq"],
24
- ];
25
-
26
- const options_values = options.map((v) => v[1]);
27
-
28
- /**
29
- * Recursively iterate through relations with matching prefixes.
30
- * Attempts to get the accompanying AttributeInfo given a full query.
31
- * @param full_query
32
- * @param ass
33
- */
34
- const get_association = (
35
- full_query,
36
- ass
37
- ): {
38
- [association: string]: AttributeInfo;
39
- } => {
40
- if (ass instanceof Array) {
41
- return ass.find((dict) => {
42
- return full_query.includes(Object.keys(dict)[0]);
43
- });
44
- }
45
-
46
- const key = Object.keys(ass).find((k) => full_query.includes(k));
47
-
48
- if (typeof key === "undefined") return null;
49
-
50
- const remainder = full_query.slice(full_query.indexOf(key) + key.length + 1);
51
-
52
- return get_association(remainder, ass[key]);
53
- };
54
-
55
- export default class QueryBuilder {
56
- attributes = {};
57
- associations;
58
- possible_values = [];
59
-
60
- constructor(attributes, associations) {
61
- this.attributes = attributes;
62
- this.associations = associations;
63
-
64
- this.possible_values = Object.keys(attributes).concat(
65
- associations.map((v) => Object.keys(v)[0]).flat()
66
- );
67
- }
68
-
69
- parseQueries(queries) {
70
- let s = queries || {};
71
-
72
- let f = {};
73
-
74
- if (Object.entries(s)?.length > 0) {
75
- for (const [k, v] of Object.entries(s)) {
76
- // sorting parameter
77
- if (k === "s") continue;
78
-
79
- const query = options_values
80
- .filter((v) => k.includes(v))
81
- .reduce((x, y) => (x.length > y.length ? x : y), "");
82
-
83
- const attribute = this.possible_values
84
- .filter((v) => k.includes(v))
85
- .reduce((x, y) => (x.length > y.length ? x : y), "");
86
-
87
- const asses = get_association(
88
- k,
89
- this.associations.reduce((acc, cur) => ({ ...acc, ...cur }), {})
90
- );
91
-
92
- if (asses) {
93
- const full_ass_key = Object.keys(asses)[0];
94
- const full_ass_values = Object.values(asses)[0];
95
-
96
- const t = {};
97
- t["query"] = query;
98
- t["value"] = v;
99
- t["type"] = full_ass_values?.type;
100
- t["label"] = full_ass_values?.label;
101
- t["ass"] = attribute;
102
-
103
- if (query.length > 0 && attribute.length > 0) {
104
- if (f[`${attribute}_${full_ass_key}`] instanceof Array) {
105
- f[`${attribute}_${full_ass_key}`].push(t);
106
- } else {
107
- f[`${attribute}_${full_ass_key}`] = [];
108
- f[`${attribute}_${full_ass_key}`].push(t);
109
- }
110
- }
111
- } else {
112
- const t = {};
113
- t["query"] = query;
114
- t["value"] = v;
115
- t["type"] = this.attributes[attribute]?.type;
116
- t["label"] = this.attributes[attribute]?.label;
117
-
118
- if (query.length > 0 && attribute.length > 0) {
119
- if (f[`${attribute}`] instanceof Array) {
120
- f[`${attribute}`].push(t);
121
- } else {
122
- f[`${attribute}`] = [];
123
- f[`${attribute}`].push(t);
124
- }
125
- }
126
- }
127
- }
128
- } else {
129
- return {};
130
- }
131
-
132
- return f;
133
- }
134
- }