@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.
@@ -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
+ }