katalyst-tables 3.3.1 → 3.3.3
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 +4 -4
- data/README.md +1 -1
- data/app/assets/builds/katalyst/tables.esm.js +332 -6
- data/app/assets/builds/katalyst/tables.js +332 -6
- data/app/assets/builds/katalyst/tables.min.js +1 -1
- data/app/assets/builds/katalyst/tables.min.js.map +1 -1
- data/app/assets/stylesheets/katalyst/tables/_index.scss +1 -1
- data/app/assets/stylesheets/katalyst/tables/_query.scss +109 -0
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_date.scss +1 -1
- data/app/assets/stylesheets/katalyst/tables/typed-columns/_datetime.scss +1 -1
- data/app/components/katalyst/table_component.rb +13 -2
- data/app/components/katalyst/tables/cells/currency_component.rb +21 -2
- data/app/components/katalyst/tables/cells/number_component.rb +31 -1
- data/app/components/katalyst/tables/query/input_component.html.erb +13 -0
- data/app/components/katalyst/tables/query/input_component.rb +50 -0
- data/app/components/katalyst/tables/query/modal_component.html.erb +33 -0
- data/app/components/katalyst/tables/query/modal_component.rb +89 -0
- data/app/components/katalyst/tables/query_component.html.erb +8 -0
- data/app/components/katalyst/tables/{filter_component.rb → query_component.rb} +37 -30
- data/app/controllers/concerns/katalyst/tables/backend.rb +14 -0
- data/app/helpers/katalyst/tables/frontend.rb +14 -8
- data/app/javascript/tables/application.js +8 -3
- data/app/javascript/tables/query_controller.js +108 -0
- data/app/javascript/tables/query_input_controller.js +228 -0
- data/app/models/concerns/katalyst/tables/collection/core.rb +2 -2
- data/app/models/concerns/katalyst/tables/collection/query/array_value_parser.rb +13 -26
- data/app/models/concerns/katalyst/tables/collection/query/parser.rb +16 -13
- data/app/models/concerns/katalyst/tables/collection/query/single_value_parser.rb +11 -3
- data/app/models/concerns/katalyst/tables/collection/query/value_parser.rb +10 -5
- data/app/models/concerns/katalyst/tables/collection/query.rb +42 -5
- data/app/models/concerns/katalyst/tables/collection/sorting.rb +11 -1
- data/app/models/katalyst/tables/collection/base.rb +10 -0
- data/app/models/katalyst/tables/collection/filter.rb +10 -0
- data/config/locales/tables.en.yml +2 -0
- data/{app/models → lib}/katalyst/tables/collection/type/boolean.rb +11 -1
- data/lib/katalyst/tables/collection/type/date.rb +57 -0
- data/lib/katalyst/tables/collection/type/enum.rb +32 -0
- data/lib/katalyst/tables/collection/type/float.rb +21 -0
- data/lib/katalyst/tables/collection/type/helpers/delegate.rb +32 -0
- data/{app/models → lib}/katalyst/tables/collection/type/helpers/extensions.rb +14 -0
- data/lib/katalyst/tables/collection/type/helpers/multiple.rb +60 -0
- data/lib/katalyst/tables/collection/type/helpers/range.rb +59 -0
- data/lib/katalyst/tables/collection/type/integer.rb +21 -0
- data/{app/models → lib}/katalyst/tables/collection/type/value.rb +22 -2
- data/{app/models → lib}/katalyst/tables/collection/type.rb +16 -0
- data/lib/katalyst/tables/collection.rb +11 -0
- data/lib/katalyst/tables.rb +6 -0
- metadata +26 -21
- data/app/assets/stylesheets/katalyst/tables/_filter.scss +0 -43
- data/app/components/katalyst/tables/filter/modal_component.html.erb +0 -25
- data/app/components/katalyst/tables/filter/modal_component.rb +0 -112
- data/app/components/katalyst/tables/filter_component.html.erb +0 -18
- data/app/javascript/tables/filter/modal_controller.js +0 -13
- data/app/models/katalyst/tables/collection/type/date.rb +0 -60
- data/app/models/katalyst/tables/collection/type/enum.rb +0 -21
- data/app/models/katalyst/tables/collection/type/float.rb +0 -57
- data/app/models/katalyst/tables/collection/type/helpers/delegate.rb +0 -50
- data/app/models/katalyst/tables/collection/type/helpers/multiple.rb +0 -30
- data/app/models/katalyst/tables/collection/type/integer.rb +0 -57
- /data/{app/models → lib}/katalyst/tables/collection/type/query.rb +0 -0
- /data/{app/models → lib}/katalyst/tables/collection/type/search.rb +0 -0
- /data/{app/models → lib}/katalyst/tables/collection/type/string.rb +0 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class QueryInputController extends Controller {
|
4
|
+
static targets = ["input", "highlight"];
|
5
|
+
static values = { query: String };
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
this.queryValue = this.inputTarget.value;
|
9
|
+
}
|
10
|
+
|
11
|
+
update() {
|
12
|
+
this.queryValue = this.inputTarget.value;
|
13
|
+
}
|
14
|
+
|
15
|
+
queryValueChanged(query) {
|
16
|
+
this.highlightTarget.innerHTML = "";
|
17
|
+
|
18
|
+
new Parser().parse(query).tokens.forEach((token) => {
|
19
|
+
this.highlightTarget.appendChild(token.render());
|
20
|
+
});
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
class Parser {
|
25
|
+
constructor() {
|
26
|
+
this.tokens = [];
|
27
|
+
this.values = null;
|
28
|
+
}
|
29
|
+
|
30
|
+
parse(input) {
|
31
|
+
const query = new StringScanner(input);
|
32
|
+
|
33
|
+
while (!query.isEos()) {
|
34
|
+
this.push(this.skipWhitespace(query));
|
35
|
+
|
36
|
+
const value = this.takeTagged(query) || this.takeUntagged(query);
|
37
|
+
|
38
|
+
if (!this.push(value)) break;
|
39
|
+
}
|
40
|
+
|
41
|
+
return this;
|
42
|
+
}
|
43
|
+
|
44
|
+
push(token) {
|
45
|
+
if (token) {
|
46
|
+
this.values ? this.values.push(token) : this.tokens.push(token);
|
47
|
+
}
|
48
|
+
|
49
|
+
return !!token;
|
50
|
+
}
|
51
|
+
|
52
|
+
skipWhitespace(query) {
|
53
|
+
if (!query.scan(/\s+/)) return;
|
54
|
+
|
55
|
+
return new Token(query.matched());
|
56
|
+
}
|
57
|
+
|
58
|
+
takeUntagged(query) {
|
59
|
+
if (!query.scan(/\S+/)) return;
|
60
|
+
|
61
|
+
return new Untagged(query.matched());
|
62
|
+
}
|
63
|
+
|
64
|
+
takeTagged(query) {
|
65
|
+
if (!query.scan(/(\w+(?:\.\w+)?)(:\s*)/)) return;
|
66
|
+
|
67
|
+
const key = query.valueAt(1);
|
68
|
+
const separator = query.valueAt(2);
|
69
|
+
|
70
|
+
const value =
|
71
|
+
this.takeArrayValue(query) || this.takeSingleValue(query) || new Token();
|
72
|
+
|
73
|
+
return new Tagged(key, separator, value);
|
74
|
+
}
|
75
|
+
|
76
|
+
takeArrayValue(query) {
|
77
|
+
if (!query.scan(/\[\s*/)) return;
|
78
|
+
|
79
|
+
const start = new Token(query.matched());
|
80
|
+
const values = (this.values = []);
|
81
|
+
|
82
|
+
while (!query.isEos()) {
|
83
|
+
if (!this.push(this.takeSingleValue(query))) break;
|
84
|
+
if (!this.push(this.takeDelimiter(query))) break;
|
85
|
+
}
|
86
|
+
|
87
|
+
query.scan(/\s*]/);
|
88
|
+
const end = new Token(query.matched());
|
89
|
+
|
90
|
+
this.values = null;
|
91
|
+
|
92
|
+
return new Array(start, values, end);
|
93
|
+
}
|
94
|
+
|
95
|
+
takeDelimiter(query) {
|
96
|
+
if (!query.scan(/\s*,\s*/)) return;
|
97
|
+
|
98
|
+
return new Token(query.matched());
|
99
|
+
}
|
100
|
+
|
101
|
+
takeSingleValue(query) {
|
102
|
+
return this.takeQuotedValue(query) || this.takeUnquotedValue(query);
|
103
|
+
}
|
104
|
+
|
105
|
+
takeQuotedValue(query) {
|
106
|
+
if (!query.scan(/"([^"]*)"/)) return;
|
107
|
+
|
108
|
+
return new Value(query.matched());
|
109
|
+
}
|
110
|
+
|
111
|
+
takeUnquotedValue(query) {
|
112
|
+
if (!query.scan(/[^ \],]*/)) return;
|
113
|
+
|
114
|
+
return new Value(query.matched());
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
class Token {
|
119
|
+
constructor(value = "") {
|
120
|
+
this.value = value;
|
121
|
+
}
|
122
|
+
|
123
|
+
render() {
|
124
|
+
return document.createTextNode(this.value);
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
class Value extends Token {
|
129
|
+
render() {
|
130
|
+
const span = document.createElement("span");
|
131
|
+
span.className = "value";
|
132
|
+
span.innerText = this.value;
|
133
|
+
|
134
|
+
return span;
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
class Tagged extends Token {
|
139
|
+
constructor(key, separator, value) {
|
140
|
+
super();
|
141
|
+
|
142
|
+
this.key = key;
|
143
|
+
this.separator = separator;
|
144
|
+
this.value = value;
|
145
|
+
}
|
146
|
+
|
147
|
+
render() {
|
148
|
+
const span = document.createElement("span");
|
149
|
+
span.className = "tag";
|
150
|
+
|
151
|
+
const key = document.createElement("span");
|
152
|
+
key.className = "key";
|
153
|
+
key.innerText = this.key;
|
154
|
+
|
155
|
+
span.appendChild(key);
|
156
|
+
span.appendChild(document.createTextNode(this.separator));
|
157
|
+
span.appendChild(this.value.render());
|
158
|
+
|
159
|
+
return span;
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
class Untagged extends Token {
|
164
|
+
render() {
|
165
|
+
const span = document.createElement("span");
|
166
|
+
span.className = "untagged";
|
167
|
+
span.innerText = this.value;
|
168
|
+
return span;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
class Array extends Token {
|
173
|
+
constructor(start, values, end) {
|
174
|
+
super();
|
175
|
+
|
176
|
+
this.start = start;
|
177
|
+
this.values = values;
|
178
|
+
this.end = end;
|
179
|
+
}
|
180
|
+
|
181
|
+
render() {
|
182
|
+
const array = document.createElement("span");
|
183
|
+
array.className = "array-values";
|
184
|
+
array.appendChild(this.start.render());
|
185
|
+
|
186
|
+
this.values.forEach((value) => {
|
187
|
+
const span = document.createElement("span");
|
188
|
+
span.appendChild(value.render());
|
189
|
+
array.appendChild(span);
|
190
|
+
});
|
191
|
+
|
192
|
+
array.appendChild(this.end.render());
|
193
|
+
|
194
|
+
return array;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
class StringScanner {
|
199
|
+
constructor(input) {
|
200
|
+
this.input = input;
|
201
|
+
this.position = 0;
|
202
|
+
this.last = null;
|
203
|
+
}
|
204
|
+
|
205
|
+
isEos() {
|
206
|
+
return this.position >= this.input.length;
|
207
|
+
}
|
208
|
+
|
209
|
+
scan(regex) {
|
210
|
+
const match = regex.exec(this.input.substring(this.position));
|
211
|
+
if (match?.index === 0) {
|
212
|
+
this.last = match;
|
213
|
+
this.position += match[0].length;
|
214
|
+
return true;
|
215
|
+
} else {
|
216
|
+
this.last = {};
|
217
|
+
return false;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
matched() {
|
222
|
+
return this.last && this.last[0];
|
223
|
+
}
|
224
|
+
|
225
|
+
valueAt(index) {
|
226
|
+
return this.last && this.last[index];
|
227
|
+
}
|
228
|
+
}
|
@@ -46,7 +46,7 @@ module Katalyst
|
|
46
46
|
end
|
47
47
|
|
48
48
|
included do
|
49
|
-
attr_accessor :items
|
49
|
+
attr_accessor :items, :unscoped_items
|
50
50
|
|
51
51
|
delegate :each, :count, :empty?, to: :items, allow_nil: true
|
52
52
|
end
|
@@ -73,7 +73,7 @@ module Katalyst
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def apply(items)
|
76
|
-
@items = items
|
76
|
+
@unscoped_items = @items = items
|
77
77
|
reducers.build do |_|
|
78
78
|
filter
|
79
79
|
self
|
@@ -5,49 +5,36 @@ module Katalyst
|
|
5
5
|
module Collection
|
6
6
|
module Query
|
7
7
|
class ArrayValueParser < ValueParser
|
8
|
+
def initialize(...)
|
9
|
+
super
|
10
|
+
|
11
|
+
@value = []
|
12
|
+
end
|
13
|
+
|
8
14
|
# @param query [StringScanner]
|
9
15
|
def parse(query)
|
10
16
|
@query = query
|
11
17
|
|
12
|
-
|
13
|
-
|
14
|
-
if query.scan(/#{'\['}/)
|
15
|
-
take_values
|
16
|
-
else
|
17
|
-
take_value
|
18
|
-
end
|
19
|
-
end
|
18
|
+
query.scan(/#{'\['}\s*/)
|
20
19
|
|
21
|
-
def take_values
|
22
20
|
until query.eos?
|
23
|
-
skip_whitespace
|
24
21
|
break unless take_quoted_value || take_unquoted_value
|
25
|
-
|
26
|
-
skip_whitespace
|
27
22
|
break unless take_delimiter
|
28
23
|
end
|
29
24
|
|
30
|
-
|
31
|
-
take_end_of_list
|
32
|
-
end
|
25
|
+
query.scan(/\s*#{'\]'}?/)
|
33
26
|
|
34
|
-
|
35
|
-
take_quoted_value || take_unquoted_value
|
36
|
-
end
|
27
|
+
@end = query.charpos
|
37
28
|
|
38
|
-
|
39
|
-
query.scan(/#{','}/)
|
29
|
+
self
|
40
30
|
end
|
41
31
|
|
42
|
-
def
|
43
|
-
query.scan(
|
32
|
+
def take_delimiter
|
33
|
+
query.scan(/\s*#{','}\s*/)
|
44
34
|
end
|
45
35
|
|
46
36
|
def value=(value)
|
47
|
-
|
48
|
-
|
49
|
-
current = @collection.attributes[@attribute.name]
|
50
|
-
@collection.assign_attributes(@attribute.name => current + [value])
|
37
|
+
@value << value
|
51
38
|
end
|
52
39
|
end
|
53
40
|
end
|
@@ -7,11 +7,12 @@ module Katalyst
|
|
7
7
|
class Parser # :nodoc:
|
8
8
|
# query [StringScanner]
|
9
9
|
attr_accessor :query
|
10
|
-
attr_reader :collection, :untagged
|
10
|
+
attr_reader :collection, :untagged, :tagged
|
11
11
|
|
12
12
|
def initialize(collection)
|
13
13
|
@collection = collection
|
14
|
-
@
|
14
|
+
@tagged = {}
|
15
|
+
@untagged = []
|
15
16
|
end
|
16
17
|
|
17
18
|
# @param query [String]
|
@@ -25,10 +26,6 @@ module Katalyst
|
|
25
26
|
break unless take_tagged || take_untagged
|
26
27
|
end
|
27
28
|
|
28
|
-
if untagged.any? && (search = collection.class.search_attribute)
|
29
|
-
collection.assign_attributes(search => untagged.join(" "))
|
30
|
-
end
|
31
|
-
|
32
29
|
self
|
33
30
|
end
|
34
31
|
|
@@ -39,12 +36,14 @@ module Katalyst
|
|
39
36
|
end
|
40
37
|
|
41
38
|
def take_tagged
|
39
|
+
start = query.charpos
|
40
|
+
|
42
41
|
return unless query.scan(/(\w+(\.\w+)?):/)
|
43
42
|
|
44
43
|
key, = query.values_at(1)
|
45
44
|
skip_whitespace
|
46
45
|
|
47
|
-
|
46
|
+
tagged[key] = value_parser(start).parse(query)
|
48
47
|
end
|
49
48
|
|
50
49
|
def take_untagged
|
@@ -57,14 +56,18 @@ module Katalyst
|
|
57
56
|
|
58
57
|
using Type::Helpers::Extensions
|
59
58
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
if attribute.type.multiple? || attribute.value.is_a?(::Array)
|
64
|
-
ArrayValueParser.new(collection:, attribute:)
|
59
|
+
def value_parser(start)
|
60
|
+
if query.check(/#{'\['}\s*/)
|
61
|
+
ArrayValueParser.new(start:)
|
65
62
|
else
|
66
|
-
SingleValueParser.new(
|
63
|
+
SingleValueParser.new(start:)
|
67
64
|
end
|
65
|
+
|
66
|
+
# if attribute.type.multiple? || attribute.value.is_a?(::Array)
|
67
|
+
# ArrayValueParser.new(attribute:, pos:)
|
68
|
+
# else
|
69
|
+
# SingleValueParser.new(attribute:, pos:)
|
70
|
+
# end
|
68
71
|
end
|
69
72
|
end
|
70
73
|
end
|
@@ -5,17 +5,25 @@ module Katalyst
|
|
5
5
|
module Collection
|
6
6
|
module Query
|
7
7
|
class SingleValueParser < ValueParser
|
8
|
+
def initialize(...)
|
9
|
+
super
|
10
|
+
|
11
|
+
@value = nil
|
12
|
+
end
|
13
|
+
|
8
14
|
# @param query [StringScanner]
|
9
15
|
def parse(query)
|
10
16
|
@query = query
|
11
17
|
|
12
18
|
take_quoted_value || take_unquoted_value
|
19
|
+
|
20
|
+
@end = query.charpos
|
21
|
+
|
22
|
+
self
|
13
23
|
end
|
14
24
|
|
15
25
|
def value=(value)
|
16
|
-
|
17
|
-
|
18
|
-
@collection.assign_attributes(@attribute.name => value)
|
26
|
+
@value = value
|
19
27
|
end
|
20
28
|
end
|
21
29
|
end
|
@@ -5,11 +5,14 @@ module Katalyst
|
|
5
5
|
module Collection
|
6
6
|
module Query
|
7
7
|
class ValueParser
|
8
|
-
attr_accessor :query
|
8
|
+
attr_accessor :query, :value
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
|
10
|
+
def initialize(start:)
|
11
|
+
@start = start
|
12
|
+
end
|
13
|
+
|
14
|
+
def range
|
15
|
+
@start..@end
|
13
16
|
end
|
14
17
|
|
15
18
|
def take_quoted_value
|
@@ -19,7 +22,9 @@ module Katalyst
|
|
19
22
|
end
|
20
23
|
|
21
24
|
def take_unquoted_value
|
22
|
-
|
25
|
+
# note, we allow unquoted values to begin with a " so that partial
|
26
|
+
# inputs can be accepted
|
27
|
+
return unless query.scan(/"?([^ \],]*)/)
|
23
28
|
|
24
29
|
self.value, = query.values_at(1)
|
25
30
|
end
|
@@ -13,6 +13,8 @@ module Katalyst
|
|
13
13
|
_default_attributes.each_value do |attribute|
|
14
14
|
return attribute.name if attribute.type.type == :search
|
15
15
|
end
|
16
|
+
|
17
|
+
nil
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
@@ -20,14 +22,49 @@ module Katalyst
|
|
20
22
|
attribute :q, :query, default: ""
|
21
23
|
alias_attribute :query, :q
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
attribute :p, :integer, filter: false
|
26
|
+
alias_attribute :position, :p
|
27
|
+
end
|
28
|
+
|
29
|
+
using Type::Helpers::Extensions
|
30
|
+
|
31
|
+
def examples_for(key)
|
32
|
+
key = key.to_s
|
33
|
+
values_method = "#{key.parameterize.underscore}_values"
|
34
|
+
if respond_to?(values_method)
|
35
|
+
public_send(values_method)
|
36
|
+
elsif @attributes.key?(key)
|
37
|
+
@attributes[key].type.examples_for(unscoped_items, @attributes[key])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def query_active?(attribute)
|
42
|
+
@attributes[attribute].query_range&.cover?(position)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
26
46
|
|
27
|
-
|
47
|
+
def _assign_attributes(new_attributes)
|
48
|
+
result = super
|
28
49
|
|
29
|
-
|
50
|
+
if query_changed?
|
51
|
+
parser = Parser.new(self).parse(query)
|
52
|
+
|
53
|
+
parser.tagged.each do |k, p|
|
54
|
+
if @attributes.key?(k)
|
55
|
+
_assign_attribute(k, p.value)
|
56
|
+
@attributes[k].query_range = p.range
|
57
|
+
else
|
58
|
+
errors.add(k, :unknown)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if parser.untagged.any? && (search = self.class.search_attribute)
|
63
|
+
_assign_attribute(search, parser.untagged.join(" "))
|
64
|
+
end
|
30
65
|
end
|
66
|
+
|
67
|
+
result
|
31
68
|
end
|
32
69
|
end
|
33
70
|
end
|
@@ -35,6 +35,16 @@ module Katalyst
|
|
35
35
|
{ column:, direction: }
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
refine Symbol do
|
40
|
+
def to_param
|
41
|
+
to_s.to_param
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_h
|
45
|
+
to_s.to_h
|
46
|
+
end
|
47
|
+
end
|
38
48
|
end
|
39
49
|
|
40
50
|
using SortParams
|
@@ -116,7 +126,7 @@ module Katalyst
|
|
116
126
|
|
117
127
|
if collection.items.respond_to?(:"order_by_#{column}")
|
118
128
|
collection.items = collection.items.reorder(nil).public_send(:"order_by_#{column}", direction.to_sym)
|
119
|
-
elsif collection.
|
129
|
+
elsif collection.model.has_attribute?(column)
|
120
130
|
collection.items = collection.items.reorder(column => direction)
|
121
131
|
end
|
122
132
|
|
@@ -48,6 +48,16 @@ module Katalyst
|
|
48
48
|
self
|
49
49
|
end
|
50
50
|
|
51
|
+
def apply(items)
|
52
|
+
if items.is_a?(Class) && items < ActiveRecord::Base
|
53
|
+
super(items.all)
|
54
|
+
elsif items.is_a?(ActiveRecord::Relation)
|
55
|
+
super
|
56
|
+
else
|
57
|
+
raise ArgumentError, "Collection requires an ActiveRecord scope, given #{items.class}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
51
61
|
def inspect
|
52
62
|
"#<#{self.class.name} @attributes=#{attributes.inspect} @model_name=\"#{model_name}\" @count=#{items&.count}>"
|
53
63
|
end
|
@@ -69,6 +69,16 @@ module Katalyst
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
def apply(items)
|
73
|
+
if items.is_a?(Class) && items < ActiveRecord::Base
|
74
|
+
super(items.all)
|
75
|
+
elsif items.is_a?(ActiveRecord::Relation)
|
76
|
+
super
|
77
|
+
else
|
78
|
+
raise ArgumentError, "Collection requires an ActiveRecord scope, given #{items.class}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
72
82
|
def inspect
|
73
83
|
"#<#{self.class.name} @param_key=#{param_key.inspect} " +
|
74
84
|
"@attributes=#{attributes.inspect} @model=\"#{model}\" @count=#{items&.count}>"
|
@@ -13,7 +13,17 @@ module Katalyst
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def filter?(attribute, value)
|
16
|
-
|
16
|
+
return false unless filterable?
|
17
|
+
|
18
|
+
if attribute.came_from_user?
|
19
|
+
attribute.value_before_type_cast.present? || value === false
|
20
|
+
else
|
21
|
+
!value.nil? && !value.eql?([])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def examples_for(...)
|
26
|
+
[true, false]
|
17
27
|
end
|
18
28
|
end
|
19
29
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Tables
|
5
|
+
module Collection
|
6
|
+
module Type
|
7
|
+
class Date < Value
|
8
|
+
include Helpers::Range
|
9
|
+
|
10
|
+
define_range_patterns /\d{4}-\d\d-\d\d/
|
11
|
+
|
12
|
+
def type
|
13
|
+
:date
|
14
|
+
end
|
15
|
+
|
16
|
+
def serialize(value)
|
17
|
+
if value.is_a?(::Date)
|
18
|
+
value.to_fs(:db)
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def examples_for(...)
|
25
|
+
[
|
26
|
+
::Date.current,
|
27
|
+
::Date.yesterday,
|
28
|
+
::Date.current.beginning_of_week..,
|
29
|
+
::Date.current.beginning_of_month..,
|
30
|
+
::Date.current.beginning_of_year..,
|
31
|
+
].map { |d| serialize(d) }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
ISO_DATE = /\A(?<year>\d{4})-(?<month>\d\d)-(?<day>\d\d)\z/
|
37
|
+
|
38
|
+
def cast_value(value)
|
39
|
+
return value unless value.is_a?(::String)
|
40
|
+
|
41
|
+
if /\A(?<year>\d{4})-(?<month>\d\d)-(?<day>\d\d)\z/ =~ value
|
42
|
+
new_date(year.to_i, month.to_i, day.to_i)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def new_date(year, mon, mday)
|
47
|
+
return nil if year.nil? || (year.zero? && mon.zero? && mday.zero?)
|
48
|
+
|
49
|
+
::Date.new(year, mon, mday)
|
50
|
+
rescue ArgumentError, TypeError
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Tables
|
5
|
+
module Collection
|
6
|
+
module Type
|
7
|
+
class Enum < Value
|
8
|
+
include Helpers::Multiple
|
9
|
+
|
10
|
+
def initialize(multiple: true, **)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
:enum
|
16
|
+
end
|
17
|
+
|
18
|
+
def examples_for(scope, attribute)
|
19
|
+
_, model, column = model_and_column_for(scope, attribute)
|
20
|
+
keys = model.defined_enums[column]&.keys
|
21
|
+
|
22
|
+
if attribute.value_before_type_cast.present?
|
23
|
+
keys.select { |key| key.include?(attribute.value_before_type_cast.last) }
|
24
|
+
else
|
25
|
+
keys
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Katalyst
|
4
|
+
module Tables
|
5
|
+
module Collection
|
6
|
+
module Type
|
7
|
+
class Float < Value
|
8
|
+
include Helpers::Delegate
|
9
|
+
include Helpers::Multiple
|
10
|
+
include Helpers::Range
|
11
|
+
|
12
|
+
define_range_patterns(/-?\d+(?:\.\d+)?/)
|
13
|
+
|
14
|
+
def initialize(**)
|
15
|
+
super(**, delegate: ActiveModel::Type::Float)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|