@wral/studio.ui.search-filter 0.1.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.
- package/README.md +197 -0
- package/dist/define.mjs +11 -0
- package/dist/define.standalone.js +1603 -0
- package/dist/index.mjs +17 -0
- package/dist/model.mjs +582 -0
- package/dist/search-filter-B-aiJCKp.js +453 -0
- package/package.json +40 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { LitElement as m, html as o, nothing as n, css as v } from "lit";
|
|
2
|
+
import { repeat as c } from "lit/directives/repeat.js";
|
|
3
|
+
import { createQueryModel as h, resolveLabels as g, validateSyntax as b } from "./model.mjs";
|
|
4
|
+
const _ = ["fields", "operators", "booleans", "defaults"];
|
|
5
|
+
class x extends m {
|
|
6
|
+
static get properties() {
|
|
7
|
+
return {
|
|
8
|
+
value: { type: String },
|
|
9
|
+
fields: { type: Object },
|
|
10
|
+
operators: { type: Object },
|
|
11
|
+
booleans: { type: Array },
|
|
12
|
+
defaults: { type: Object },
|
|
13
|
+
labels: { type: Object },
|
|
14
|
+
advanced: { type: Boolean, state: !0 },
|
|
15
|
+
rawError: { type: String, state: !0 },
|
|
16
|
+
configError: { type: String, state: !0 },
|
|
17
|
+
dragId: { type: String, state: !0 },
|
|
18
|
+
dropId: { type: String, state: !0 }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
constructor() {
|
|
22
|
+
super(), this.value = "", this.fields = null, this.operators = null, this.booleans = null, this.defaults = null, this.labels = null, this.configError = "", this._model = h(), this._labels = g(), this.root = this._model.newGroup(void 0, []), this.advanced = !1, this.canVisual = !0, this.rawError = "", this.dragId = null, this.dropId = null, this._emitted = void 0;
|
|
23
|
+
}
|
|
24
|
+
willUpdate(t) {
|
|
25
|
+
const e = _.some((i) => t.has(i));
|
|
26
|
+
e && this._rebuildModel(), t.has("labels") && (this._labels = g(this.labels || {})), (e && !this.configError || t.has("value") && this.value !== this._emitted) && this._syncFromValue();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Rebuild the catalog-bound model from the JSON config. A bad config
|
|
30
|
+
* (unknown type/operator, missing path, …) keeps the previous model
|
|
31
|
+
* and surfaces the error instead of crashing the host app.
|
|
32
|
+
*/
|
|
33
|
+
_rebuildModel() {
|
|
34
|
+
try {
|
|
35
|
+
this._model = h({
|
|
36
|
+
fields: this.fields ?? void 0,
|
|
37
|
+
operators: this.operators ?? void 0,
|
|
38
|
+
booleans: this.booleans ?? void 0,
|
|
39
|
+
defaults: this.defaults ?? void 0
|
|
40
|
+
}), this.configError = "";
|
|
41
|
+
} catch (t) {
|
|
42
|
+
this.configError = t.message;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
_syncFromValue() {
|
|
46
|
+
const t = this.value || "";
|
|
47
|
+
if (this._model.roundTrips(t)) {
|
|
48
|
+
this.canVisual = !0;
|
|
49
|
+
try {
|
|
50
|
+
this.root = this._model.parseLucene(t);
|
|
51
|
+
} catch {
|
|
52
|
+
this.canVisual = !1, this.advanced = !0;
|
|
53
|
+
}
|
|
54
|
+
} else
|
|
55
|
+
this.canVisual = !1, this.advanced = !0;
|
|
56
|
+
}
|
|
57
|
+
_commit() {
|
|
58
|
+
this.root = { ...this.root }, this._emit(this._model.toLucene(this.root));
|
|
59
|
+
}
|
|
60
|
+
_emit(t) {
|
|
61
|
+
this._emitted = t, this.value = t, this.dispatchEvent(new CustomEvent("change", {
|
|
62
|
+
detail: { value: t }
|
|
63
|
+
})), this.requestUpdate();
|
|
64
|
+
}
|
|
65
|
+
/* ---- tree helpers ---- */
|
|
66
|
+
_kids(t) {
|
|
67
|
+
return t.kind === "group" ? t.children : t.kind === "conditional" ? [t.cond, t.then, t.els].filter(Boolean) : [];
|
|
68
|
+
}
|
|
69
|
+
_find(t, e) {
|
|
70
|
+
if (t.id === e) return t;
|
|
71
|
+
for (const i of this._kids(t)) {
|
|
72
|
+
const s = this._find(i, e);
|
|
73
|
+
if (s) return s;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
_parent(t, e, i = null) {
|
|
78
|
+
if (t.id === e) return i;
|
|
79
|
+
for (const s of this._kids(t)) {
|
|
80
|
+
const a = this._parent(s, e, t);
|
|
81
|
+
if (a !== void 0) return a;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/* ---- mutations ---- */
|
|
85
|
+
_addRule(t) {
|
|
86
|
+
this._find(this.root, t).children.push(this._model.newRule()), this._commit();
|
|
87
|
+
}
|
|
88
|
+
_addCond(t) {
|
|
89
|
+
this._find(this.root, t).children.push(this._model.newConditional()), this._commit();
|
|
90
|
+
}
|
|
91
|
+
_remove(t) {
|
|
92
|
+
const e = this._parent(this.root, t);
|
|
93
|
+
!e || e.kind !== "group" || (e.children = e.children.filter((i) => i.id !== t), this._commit());
|
|
94
|
+
}
|
|
95
|
+
_setOp(t, e) {
|
|
96
|
+
this._find(this.root, t).op = e, this._commit();
|
|
97
|
+
}
|
|
98
|
+
_toggleNeg(t) {
|
|
99
|
+
const e = this._find(this.root, t);
|
|
100
|
+
e.negate = !e.negate, this._commit();
|
|
101
|
+
}
|
|
102
|
+
_patch(t, e) {
|
|
103
|
+
const i = this._find(this.root, t);
|
|
104
|
+
Object.assign(i, e), e.field && (i.op = this._model.fields[e.field].ops[0].id, i.value = "", i.value2 = ""), this._commit();
|
|
105
|
+
}
|
|
106
|
+
_dropEls(t) {
|
|
107
|
+
this._find(this.root, t).els = null, this._commit();
|
|
108
|
+
}
|
|
109
|
+
_addEls(t) {
|
|
110
|
+
this._find(this.root, t).els = this._model.newElse(), this._commit();
|
|
111
|
+
}
|
|
112
|
+
/* ---- drag to reorder ---- */
|
|
113
|
+
_dragStart(t, e) {
|
|
114
|
+
this.dragId = e, t.dataTransfer.effectAllowed = "move";
|
|
115
|
+
}
|
|
116
|
+
/** A drag can only drop onto a sibling in the same group. */
|
|
117
|
+
_canDrop(t) {
|
|
118
|
+
const e = this.dragId;
|
|
119
|
+
if (!e || e === t) return !1;
|
|
120
|
+
const i = this._parent(this.root, e), s = this._parent(this.root, t);
|
|
121
|
+
return !!i && i === s && i.kind === "group";
|
|
122
|
+
}
|
|
123
|
+
_dragOver(t, e) {
|
|
124
|
+
t.preventDefault(), e !== this.dropId && this._canDrop(e) && (this.dropId = e);
|
|
125
|
+
}
|
|
126
|
+
_dragEnd() {
|
|
127
|
+
this.dragId = null, this.dropId = null;
|
|
128
|
+
}
|
|
129
|
+
_drop(t, e) {
|
|
130
|
+
t.preventDefault();
|
|
131
|
+
const i = this.dragId, s = this._canDrop(e);
|
|
132
|
+
if (this.dragId = null, this.dropId = null, !s) return;
|
|
133
|
+
const l = this._parent(this.root, i).children, u = l.findIndex((r) => r.id === i), d = l.findIndex((r) => r.id === e);
|
|
134
|
+
if (u < 0 || d < 0) return;
|
|
135
|
+
const [p] = l.splice(u, 1);
|
|
136
|
+
l.splice(d, 0, p), this._commit();
|
|
137
|
+
}
|
|
138
|
+
/* ---- raw (advanced) mode ---- */
|
|
139
|
+
_syncRaw(t) {
|
|
140
|
+
if (this.rawError = b(t), !this.rawError) {
|
|
141
|
+
if (this._model.roundTrips(t)) {
|
|
142
|
+
this.canVisual = !0;
|
|
143
|
+
try {
|
|
144
|
+
this.root = this._model.parseLucene(t);
|
|
145
|
+
} catch {
|
|
146
|
+
this.canVisual = !1;
|
|
147
|
+
}
|
|
148
|
+
} else
|
|
149
|
+
this.canVisual = !1;
|
|
150
|
+
this._emit(t);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
_toggleAdvanced() {
|
|
154
|
+
this.advanced && !this.canVisual || (this.advanced = !this.advanced);
|
|
155
|
+
}
|
|
156
|
+
render() {
|
|
157
|
+
const t = this._labels;
|
|
158
|
+
return this.configError ? o`<p class="err">${this.configError}</p>` : o`
|
|
159
|
+
<div class="qbar">
|
|
160
|
+
${this.canVisual ? this.advanced ? n : o`<span class="intro">
|
|
161
|
+
${t.intro}</span>` : o`<span class="note">${t.rawUnsupported}
|
|
162
|
+
</span>`}
|
|
163
|
+
<studio-button size="small" style-type="outline"
|
|
164
|
+
state=${this.advanced && !this.canVisual ? "disabled" : "default"}
|
|
165
|
+
background-color=${this.advanced ? "var(--studio-primary, #001D68)" : ""}
|
|
166
|
+
text-color=${this.advanced ? "#FFFFFF" : ""}
|
|
167
|
+
@click=${this._toggleAdvanced}>
|
|
168
|
+
${this.advanced ? t.visualButton : t.rawButton}</studio-button>
|
|
169
|
+
</div>
|
|
170
|
+
${this.advanced ? o`<textarea class="raw" .value=${this.value || ""}
|
|
171
|
+
@input=${(e) => this._syncRaw(e.target.value)}></textarea>
|
|
172
|
+
${this.rawError ? o`<p class="err">${this.rawError}</p>` : this.canVisual ? o`<p class="ok">${t.rawSynced}</p>` : o`<p class="note-raw">${t.rawSaved}</p>`}` : this._group(this.root, !0)}`;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* The library button keeps its :focus styling after a click (the
|
|
176
|
+
* group's focus suppression cannot cross the shadow boundary), so
|
|
177
|
+
* a just-clicked segment would look selected next to the truly
|
|
178
|
+
* selected one. Blur the inner button to keep the toggle state
|
|
179
|
+
* unambiguous.
|
|
180
|
+
*/
|
|
181
|
+
_blurButton(t) {
|
|
182
|
+
t.currentTarget?.renderRoot?.querySelector("button")?.blur();
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* all/any segmented toggle (mutually exclusive). The selected colour is
|
|
186
|
+
* driven by CSS keyed on aria-pressed (see the `.tog` rules), NOT by
|
|
187
|
+
* studio-button's background-color attribute. Two traps force this:
|
|
188
|
+
* studio-button does not inherit the group's style-type (so each child
|
|
189
|
+
* sets style-type="outline" to get a transparent unselected base), and
|
|
190
|
+
* changing its background-color through re-renders lags a render behind,
|
|
191
|
+
* leaving a deselected button still filled. Setting --button-bg-color
|
|
192
|
+
* from our own stylesheet sidesteps the component's colour pipeline.
|
|
193
|
+
*/
|
|
194
|
+
_segGroup(t, e) {
|
|
195
|
+
const i = (s, a) => o`
|
|
196
|
+
<studio-button class="tog primary" size="small" style-type="outline"
|
|
197
|
+
aria-pressed=${String(e === s)}
|
|
198
|
+
@click=${(l) => {
|
|
199
|
+
this._blurButton(l), this._setOp(t, s);
|
|
200
|
+
}}>${a}</studio-button>`;
|
|
201
|
+
return o`<studio-button-group size="small" style-type="outline"
|
|
202
|
+
role="group" aria-label="Match all or any">
|
|
203
|
+
${this._model.groupOps.map((s) => i(s.id, s.label))}
|
|
204
|
+
</studio-button-group>`;
|
|
205
|
+
}
|
|
206
|
+
_group(t, e = !1) {
|
|
207
|
+
const i = this._labels;
|
|
208
|
+
return o`
|
|
209
|
+
<div class="group ${e ? "top" : ""} ${t.negate ? "neg" : ""}">
|
|
210
|
+
<div class="gbar"><span class="ll">${i.matchPrefix}</span>
|
|
211
|
+
${this._segGroup(t.id, t.op)}
|
|
212
|
+
<span class="ll">${i.matchSuffix}</span>
|
|
213
|
+
${e ? n : o`<studio-button size="tiny" icon-only icon="circle-xmark"
|
|
214
|
+
style-type="clear" aria-label="Remove group"
|
|
215
|
+
@click=${() => this._remove(t.id)}></studio-button>`}
|
|
216
|
+
</div>
|
|
217
|
+
${t.children.length ? o`<div class="kids">${this._kidRows(t)}</div>` : o`<p class="empty">${i.empty}</p>`}
|
|
218
|
+
<div class="acts" role="group" aria-label="Add to this group">
|
|
219
|
+
<studio-tooltip position="top" size="small"
|
|
220
|
+
text=${i.addRuleTip}>
|
|
221
|
+
<studio-button size="small" style-type="outline" icon="filter"
|
|
222
|
+
@click=${() => this._addRule(t.id)}>${i.addRule}</studio-button>
|
|
223
|
+
</studio-tooltip>
|
|
224
|
+
${this._model.supportsConditionals ? o`
|
|
225
|
+
<studio-tooltip position="top" size="small"
|
|
226
|
+
text=${i.addConditionalTip}>
|
|
227
|
+
<studio-button size="small" style-type="outline"
|
|
228
|
+
icon="code-compare"
|
|
229
|
+
@click=${() => this._addCond(t.id)}>
|
|
230
|
+
${i.addConditional}</studio-button>
|
|
231
|
+
</studio-tooltip>` : n}
|
|
232
|
+
</div>
|
|
233
|
+
${e && this._model.supportsConditionals ? o`<p class="acts-help">
|
|
234
|
+
<strong>${i.addRule}</strong> ${i.actsHelpRule} ·
|
|
235
|
+
<strong>${i.addConditional}</strong>
|
|
236
|
+
${i.actsHelpConditional}</p>` : n}
|
|
237
|
+
</div>`;
|
|
238
|
+
}
|
|
239
|
+
/** Children of a group, numbering its rules "Rule 1", "Rule 2", … */
|
|
240
|
+
_kidRows(t) {
|
|
241
|
+
let e = 0;
|
|
242
|
+
const i = new Map(t.children.map((s) => [s.id, s.kind === "rule" ? ++e : 0]));
|
|
243
|
+
return c(
|
|
244
|
+
t.children,
|
|
245
|
+
(s) => s.id,
|
|
246
|
+
(s) => this._row(s, i.get(s.id))
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
_row(t, e = 0) {
|
|
250
|
+
const i = this.dragId === t.id, s = this.dropId === t.id && this.dragId && this.dragId !== t.id, a = t.kind === "group" ? this._group(t) : t.kind === "conditional" ? this._cond(t) : this._rule(t, e);
|
|
251
|
+
return o`<div class="rw ${i ? "drag" : ""} ${s ? "ov" : ""}"
|
|
252
|
+
@dragover=${(l) => this._dragOver(l, t.id)} @drop=${(l) => this._drop(l, t.id)}>
|
|
253
|
+
<span class="h" draggable="true"
|
|
254
|
+
@dragstart=${(l) => this._dragStart(l, t.id)}
|
|
255
|
+
@dragend=${() => this._dragEnd()}>
|
|
256
|
+
<studio-icon name="grip" size=".9rem"></studio-icon></span>
|
|
257
|
+
<div class="rb">${a}</div>
|
|
258
|
+
</div>`;
|
|
259
|
+
}
|
|
260
|
+
_cond(t) {
|
|
261
|
+
const e = this._labels;
|
|
262
|
+
return o`<div class="cond">
|
|
263
|
+
<div class="cb"><span class="ct">${e.conditionalTitle}</span>
|
|
264
|
+
<studio-button size="tiny" icon-only icon="circle-xmark"
|
|
265
|
+
style-type="clear" aria-label="Remove conditional"
|
|
266
|
+
@click=${() => this._remove(t.id)}></studio-button></div>
|
|
267
|
+
<div class="cs"><span class="sl">${e.conditionalIf}</span>
|
|
268
|
+
${this._sg(t.cond)}</div>
|
|
269
|
+
<div class="cs"><span class="sl">${e.conditionalThen}</span>
|
|
270
|
+
${this._sg(t.then)}</div>
|
|
271
|
+
${t.els ? o`<div class="cs"><span class="sl el">${e.conditionalElse}
|
|
272
|
+
<studio-button size="tiny" style-type="clear"
|
|
273
|
+
text-color="var(--wral-red, #D1232A)"
|
|
274
|
+
@click=${() => this._dropEls(t.id)}>
|
|
275
|
+
${e.removeElse}</studio-button>
|
|
276
|
+
</span>${this._sg(t.els)}</div>` : o`<studio-button size="tiny" style-type="clear" icon="plus"
|
|
277
|
+
@click=${() => this._addEls(t.id)}>${e.addElse}</studio-button>`}
|
|
278
|
+
</div>`;
|
|
279
|
+
}
|
|
280
|
+
_sg(t) {
|
|
281
|
+
const e = this._labels;
|
|
282
|
+
let i = 0;
|
|
283
|
+
const s = new Map(t.children.map((a) => [a.id, a.kind === "rule" ? ++i : 0]));
|
|
284
|
+
return o`<div class="sgp">
|
|
285
|
+
<div class="sgh">${this._segGroup(t.id, t.op)}</div>
|
|
286
|
+
${c(
|
|
287
|
+
t.children,
|
|
288
|
+
(a) => a.id,
|
|
289
|
+
(a) => a.kind === "rule" ? this._rule(a, s.get(a.id)) : this._row(a, s.get(a.id))
|
|
290
|
+
)}
|
|
291
|
+
<studio-tooltip position="top" size="small"
|
|
292
|
+
text=${e.addSubRuleTip}>
|
|
293
|
+
<studio-button class="add-rule" size="tiny" style-type="clear"
|
|
294
|
+
icon="plus"
|
|
295
|
+
@click=${() => this._addRule(t.id)}>${e.addSubRule}</studio-button>
|
|
296
|
+
</studio-tooltip>
|
|
297
|
+
</div>`;
|
|
298
|
+
}
|
|
299
|
+
_rule(t, e = 0) {
|
|
300
|
+
const i = this._labels, s = this._model.fields[t.field], a = s.ops, l = t.op !== "exists", u = t.op === "between", d = s.type === "date" ? "date" : s.type === "number" || s.type === "video" ? "number" : "text", p = l && (!(t.value || "").trim() || u && !(t.value2 || "").trim());
|
|
301
|
+
return o`<div class="rule ${t.negate ? "exc" : ""}">
|
|
302
|
+
${e ? o`<span class="rule-tag">${i.ruleTag} ${e}</span>` : n}
|
|
303
|
+
<studio-button-group size="small" style-type="outline"
|
|
304
|
+
role="group" aria-label="Include or exclude">
|
|
305
|
+
<studio-button class="tog incl" size="small" style-type="outline"
|
|
306
|
+
aria-pressed=${String(!t.negate)}
|
|
307
|
+
@click=${(r) => {
|
|
308
|
+
this._blurButton(r), this._patch(t.id, { negate: !1 });
|
|
309
|
+
}}>${i.include}</studio-button>
|
|
310
|
+
<studio-button class="tog excl" size="small" style-type="outline"
|
|
311
|
+
aria-pressed=${String(!!t.negate)}
|
|
312
|
+
@click=${(r) => {
|
|
313
|
+
this._blurButton(r), this._patch(t.id, { negate: !0 });
|
|
314
|
+
}}>${i.exclude}</studio-button>
|
|
315
|
+
</studio-button-group>
|
|
316
|
+
<studio-select class="f-sel" .singleSelect=${!0}
|
|
317
|
+
.preventNull=${!0} height="small" width="100%"
|
|
318
|
+
.options=${Object.entries(this._model.fields).map(([r, f]) => ({ value: r, label: f.label }))}
|
|
319
|
+
.selected=${[t.field]}
|
|
320
|
+
@change=${(r) => {
|
|
321
|
+
r.stopPropagation(), this._patch(t.id, { field: r.detail.selected[0] });
|
|
322
|
+
}}
|
|
323
|
+
></studio-select>
|
|
324
|
+
<studio-select class="op-sel" .singleSelect=${!0}
|
|
325
|
+
.preventNull=${!0} height="small" width="100%"
|
|
326
|
+
.options=${a.map((r) => ({ value: r.id, label: r.label }))}
|
|
327
|
+
.selected=${[t.op]}
|
|
328
|
+
@change=${(r) => {
|
|
329
|
+
r.stopPropagation(), this._patch(t.id, { op: r.detail.selected[0] });
|
|
330
|
+
}}
|
|
331
|
+
></studio-select>
|
|
332
|
+
<studio-button size="tiny" icon-only icon="circle-xmark"
|
|
333
|
+
style-type="clear" aria-label="Remove rule"
|
|
334
|
+
@click=${() => this._remove(t.id)}></studio-button>
|
|
335
|
+
${l ? o`<div class="val-row">
|
|
336
|
+
${d === "date" ? o`<input type="date" .value=${t.value}
|
|
337
|
+
@input=${(r) => this._patch(t.id, { value: r.target.value })}>` : o`<studio-input class="val" height="small" width="100%" type=${d}
|
|
338
|
+
placeholder=${s.hint || "value"} .value=${t.value}
|
|
339
|
+
@input=${(r) => this._patch(t.id, { value: r.target.value })}
|
|
340
|
+
></studio-input>`}
|
|
341
|
+
${u ? o`<span class="as">${i.between}</span>
|
|
342
|
+
${d === "date" ? o`<input type="date" .value=${t.value2}
|
|
343
|
+
@input=${(r) => this._patch(t.id, { value2: r.target.value })}>` : o`<studio-input class="val" height="small" width="100%" type=${d}
|
|
344
|
+
placeholder=${s.hint || "value"} .value=${t.value2}
|
|
345
|
+
@input=${(r) => this._patch(t.id, { value2: r.target.value })}
|
|
346
|
+
></studio-input>`}` : n}
|
|
347
|
+
</div>` : n}
|
|
348
|
+
${p ? o`<p class="pending">
|
|
349
|
+
<studio-icon name="circle-info" size=".7rem"></studio-icon>
|
|
350
|
+
${u ? i.pendingBoth : i.pendingOne}</p>` : n}
|
|
351
|
+
</div>`;
|
|
352
|
+
}
|
|
353
|
+
static styles = v`
|
|
354
|
+
:host{display:block;font-family:var(--wral-font-body,inherit);font-size:.85rem;}
|
|
355
|
+
*{box-sizing:border-box;}
|
|
356
|
+
/* Selected state for the segmented toggles (all/any, include/exclude).
|
|
357
|
+
Keyed on aria-pressed and exposed as inheritable CSS vars so the
|
|
358
|
+
studio-button picks up the fill without its colour attributes,
|
|
359
|
+
which lag a render behind on toggle. Unselected buttons use the
|
|
360
|
+
outline style-type's transparent default. */
|
|
361
|
+
.tog[aria-pressed="true"]{--button-text-color:#FFFFFF;}
|
|
362
|
+
.tog.primary[aria-pressed="true"]{
|
|
363
|
+
--button-bg-color:var(--studio-primary,#001D68);}
|
|
364
|
+
.tog.incl[aria-pressed="true"]{--button-bg-color:var(--wral-green,#72B509);}
|
|
365
|
+
.tog.excl[aria-pressed="true"]{--button-bg-color:var(--wral-red,#D1232A);}
|
|
366
|
+
/* native styling kept for the controls with no studio-ui
|
|
367
|
+
counterpart: date inputs and the raw-query textarea */
|
|
368
|
+
input,textarea{font:inherit;font-size:.8rem;
|
|
369
|
+
border:1px solid var(--studio-border,#E1E2E4);border-radius:4px;
|
|
370
|
+
padding:5px 7px;background:var(--studio-surface,#fff);
|
|
371
|
+
color:var(--studio-fg,#1B1C1E);}
|
|
372
|
+
.qbar{display:flex;justify-content:flex-end;align-items:center;gap:8px;
|
|
373
|
+
margin-bottom:10px;}
|
|
374
|
+
.note{flex:1;font-size:.7rem;color:var(--studio-muted,#6B6F78);
|
|
375
|
+
font-style:italic;}
|
|
376
|
+
.intro{flex:1;font-size:.78rem;color:var(--studio-muted,#6B6F78);}
|
|
377
|
+
.empty{margin:0;padding:14px 12px;border:1px dashed
|
|
378
|
+
var(--studio-line,#C3C5CA);border-radius:6px;font-size:.8rem;
|
|
379
|
+
color:var(--studio-muted,#6B6F78);text-align:center;}
|
|
380
|
+
.group{border-radius:5px;}
|
|
381
|
+
.group:not(.top){border-left:3px solid var(--studio-secondary,#2594E3);
|
|
382
|
+
padding:5px;margin:5px 0;
|
|
383
|
+
background:color-mix(in srgb,var(--studio-secondary,#2594E3) 5%,transparent);}
|
|
384
|
+
.group.neg:not(.top){border-left-color:var(--wral-red,#D1232A);}
|
|
385
|
+
.gbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
|
|
386
|
+
margin-bottom:10px;}
|
|
387
|
+
.gbar studio-button[icon-only]{margin-left:auto;}
|
|
388
|
+
.ll{font-size:.68rem;font-weight:700;letter-spacing:.05em;
|
|
389
|
+
text-transform:uppercase;color:var(--studio-subtle,#878B94);}
|
|
390
|
+
.kids{display:flex;flex-direction:column;gap:8px;}
|
|
391
|
+
.rw{display:flex;align-items:flex-start;gap:3px;}
|
|
392
|
+
.rw.ov{box-shadow:0 -2px 0 var(--studio-secondary,#2594E3);}
|
|
393
|
+
.rw.drag{opacity:.4;}
|
|
394
|
+
.h{cursor:grab;color:var(--studio-subtle,#A5A8AF);padding:6px 1px;
|
|
395
|
+
user-select:none;}
|
|
396
|
+
.rb{flex:1;min-width:0;}
|
|
397
|
+
/* full-width rule rows: label | segment | field | op | remove,
|
|
398
|
+
with the value(s) on their own full-width line below */
|
|
399
|
+
.rule{display:grid;
|
|
400
|
+
grid-template-columns:auto auto minmax(0,1.2fr) minmax(0,1fr) auto;
|
|
401
|
+
gap:8px;align-items:center;
|
|
402
|
+
background:var(--studio-surface,#fff);
|
|
403
|
+
border:1px solid var(--studio-border,#E1E2E4);border-radius:6px;
|
|
404
|
+
padding:8px 10px;}
|
|
405
|
+
.rule-tag{font-family:var(--wral-font-ui,inherit);font-size:.64rem;
|
|
406
|
+
font-weight:700;letter-spacing:.05em;text-transform:uppercase;
|
|
407
|
+
color:var(--studio-muted,#6B6F78);white-space:nowrap;
|
|
408
|
+
background:var(--studio-bg,#F4F5F6);border-radius:4px;
|
|
409
|
+
padding:3px 7px;}
|
|
410
|
+
.pending{grid-column:1/-1;display:flex;align-items:center;gap:5px;
|
|
411
|
+
margin:0;font-size:.72rem;font-style:italic;
|
|
412
|
+
color:var(--studio-subtle,#878B94);}
|
|
413
|
+
.rule.exc{background:color-mix(in srgb,var(--wral-red,#D1232A) 6%,
|
|
414
|
+
var(--studio-surface,#fff));}
|
|
415
|
+
.rule .f-sel,.rule .op-sel{width:100%;min-width:0;}
|
|
416
|
+
.val-row{grid-column:1/-1;display:flex;align-items:center;gap:6px;}
|
|
417
|
+
.val-row .val{flex:1;min-width:0;}
|
|
418
|
+
.val-row input[type="date"]{flex:1;min-width:0;}
|
|
419
|
+
.as{font-size:.74rem;color:var(--studio-muted,#6B6F78);flex:0 0 auto;}
|
|
420
|
+
.acts{display:flex;gap:8px;padding-top:10px;flex-wrap:wrap;}
|
|
421
|
+
.acts-help{font-size:.74rem;color:var(--studio-muted,#6B6F78);
|
|
422
|
+
line-height:1.5;margin:8px 0 0;padding-top:8px;
|
|
423
|
+
border-top:1px solid var(--studio-border,#E1E2E4);}
|
|
424
|
+
.acts-help strong{color:var(--studio-fg,#1B1C1E);font-weight:600;}
|
|
425
|
+
.add-rule{align-self:flex-start;}
|
|
426
|
+
.cond{border:1px solid var(--wral-purple,#33109C);
|
|
427
|
+
border-left:3px solid var(--wral-purple,#33109C);border-radius:5px;
|
|
428
|
+
background:color-mix(in srgb,var(--wral-purple,#33109C) 5%,transparent);
|
|
429
|
+
padding:5px;}
|
|
430
|
+
.cb{display:flex;justify-content:space-between;align-items:center;}
|
|
431
|
+
.ct{font-family:var(--wral-font-mono,monospace);font-size:.66rem;
|
|
432
|
+
font-weight:700;color:var(--wral-purple,#33109C);}
|
|
433
|
+
.cs{margin:5px 0;}
|
|
434
|
+
.sl{display:block;font-size:.72rem;font-weight:600;
|
|
435
|
+
color:var(--studio-muted,#6B6F78);margin-bottom:3px;}
|
|
436
|
+
.sl.el{color:var(--wral-purple,#33109C);}
|
|
437
|
+
.sgp{background:var(--studio-surface,#fff);
|
|
438
|
+
border:1px solid var(--studio-border,#E1E2E4);border-radius:4px;padding:5px;
|
|
439
|
+
display:flex;flex-direction:column;gap:4px;}
|
|
440
|
+
.sgh{display:flex;justify-content:flex-end;}
|
|
441
|
+
.raw{width:100%;min-height:90px;
|
|
442
|
+
font-family:var(--wral-font-mono,monospace);font-size:.78rem;}
|
|
443
|
+
.err{color:var(--wral-red,#D1232A);font-size:.74rem;margin:4px 0 0;
|
|
444
|
+
font-weight:600;}
|
|
445
|
+
.ok{color:var(--wral-green-deep,#66A208);font-size:.74rem;margin:4px 0 0;}
|
|
446
|
+
.note-raw{color:var(--studio-muted,#6B6F78);font-size:.74rem;margin:4px 0 0;
|
|
447
|
+
font-style:italic;line-height:1.4;}
|
|
448
|
+
`;
|
|
449
|
+
}
|
|
450
|
+
globalThis?.customElements && !globalThis.customElements.get("studio-search-filter") && globalThis.customElements.define("studio-search-filter", x);
|
|
451
|
+
export {
|
|
452
|
+
x as S
|
|
453
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wral/studio.ui.search-filter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lit web component for building Lucene search queries visually (WRAL Studio Search Filter).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "Dominick Jones <djones@wral.com>",
|
|
7
|
+
"license": "UNLICENSED",
|
|
8
|
+
"peerDependencies": {
|
|
9
|
+
"lit": "^3.0.0"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "vite",
|
|
13
|
+
"lint": "eslint",
|
|
14
|
+
"test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"build": "vite build && vite build --config vite.standalone.config.mjs"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./dist/index.mjs",
|
|
20
|
+
"./define": "./dist/define.mjs",
|
|
21
|
+
"./model": "./dist/model.mjs",
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/index.mjs",
|
|
25
|
+
"module": "./dist/index.mjs",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"eslint": "^9.36.0",
|
|
34
|
+
"jest": "^30.1.1",
|
|
35
|
+
"jest-environment-jsdom": "^30.4.1",
|
|
36
|
+
"jsdom": "^26.0.0",
|
|
37
|
+
"lit": "^3.3.1",
|
|
38
|
+
"vite": "^7.1.7"
|
|
39
|
+
}
|
|
40
|
+
}
|