@dorsk/tsumikit 0.10.0 → 0.11.1
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/dist/components/molecules/Tooltip.svelte +79 -14
- package/dist/components/molecules/Tooltip.svelte.d.ts +7 -1
- package/dist/components/organisms/FilterSearchBar.svelte +69 -2
- package/dist/components/organisms/FilterSearchBar.svelte.d.ts +6 -0
- package/dist/query/edit.d.ts +27 -0
- package/dist/query/edit.js +79 -0
- package/dist/query/index.d.ts +1 -0
- package/dist/query/index.js +1 -0
- package/package.json +1 -1
|
@@ -8,33 +8,50 @@
|
|
|
8
8
|
// only opens/closes under our control) and is positioned by the shared
|
|
9
9
|
// `place()` helper — so, like Popover, it escapes ancestor overflow/transform
|
|
10
10
|
// clipping and flips/clamps to stay in the viewport. Dependency-free.
|
|
11
|
+
//
|
|
12
|
+
// Rich mode: pass a `content` snippet instead of (or as well as — `content`
|
|
13
|
+
// wins) `text` and the panel renders arbitrary markup: key/value rows, code
|
|
14
|
+
// ids, CopyButton, small buttons. Closing is then hovercard-style: debounced
|
|
15
|
+
// by `closeDelay` and cancelled when the pointer (or focus) enters the panel,
|
|
16
|
+
// so users can move in to select/copy text or click a button. Note the
|
|
17
|
+
// role="tooltip" caveat: `aria-describedby` flattens the panel to its text.
|
|
11
18
|
import type { Snippet } from 'svelte';
|
|
12
19
|
import { place } from '../../floating';
|
|
13
20
|
|
|
14
21
|
let {
|
|
15
22
|
text,
|
|
23
|
+
content,
|
|
16
24
|
placement = 'top',
|
|
17
25
|
delay = 200,
|
|
26
|
+
closeDelay = 250,
|
|
18
27
|
trigger
|
|
19
28
|
}: {
|
|
20
|
-
text
|
|
29
|
+
/** Plain-text bubble. Ignored when `content` is provided. */
|
|
30
|
+
text?: string;
|
|
31
|
+
/** Rich panel content — makes the panel hover-persistent and selectable. */
|
|
32
|
+
content?: Snippet;
|
|
21
33
|
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
34
|
+
/** Open delay (ms) on hover. */
|
|
22
35
|
delay?: number;
|
|
36
|
+
/** Grace period (ms) before closing — lets the pointer travel into the panel. */
|
|
37
|
+
closeDelay?: number;
|
|
23
38
|
trigger: Snippet;
|
|
24
39
|
} = $props();
|
|
25
40
|
|
|
26
41
|
const id = `tip-${Math.random().toString(36).slice(2, 8)}`;
|
|
27
42
|
let wrapEl = $state<HTMLElement | null>(null);
|
|
28
43
|
let tipEl = $state<HTMLElement | null>(null);
|
|
29
|
-
let
|
|
44
|
+
let showTimer: ReturnType<typeof setTimeout> | undefined;
|
|
45
|
+
let hideTimer: ReturnType<typeof setTimeout> | undefined;
|
|
30
46
|
|
|
31
47
|
function reposition() {
|
|
32
48
|
if (wrapEl && tipEl) place(wrapEl, tipEl, `${placement}-center`, 6);
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
function show() {
|
|
36
|
-
clearTimeout(
|
|
37
|
-
|
|
52
|
+
clearTimeout(hideTimer); // pointer came back within the grace period
|
|
53
|
+
clearTimeout(showTimer);
|
|
54
|
+
showTimer = setTimeout(() => {
|
|
38
55
|
if (!tipEl || tipEl.matches(':popover-open')) return; // re-entry guard
|
|
39
56
|
tipEl.showPopover(); // top layer — displayed before we measure it
|
|
40
57
|
reposition();
|
|
@@ -42,12 +59,23 @@
|
|
|
42
59
|
addEventListener('resize', reposition);
|
|
43
60
|
}, delay);
|
|
44
61
|
}
|
|
45
|
-
function
|
|
46
|
-
clearTimeout(
|
|
62
|
+
function hideNow() {
|
|
63
|
+
clearTimeout(showTimer);
|
|
64
|
+
clearTimeout(hideTimer);
|
|
47
65
|
if (tipEl?.matches(':popover-open')) tipEl.hidePopover();
|
|
48
66
|
removeEventListener('scroll', reposition, true);
|
|
49
67
|
removeEventListener('resize', reposition);
|
|
50
68
|
}
|
|
69
|
+
// Debounced close: cancelled if the pointer/focus enters the panel (rich
|
|
70
|
+
// mode) or returns to the trigger before `closeDelay` elapses.
|
|
71
|
+
function scheduleHide() {
|
|
72
|
+
clearTimeout(showTimer);
|
|
73
|
+
clearTimeout(hideTimer);
|
|
74
|
+
hideTimer = setTimeout(hideNow, closeDelay);
|
|
75
|
+
}
|
|
76
|
+
function cancelHide() {
|
|
77
|
+
clearTimeout(hideTimer);
|
|
78
|
+
}
|
|
51
79
|
|
|
52
80
|
const FOCUSABLE = 'a[href],button,input,select,textarea,[tabindex]';
|
|
53
81
|
// Action: wire aria-describedby onto the trigger's focusable element AND the
|
|
@@ -58,19 +86,39 @@
|
|
|
58
86
|
| HTMLElement
|
|
59
87
|
| null;
|
|
60
88
|
target?.setAttribute('aria-describedby', id);
|
|
61
|
-
const onkey = (e: KeyboardEvent) => e.key === 'Escape' &&
|
|
89
|
+
const onkey = (e: KeyboardEvent) => e.key === 'Escape' && hideNow();
|
|
62
90
|
node.addEventListener('mouseenter', show);
|
|
63
|
-
node.addEventListener('mouseleave',
|
|
91
|
+
node.addEventListener('mouseleave', scheduleHide);
|
|
64
92
|
node.addEventListener('focusin', show);
|
|
65
|
-
node.addEventListener('focusout',
|
|
93
|
+
node.addEventListener('focusout', scheduleHide);
|
|
66
94
|
node.addEventListener('keydown', onkey);
|
|
67
95
|
return {
|
|
68
96
|
destroy() {
|
|
69
97
|
target?.removeAttribute('aria-describedby');
|
|
70
98
|
node.removeEventListener('mouseenter', show);
|
|
71
|
-
node.removeEventListener('mouseleave',
|
|
99
|
+
node.removeEventListener('mouseleave', scheduleHide);
|
|
72
100
|
node.removeEventListener('focusin', show);
|
|
73
|
-
node.removeEventListener('focusout',
|
|
101
|
+
node.removeEventListener('focusout', scheduleHide);
|
|
102
|
+
node.removeEventListener('keydown', onkey);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Action for the panel: in rich mode it accepts the pointer/focus, which
|
|
108
|
+
// cancels the pending close (hovercard persistence).
|
|
109
|
+
function panel(node: HTMLElement) {
|
|
110
|
+
const onkey = (e: KeyboardEvent) => e.key === 'Escape' && hideNow();
|
|
111
|
+
node.addEventListener('mouseenter', cancelHide);
|
|
112
|
+
node.addEventListener('mouseleave', scheduleHide);
|
|
113
|
+
node.addEventListener('focusin', cancelHide);
|
|
114
|
+
node.addEventListener('focusout', scheduleHide);
|
|
115
|
+
node.addEventListener('keydown', onkey);
|
|
116
|
+
return {
|
|
117
|
+
destroy() {
|
|
118
|
+
node.removeEventListener('mouseenter', cancelHide);
|
|
119
|
+
node.removeEventListener('mouseleave', scheduleHide);
|
|
120
|
+
node.removeEventListener('focusin', cancelHide);
|
|
121
|
+
node.removeEventListener('focusout', scheduleHide);
|
|
74
122
|
node.removeEventListener('keydown', onkey);
|
|
75
123
|
}
|
|
76
124
|
};
|
|
@@ -81,9 +129,17 @@
|
|
|
81
129
|
{@render trigger()}
|
|
82
130
|
</span>
|
|
83
131
|
|
|
84
|
-
<
|
|
85
|
-
{
|
|
86
|
-
|
|
132
|
+
<div
|
|
133
|
+
bind:this={tipEl}
|
|
134
|
+
{id}
|
|
135
|
+
role="tooltip"
|
|
136
|
+
popover="manual"
|
|
137
|
+
class="tip"
|
|
138
|
+
class:rich={!!content}
|
|
139
|
+
use:panel
|
|
140
|
+
>
|
|
141
|
+
{#if content}{@render content()}{:else}{text}{/if}
|
|
142
|
+
</div>
|
|
87
143
|
|
|
88
144
|
<style>
|
|
89
145
|
.tip-wrap {
|
|
@@ -106,6 +162,15 @@
|
|
|
106
162
|
white-space: normal;
|
|
107
163
|
pointer-events: none; /* non-interactive: never steals hover/clicks */
|
|
108
164
|
}
|
|
165
|
+
/* Rich panel: interactive + selectable so the user can move the pointer in
|
|
166
|
+
to copy an id or click a button (the close grace period allows the trip). */
|
|
167
|
+
.tip.rich {
|
|
168
|
+
pointer-events: auto;
|
|
169
|
+
user-select: text;
|
|
170
|
+
max-width: min(22rem, calc(100vw - 2 * var(--sp-3)));
|
|
171
|
+
padding: var(--sp-2);
|
|
172
|
+
border-radius: var(--r-md);
|
|
173
|
+
}
|
|
109
174
|
/* Fade/scale in when shown (skipped under reduced-motion via the global rule). */
|
|
110
175
|
.tip:popover-open {
|
|
111
176
|
animation: tip-in 0.12s var(--ease);
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
type $$ComponentProps = {
|
|
3
|
-
text
|
|
3
|
+
/** Plain-text bubble. Ignored when `content` is provided. */
|
|
4
|
+
text?: string;
|
|
5
|
+
/** Rich panel content — makes the panel hover-persistent and selectable. */
|
|
6
|
+
content?: Snippet;
|
|
4
7
|
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
8
|
+
/** Open delay (ms) on hover. */
|
|
5
9
|
delay?: number;
|
|
10
|
+
/** Grace period (ms) before closing — lets the pointer travel into the panel. */
|
|
11
|
+
closeDelay?: number;
|
|
6
12
|
trigger: Snippet;
|
|
7
13
|
};
|
|
8
14
|
declare const Tooltip: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
import Badge from '../atoms/Badge.svelte';
|
|
21
21
|
import Icon from '../atoms/Icon.svelte';
|
|
22
22
|
import { filters, freeText, type Query } from '../../query/ast';
|
|
23
|
+
import {
|
|
24
|
+
autoQuoteEdit,
|
|
25
|
+
backspaceEmptyQuotes,
|
|
26
|
+
closingQuoteExit
|
|
27
|
+
} from '../../query/edit';
|
|
23
28
|
import { findField, operatorById, type Schema } from '../../query/schema';
|
|
24
29
|
import { parse } from '../../query/parser';
|
|
25
30
|
import { suggest, type SuggestState } from '../../query/suggest';
|
|
@@ -29,6 +34,7 @@
|
|
|
29
34
|
value = $bindable(''),
|
|
30
35
|
placeholder = 'artist:"Daft Punk" AND year>=2000',
|
|
31
36
|
showChips = true,
|
|
37
|
+
autoQuote = true,
|
|
32
38
|
onchange,
|
|
33
39
|
onsubmit
|
|
34
40
|
}: {
|
|
@@ -38,6 +44,12 @@
|
|
|
38
44
|
placeholder?: string;
|
|
39
45
|
/** Render the parsed, removable filter chips under the bar. */
|
|
40
46
|
showChips?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* When a string field's value step opens (`title:`), auto-insert a `""`
|
|
49
|
+
* pair with the caret inside so multi-word values stay together; Tab exits
|
|
50
|
+
* the quotes. Set false for bare typing where spaces split the value.
|
|
51
|
+
*/
|
|
52
|
+
autoQuote?: boolean;
|
|
41
53
|
/** Fires the parsed AST on every change — feed this to your backend. */
|
|
42
54
|
onchange?: (query: Query, raw: string) => void;
|
|
43
55
|
/** Fires on Enter / clear / chip-remove with the raw query string. */
|
|
@@ -64,6 +76,28 @@
|
|
|
64
76
|
return findField(schema, fieldName)?.label ?? fieldName;
|
|
65
77
|
}
|
|
66
78
|
|
|
79
|
+
function setCaret(pos: number) {
|
|
80
|
+
queueMicrotask(() => {
|
|
81
|
+
if (!el) return;
|
|
82
|
+
el.focus();
|
|
83
|
+
el.setSelectionRange(pos, pos);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function oninput(e: Event) {
|
|
88
|
+
if (autoQuote && el && (e as InputEvent).inputType?.startsWith('insert')) {
|
|
89
|
+
const pos = el.selectionStart ?? value.length;
|
|
90
|
+
const edit = autoQuoteEdit(schema, value, pos);
|
|
91
|
+
if (edit) {
|
|
92
|
+
value = edit.value;
|
|
93
|
+
setCaret(edit.pos);
|
|
94
|
+
queueMicrotask(refresh);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
refresh();
|
|
99
|
+
}
|
|
100
|
+
|
|
67
101
|
async function refresh() {
|
|
68
102
|
if (!el) return;
|
|
69
103
|
const pos = el.selectionStart ?? value.length;
|
|
@@ -81,18 +115,51 @@
|
|
|
81
115
|
if (!s) return;
|
|
82
116
|
const [a, b] = menu.span;
|
|
83
117
|
value = value.slice(0, a) + s.insert + value.slice(b);
|
|
118
|
+
let caret = s.caret;
|
|
119
|
+
// A field/operator pick that opens a string field's value step gets the
|
|
120
|
+
// same auto-quotes as manual typing.
|
|
121
|
+
if (autoQuote && s.advance) {
|
|
122
|
+
const edit = autoQuoteEdit(schema, value, caret);
|
|
123
|
+
if (edit) {
|
|
124
|
+
value = edit.value;
|
|
125
|
+
caret = edit.pos;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
84
128
|
// Restore caret after the inserted text, then re-run suggestions if the
|
|
85
129
|
// step advanced (field → operator → value) so the flow keeps going.
|
|
86
130
|
queueMicrotask(() => {
|
|
87
131
|
if (!el) return;
|
|
88
132
|
el.focus();
|
|
89
|
-
el.setSelectionRange(
|
|
133
|
+
el.setSelectionRange(caret, caret);
|
|
90
134
|
if (s.advance) refresh();
|
|
91
135
|
else open = false;
|
|
92
136
|
});
|
|
93
137
|
}
|
|
94
138
|
|
|
95
139
|
function onkeydown(e: KeyboardEvent) {
|
|
140
|
+
if (autoQuote && el) {
|
|
141
|
+
const pos = el.selectionStart ?? value.length;
|
|
142
|
+
if (e.key === 'Tab') {
|
|
143
|
+
// Inside auto-quotes Tab exits them (takes priority over accepting a
|
|
144
|
+
// dropdown item); elsewhere it falls through to accept.
|
|
145
|
+
const exit = closingQuoteExit(value, pos);
|
|
146
|
+
if (exit !== null) {
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
open = false;
|
|
149
|
+
setCaret(exit);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
} else if (e.key === 'Backspace') {
|
|
153
|
+
const edit = backspaceEmptyQuotes(value, pos);
|
|
154
|
+
if (edit) {
|
|
155
|
+
e.preventDefault();
|
|
156
|
+
value = edit.value;
|
|
157
|
+
setCaret(edit.pos);
|
|
158
|
+
queueMicrotask(refresh);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
96
163
|
if (!open || !menu) {
|
|
97
164
|
if (e.key === 'Enter') submit();
|
|
98
165
|
return;
|
|
@@ -150,7 +217,7 @@
|
|
|
150
217
|
spellcheck="false"
|
|
151
218
|
autocomplete="off"
|
|
152
219
|
{placeholder}
|
|
153
|
-
oninput
|
|
220
|
+
{oninput}
|
|
154
221
|
onclick={refresh}
|
|
155
222
|
onkeyup={(e) => {
|
|
156
223
|
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') refresh();
|
|
@@ -7,6 +7,12 @@ type $$ComponentProps = {
|
|
|
7
7
|
placeholder?: string;
|
|
8
8
|
/** Render the parsed, removable filter chips under the bar. */
|
|
9
9
|
showChips?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* When a string field's value step opens (`title:`), auto-insert a `""`
|
|
12
|
+
* pair with the caret inside so multi-word values stay together; Tab exits
|
|
13
|
+
* the quotes. Set false for bare typing where spaces split the value.
|
|
14
|
+
*/
|
|
15
|
+
autoQuote?: boolean;
|
|
10
16
|
/** Fires the parsed AST on every change — feed this to your backend. */
|
|
11
17
|
onchange?: (query: Query, raw: string) => void;
|
|
12
18
|
/** Fires on Enter / clear / chip-remove with the raw query string. */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Schema } from './schema';
|
|
2
|
+
/**
|
|
3
|
+
* When the text just before `pos` completes a string field's operator
|
|
4
|
+
* (`title:` / `title!:`) and no quote already follows, return the value with an
|
|
5
|
+
* empty `""` pair spliced in at `pos` and the caret parked between them. Returns
|
|
6
|
+
* `null` when it does not apply.
|
|
7
|
+
*/
|
|
8
|
+
export declare function autoQuoteEdit(schema: Schema, value: string, pos: number): {
|
|
9
|
+
value: string;
|
|
10
|
+
pos: number;
|
|
11
|
+
} | null;
|
|
12
|
+
/** True when the caret sits inside an open quote. */
|
|
13
|
+
export declare function insideQuoteAtCaret(value: string, pos: number): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* If the caret is inside a quote and its closing quote follows, return the index
|
|
16
|
+
* just past that quote (where Tab should move the caret to); otherwise `null`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function closingQuoteExit(value: string, pos: number): number | null;
|
|
19
|
+
/**
|
|
20
|
+
* If the caret sits between an empty matching quote pair (`"|"`), return the
|
|
21
|
+
* value with both quotes removed so a Backspace clears an unwanted auto-quote
|
|
22
|
+
* instead of leaving an orphan.
|
|
23
|
+
*/
|
|
24
|
+
export declare function backspaceEmptyQuotes(value: string, pos: number): {
|
|
25
|
+
value: string;
|
|
26
|
+
pos: number;
|
|
27
|
+
} | null;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Editor affordances — pure text edits an input host applies as the user types.
|
|
3
|
+
//
|
|
4
|
+
// The parser treats an unquoted value as ending at the first space, so
|
|
5
|
+
// `title:star wars` means `title:star` + free-text `wars`. To type a multi-word
|
|
6
|
+
// value the user otherwise has to add the quotes by hand. These helpers let the
|
|
7
|
+
// FilterSearchBar auto-insert the `""` pair the moment a string field's value
|
|
8
|
+
// step opens, so spaces land inside the quotes — the already-supported phrase
|
|
9
|
+
// form — without any grammar change. UI-free and framework-agnostic.
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
11
|
+
import { findField } from './schema';
|
|
12
|
+
// A field name immediately followed by a string operator (`:` contains / `!:`
|
|
13
|
+
// not-contains), anchored at the caret.
|
|
14
|
+
const STRING_CLAUSE = /([A-Za-z_][\w-]*)(!?:)$/;
|
|
15
|
+
function isQuote(c) {
|
|
16
|
+
return c === '"' || c === "'";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* When the text just before `pos` completes a string field's operator
|
|
20
|
+
* (`title:` / `title!:`) and no quote already follows, return the value with an
|
|
21
|
+
* empty `""` pair spliced in at `pos` and the caret parked between them. Returns
|
|
22
|
+
* `null` when it does not apply.
|
|
23
|
+
*/
|
|
24
|
+
export function autoQuoteEdit(schema, value, pos) {
|
|
25
|
+
if (isQuote(value[pos]))
|
|
26
|
+
return null;
|
|
27
|
+
const m = STRING_CLAUSE.exec(value.slice(0, pos));
|
|
28
|
+
if (!m)
|
|
29
|
+
return null;
|
|
30
|
+
const field = findField(schema, m[1]);
|
|
31
|
+
if (field?.type !== 'string')
|
|
32
|
+
return null;
|
|
33
|
+
return { value: `${value.slice(0, pos)}""${value.slice(pos)}`, pos: pos + 1 };
|
|
34
|
+
}
|
|
35
|
+
// Scan from the start tracking quote state (spaces inside quotes are NOT
|
|
36
|
+
// boundaries), so the caret's quote context is correct even for `"star wars"`.
|
|
37
|
+
function openQuoteAt(value, pos) {
|
|
38
|
+
let quote = null;
|
|
39
|
+
for (let i = 0; i < pos; i++) {
|
|
40
|
+
const c = value[i];
|
|
41
|
+
if (quote) {
|
|
42
|
+
if (c === quote)
|
|
43
|
+
quote = null;
|
|
44
|
+
}
|
|
45
|
+
else if (isQuote(c)) {
|
|
46
|
+
quote = c;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return quote;
|
|
50
|
+
}
|
|
51
|
+
/** True when the caret sits inside an open quote. */
|
|
52
|
+
export function insideQuoteAtCaret(value, pos) {
|
|
53
|
+
return openQuoteAt(value, pos) !== null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* If the caret is inside a quote and its closing quote follows, return the index
|
|
57
|
+
* just past that quote (where Tab should move the caret to); otherwise `null`.
|
|
58
|
+
*/
|
|
59
|
+
export function closingQuoteExit(value, pos) {
|
|
60
|
+
const quote = openQuoteAt(value, pos);
|
|
61
|
+
if (!quote)
|
|
62
|
+
return null;
|
|
63
|
+
for (let i = pos; i < value.length; i++) {
|
|
64
|
+
if (value[i] === quote)
|
|
65
|
+
return i + 1;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* If the caret sits between an empty matching quote pair (`"|"`), return the
|
|
71
|
+
* value with both quotes removed so a Backspace clears an unwanted auto-quote
|
|
72
|
+
* instead of leaving an orphan.
|
|
73
|
+
*/
|
|
74
|
+
export function backspaceEmptyQuotes(value, pos) {
|
|
75
|
+
const open = value[pos - 1];
|
|
76
|
+
if (!isQuote(open) || value[pos] !== open)
|
|
77
|
+
return null;
|
|
78
|
+
return { value: value.slice(0, pos - 1) + value.slice(pos + 1), pos: pos - 1 };
|
|
79
|
+
}
|
package/dist/query/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { AndNode, ExprNode, FilterNode, LeafNode, NotNode, OrNode, Query, QueryNode, TextNode, } from './ast';
|
|
2
2
|
export { filters, freeText, walk } from './ast';
|
|
3
|
+
export { autoQuoteEdit, backspaceEmptyQuotes, closingQuoteExit, insideQuoteAtCaret, } from './edit';
|
|
3
4
|
export { parse } from './parser';
|
|
4
5
|
export { compilePredicate, serialize, serializeFilter, toSql } from './query';
|
|
5
6
|
export type { FieldDef, FieldType, Operator, OperatorId, Schema, ValueOption, ValueProvider, } from './schema';
|
package/dist/query/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// no Svelte, no DOM. A host app supplies a `Schema` (optionally with async
|
|
4
4
|
// `ValueProvider`s) and consumes the `Query` AST against its own backend.
|
|
5
5
|
export { filters, freeText, walk } from './ast';
|
|
6
|
+
export { autoQuoteEdit, backspaceEmptyQuotes, closingQuoteExit, insideQuoteAtCaret, } from './edit';
|
|
6
7
|
export { parse } from './parser';
|
|
7
8
|
export { compilePredicate, serialize, serializeFilter, toSql } from './query';
|
|
8
9
|
export { defaultOperator, findField, OPERATORS, operatorByCode, operatorById, operatorsFor, resolveValues, } from './schema';
|
package/package.json
CHANGED