katalyst-tables 3.5.5 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,6 +24,49 @@ export default class QueryInputController extends Controller {
24
24
  this.highlightTarget.appendChild(token.render());
25
25
  });
26
26
  }
27
+
28
+ replaceToken(e) {
29
+ let tokenToAdd = e.detail.token.toString();
30
+
31
+ // wrap in quotes if it contains a spaces or special characters
32
+ if (/\s/.exec(tokenToAdd)) {
33
+ tokenToAdd = `"${tokenToAdd}"`;
34
+ }
35
+
36
+ const indexPosition = e.detail.position;
37
+ let caretPosition = indexPosition + tokenToAdd.length;
38
+ let sliceStart = indexPosition;
39
+ let sliceEnd = indexPosition;
40
+
41
+ // detect if position has a token already, if so, replace it
42
+ const existingToken = new Parser()
43
+ .parse(this.queryValue)
44
+ .tokenAtPosition(indexPosition);
45
+ if (existingToken) {
46
+ // We don't want to include the trailing space as we are replacing an existing value
47
+ tokenToAdd = tokenToAdd.trim();
48
+
49
+ // Slice up to the beginning of the tokens value (not the initial caret position)
50
+ sliceStart = existingToken.startOfValue();
51
+
52
+ // Slice after the end of the tokens value
53
+ sliceEnd = existingToken.endOfValue();
54
+
55
+ // The end position of the newly added token
56
+ caretPosition = sliceStart + tokenToAdd.length;
57
+ }
58
+
59
+ // Replace any text within sliceStart and sliceEnd with tokenToAdd
60
+ this.inputTarget.value =
61
+ this.queryValue.slice(0, sliceStart) +
62
+ tokenToAdd +
63
+ this.queryValue.slice(sliceEnd);
64
+
65
+ // Re focus the input at the end of the newly added token
66
+ this.update();
67
+ this.inputTarget.focus();
68
+ this.inputTarget.setSelectionRange(caretPosition, caretPosition);
69
+ }
27
70
  }
28
71
 
29
72
  class Parser {
@@ -57,13 +100,13 @@ class Parser {
57
100
  skipWhitespace(query) {
58
101
  if (!query.scan(/\s+/)) return;
59
102
 
60
- return new Token(query.matched());
103
+ return new Token(query.matched(), query.position);
61
104
  }
62
105
 
63
106
  takeUntagged(query) {
64
107
  if (!query.scan(/\S+/)) return;
65
108
 
66
- return new Untagged(query.matched());
109
+ return new Untagged(query.matched(), query.position);
67
110
  }
68
111
 
69
112
  takeTagged(query) {
@@ -75,13 +118,13 @@ class Parser {
75
118
  const value =
76
119
  this.takeArrayValue(query) || this.takeSingleValue(query) || new Token();
77
120
 
78
- return new Tagged(key, separator, value);
121
+ return new Tagged(key, separator, value, query.position);
79
122
  }
80
123
 
81
124
  takeArrayValue(query) {
82
125
  if (!query.scan(/\[\s*/)) return;
83
126
 
84
- const start = new Token(query.matched());
127
+ const start = new Token(query.matched(), query.position);
85
128
  const values = (this.values = []);
86
129
 
87
130
  while (!query.isEos()) {
@@ -90,17 +133,17 @@ class Parser {
90
133
  }
91
134
 
92
135
  query.scan(/\s*]/);
93
- const end = new Token(query.matched());
136
+ const end = new Token(query.matched(), query.position);
94
137
 
95
138
  this.values = null;
96
139
 
97
- return new Array(start, values, end);
140
+ return new ArrayToken(start, values, end);
98
141
  }
99
142
 
100
143
  takeDelimiter(query) {
101
144
  if (!query.scan(/\s*,\s*/)) return;
102
145
 
103
- return new Token(query.matched());
146
+ return new Token(query.matched(), query.position);
104
147
  }
105
148
 
106
149
  takeSingleValue(query) {
@@ -110,24 +153,49 @@ class Parser {
110
153
  takeQuotedValue(query) {
111
154
  if (!query.scan(/"([^"]*)"/)) return;
112
155
 
113
- return new Value(query.matched());
156
+ return new Value(query.matched(), query.position);
114
157
  }
115
158
 
116
159
  takeUnquotedValue(query) {
117
160
  if (!query.scan(/[^ \],]*/)) return;
118
161
 
119
- return new Value(query.matched());
162
+ return new Value(query.matched(), query.position);
163
+ }
164
+
165
+ tokenAtPosition(position) {
166
+ return this.tokens
167
+ .filter((t) => t instanceof Tagged || t instanceof Untagged)
168
+ .find((t) => t.range.includes(position));
120
169
  }
121
170
  }
122
171
 
123
172
  class Token {
124
- constructor(value = "") {
173
+ constructor(value = "", position) {
125
174
  this.value = value;
175
+ this.length = this.value.length;
176
+ this.start = position - this.length;
177
+ this.end = this.start + this.length;
178
+ this.range = this.arrayRange(this.start, this.end);
126
179
  }
127
180
 
128
181
  render() {
129
182
  return document.createTextNode(this.value);
130
183
  }
184
+
185
+ arrayRange(start, stop) {
186
+ return Array.from(
187
+ { length: stop - start + 1 },
188
+ (value, index) => start + index,
189
+ );
190
+ }
191
+
192
+ startOfValue() {
193
+ return this.start;
194
+ }
195
+
196
+ endOfValue() {
197
+ return this.end;
198
+ }
131
199
  }
132
200
 
133
201
  class Value extends Token {
@@ -141,12 +209,16 @@ class Value extends Token {
141
209
  }
142
210
 
143
211
  class Tagged extends Token {
144
- constructor(key, separator, value) {
212
+ constructor(key, separator, value, position) {
145
213
  super();
146
214
 
147
215
  this.key = key;
148
216
  this.separator = separator;
149
217
  this.value = value;
218
+ this.length = key.length + separator.length + value.value.length;
219
+ this.start = position - this.length;
220
+ this.end = this.start + this.length;
221
+ this.range = this.arrayRange(this.start, this.end);
150
222
  }
151
223
 
152
224
  render() {
@@ -163,6 +235,14 @@ class Tagged extends Token {
163
235
 
164
236
  return span;
165
237
  }
238
+
239
+ startOfValue() {
240
+ return this.value.startOfValue();
241
+ }
242
+
243
+ endOfValue() {
244
+ return this.value.endOfValue();
245
+ }
166
246
  }
167
247
 
168
248
  class Untagged extends Token {
@@ -174,13 +254,18 @@ class Untagged extends Token {
174
254
  }
175
255
  }
176
256
 
177
- class Array extends Token {
257
+ class ArrayToken extends Token {
178
258
  constructor(start, values, end) {
179
259
  super();
180
260
 
181
261
  this.start = start;
182
262
  this.values = values;
183
263
  this.end = end;
264
+ this.range = this.arrayRange(start.start, end.range[end.length]);
265
+ this.length =
266
+ start.length +
267
+ values.reduce((length, value) => length + value.length, 0) +
268
+ end.length;
184
269
  }
185
270
 
186
271
  render() {
@@ -198,6 +283,14 @@ class Array extends Token {
198
283
 
199
284
  return array;
200
285
  }
286
+
287
+ startOfValue() {
288
+ return this.start.start;
289
+ }
290
+
291
+ endOfValue() {
292
+ return this.end.end;
293
+ }
201
294
  }
202
295
 
203
296
  class StringScanner {
@@ -12,5 +12,5 @@ en:
12
12
  errors:
13
13
  attributes:
14
14
  query:
15
- unknown_key: "The field '%{input}' isn’t searchable here. Please check your input."
16
- no_untagged_search: "'%{input}' isn't searchable here. Please choose a field to search."
15
+ unknown_key: "The search option '%{input}' isn’t valid. Please check your input."
16
+ no_untagged_search: "'%{input}' isn't searchable. Please choose a valid search option."
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katalyst-tables
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.5
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-30 00:00:00.000000000 Z
11
+ date: 2024-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: katalyst-html-attributes