katalyst-tables 3.3.0 → 3.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 +12 -0
- data/app/components/katalyst/tables/query/input_component.rb +46 -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 +5 -3
- 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 +44 -6
- 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/app/models/katalyst/tables/collection/type/boolean.rb +11 -1
- data/app/models/katalyst/tables/collection/type/date.rb +19 -22
- data/app/models/katalyst/tables/collection/type/enum.rb +11 -0
- data/app/models/katalyst/tables/collection/type/float.rb +3 -39
- data/app/models/katalyst/tables/collection/type/helpers/delegate.rb +2 -22
- data/app/models/katalyst/tables/collection/type/helpers/extensions.rb +14 -0
- data/app/models/katalyst/tables/collection/type/helpers/multiple.rb +30 -0
- data/app/models/katalyst/tables/collection/type/helpers/range.rb +59 -0
- data/app/models/katalyst/tables/collection/type/integer.rb +3 -39
- data/app/models/katalyst/tables/collection/type/value.rb +22 -2
- metadata +12 -8
- 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
@@ -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
|
@@ -86,7 +86,9 @@ module Katalyst
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def filters
|
89
|
-
changes
|
89
|
+
changes
|
90
|
+
.select { |k, _| self.class._default_attributes[k].type.filterable? }
|
91
|
+
.transform_values(&:second)
|
90
92
|
end
|
91
93
|
|
92
94
|
def model
|
@@ -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,20 +13,58 @@ 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
|
|
19
21
|
included do
|
20
|
-
attribute :
|
22
|
+
attribute :q, :query, default: ""
|
23
|
+
alias_attribute :query, :q
|
24
|
+
|
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
|
21
40
|
|
22
|
-
|
23
|
-
|
24
|
-
|
41
|
+
def query_active?(attribute)
|
42
|
+
@attributes[attribute].query_range&.cover?(position)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
25
46
|
|
26
|
-
|
47
|
+
def _assign_attributes(new_attributes)
|
48
|
+
result = super
|
27
49
|
|
28
|
-
|
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
|
29
65
|
end
|
66
|
+
|
67
|
+
result
|
30
68
|
end
|
31
69
|
end
|
32
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
|
@@ -5,44 +5,41 @@ module Katalyst
|
|
5
5
|
module Collection
|
6
6
|
module Type
|
7
7
|
class Date < Value
|
8
|
+
include Helpers::Range
|
9
|
+
|
10
|
+
define_range_patterns /\d{4}-\d\d-\d\d/
|
11
|
+
|
8
12
|
def type
|
9
13
|
:date
|
10
14
|
end
|
11
15
|
|
12
16
|
def serialize(value)
|
13
|
-
if value.is_a?(Date)
|
17
|
+
if value.is_a?(::Date)
|
14
18
|
value.to_fs(:db)
|
15
|
-
elsif value.is_a?(Range)
|
16
|
-
if value.begin.nil?
|
17
|
-
"<#{value.end.to_fs(:db)}"
|
18
|
-
elsif value.end.nil?
|
19
|
-
">#{value.begin.to_fs(:db)}"
|
20
|
-
else
|
21
|
-
"#{value.begin.to_fs(:db)}..#{value.end.to_fs(:db)}"
|
22
|
-
end
|
23
19
|
else
|
24
|
-
|
20
|
+
super
|
25
21
|
end
|
26
22
|
end
|
27
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
|
+
|
28
34
|
private
|
29
35
|
|
30
|
-
ISO_DATE = /\A(
|
31
|
-
LOWER_BOUND = /\A>(\d{4})-(\d\d)-(\d\d)\z/
|
32
|
-
UPPER_BOUND = /\A<(\d{4})-(\d\d)-(\d\d)\z/
|
33
|
-
BOUNDED = /\A(\d{4})-(\d\d)-(\d\d)\.\.(\d{4})-(\d\d)-(\d\d)\z/
|
36
|
+
ISO_DATE = /\A(?<year>\d{4})-(?<month>\d\d)-(?<day>\d\d)\z/
|
34
37
|
|
35
38
|
def cast_value(value)
|
36
39
|
return value unless value.is_a?(::String)
|
37
40
|
|
38
|
-
if
|
39
|
-
new_date(
|
40
|
-
elsif value =~ LOWER_BOUND
|
41
|
-
(new_date($1.to_i, $2.to_i, $3.to_i)..)
|
42
|
-
elsif value =~ UPPER_BOUND
|
43
|
-
(..new_date($1.to_i, $2.to_i, $3.to_i))
|
44
|
-
elsif value =~ BOUNDED
|
45
|
-
(new_date($1.to_i, $2.to_i, $3.to_i)..new_date($4.to_i, $5.to_i, $6.to_i))
|
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)
|
46
43
|
end
|
47
44
|
end
|
48
45
|
|
@@ -14,6 +14,17 @@ module Katalyst
|
|
14
14
|
def type
|
15
15
|
:enum
|
16
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
|
17
28
|
end
|
18
29
|
end
|
19
30
|
end
|