katalyst-tables 3.5.4 → 3.6.0

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.
@@ -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.4
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-29 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