@spaethtech/svelte-ui 0.1.10 → 0.3.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 +166 -42
- package/dist/components/Alert.svelte +119 -0
- package/dist/components/Alert.svelte.d.ts +29 -0
- package/dist/components/Badge/Badge.svelte +142 -69
- package/dist/components/Badge/Badge.svelte.d.ts +26 -6
- package/dist/components/Badge/index.d.ts +1 -1
- package/dist/components/Badge/index.js +1 -1
- package/dist/components/Banner.svelte +133 -0
- package/dist/components/Banner.svelte.d.ts +31 -0
- package/dist/components/Button.svelte +240 -0
- package/dist/components/Button.svelte.d.ts +33 -0
- package/dist/components/ButtonDropdown.svelte +145 -0
- package/dist/components/ButtonDropdown.svelte.d.ts +31 -0
- package/dist/components/Calendar.svelte +259 -0
- package/dist/components/Calendar.svelte.d.ts +27 -0
- package/dist/components/Card.svelte +78 -0
- package/dist/components/Card.svelte.d.ts +17 -0
- package/dist/components/CardBody.svelte +41 -0
- package/dist/components/CardBody.svelte.d.ts +17 -0
- package/dist/components/CardFooter.svelte +49 -0
- package/dist/components/CardFooter.svelte.d.ts +16 -0
- package/dist/components/CardHeader.svelte +49 -0
- package/dist/components/CardHeader.svelte.d.ts +16 -0
- package/dist/components/Checkbox.svelte +131 -0
- package/dist/components/Checkbox.svelte.d.ts +16 -0
- package/dist/components/DataTable.svelte +518 -0
- package/dist/components/DataTable.svelte.d.ts +74 -0
- package/dist/components/DatePicker.svelte +312 -0
- package/dist/components/DatePicker.svelte.d.ts +32 -0
- package/dist/components/DateTimeInput.svelte +93 -0
- package/dist/components/DateTimeInput.svelte.d.ts +20 -0
- package/dist/components/Dialog.svelte +132 -0
- package/dist/components/Dialog.svelte.d.ts +24 -0
- package/dist/components/{EmailInput/EmailInput.svelte → EmailInput.svelte} +17 -8
- package/dist/components/EmailInput.svelte.d.ts +21 -0
- package/dist/components/Input.svelte +369 -0
- package/dist/components/Input.svelte.d.ts +35 -0
- package/dist/components/{List/List.svelte → List.svelte} +194 -133
- package/dist/components/{List/List.svelte.d.ts → List.svelte.d.ts} +9 -1
- package/dist/components/Menu.svelte +117 -0
- package/dist/components/Menu.svelte.d.ts +20 -0
- package/dist/components/NotesEditor.svelte +127 -0
- package/dist/components/NotesEditor.svelte.d.ts +17 -0
- package/dist/components/{NumberInput/NumberInput.svelte → NumberInput.svelte} +146 -105
- package/dist/components/{NumberInput/NumberInput.svelte.d.ts → NumberInput.svelte.d.ts} +11 -4
- package/dist/components/PasswordInput.svelte +52 -0
- package/dist/components/PasswordInput.svelte.d.ts +20 -0
- package/dist/components/Popup.svelte +140 -0
- package/dist/components/Popup.svelte.d.ts +31 -0
- package/dist/components/Query.svelte +284 -0
- package/dist/components/Query.svelte.d.ts +15 -0
- package/dist/components/{Rating/Rating.svelte → Rating.svelte} +19 -10
- package/dist/components/Rating.svelte.d.ts +13 -0
- package/dist/components/{SearchInput/SearchInput.svelte → SearchInput.svelte} +19 -8
- package/dist/components/{SearchInput/SearchInput.svelte.d.ts → SearchInput.svelte.d.ts} +9 -2
- package/dist/components/{Select/Select.svelte → Select.svelte} +48 -76
- package/dist/components/{Select/Select.svelte.d.ts → Select.svelte.d.ts} +11 -1
- package/dist/components/SideBarMenu/SideBarMenu.svelte +724 -0
- package/dist/components/SideBarMenu/SideBarMenu.svelte.d.ts +109 -0
- package/dist/components/SideBarMenu/index.d.ts +2 -0
- package/dist/components/SideBarMenu/index.js +1 -0
- package/dist/components/TabStrip/TabStrip.svelte +384 -0
- package/dist/components/TabStrip/TabStrip.svelte.d.ts +50 -0
- package/dist/components/TabStrip/index.d.ts +3 -0
- package/dist/components/TabStrip/index.js +2 -0
- package/dist/components/{TextArea/TextArea.svelte → TextArea.svelte} +143 -112
- package/dist/components/TextArea.svelte.d.ts +38 -0
- package/dist/components/ThemeSelector.svelte +81 -0
- package/dist/components/ThemeSelector.svelte.d.ts +10 -0
- package/dist/components/TimePicker.svelte +148 -0
- package/dist/components/TimePicker.svelte.d.ts +28 -0
- package/dist/components/TimeRangeInput.svelte +203 -0
- package/dist/components/TimeRangeInput.svelte.d.ts +29 -0
- package/dist/components/TimeSpinner.svelte +202 -0
- package/dist/components/TimeSpinner.svelte.d.ts +26 -0
- package/dist/components/Toast/Toaster.svelte +51 -0
- package/dist/components/Toast/Toaster.svelte.d.ts +12 -0
- package/dist/components/Toast/toast.svelte.d.ts +29 -0
- package/dist/components/Toast/toast.svelte.js +27 -0
- package/dist/components/Toggle.svelte +93 -0
- package/dist/components/Toggle.svelte.d.ts +15 -0
- package/dist/data/dataset.d.ts +42 -0
- package/dist/data/dataset.js +3 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/data/query/ast.d.ts +62 -0
- package/dist/data/query/ast.js +12 -0
- package/dist/data/query/index.d.ts +3 -0
- package/dist/data/query/index.js +3 -0
- package/dist/data/query/lexer.d.ts +33 -0
- package/dist/data/query/lexer.js +225 -0
- package/dist/data/query/parser.d.ts +11 -0
- package/dist/data/query/parser.js +252 -0
- package/dist/data/table/grid.svelte.d.ts +57 -0
- package/dist/data/table/grid.svelte.js +139 -0
- package/dist/data/table/index.d.ts +2 -0
- package/dist/data/table/index.js +2 -0
- package/dist/data/table/types.d.ts +79 -0
- package/dist/index.d.ts +48 -22
- package/dist/index.js +45 -21
- package/dist/positioning/anchored.d.ts +61 -0
- package/dist/positioning/anchored.js +122 -0
- package/dist/positioning/tooltip.d.ts +41 -0
- package/dist/positioning/tooltip.js +147 -0
- package/dist/theme.css +205 -0
- package/dist/types/breakpoints.d.ts +24 -0
- package/dist/types/breakpoints.js +13 -0
- package/dist/types/responsive.d.ts +32 -0
- package/dist/types/responsive.js +54 -0
- package/dist/types/sizes.d.ts +10 -3
- package/dist/types/time.d.ts +19 -0
- package/dist/types/time.js +10 -0
- package/dist/types/variants.d.ts +8 -1
- package/dist/types/variants.js +16 -1
- package/package.json +93 -63
- package/dist/components/Button/Button.svelte +0 -172
- package/dist/components/Button/Button.svelte.d.ts +0 -32
- package/dist/components/Button/index.d.ts +0 -1
- package/dist/components/Button/index.js +0 -1
- package/dist/components/Dialog/Dialog.svelte +0 -101
- package/dist/components/Dialog/Dialog.svelte.d.ts +0 -18
- package/dist/components/Dialog/index.d.ts +0 -1
- package/dist/components/Dialog/index.js +0 -1
- package/dist/components/ElementManager/ElementManager.svelte +0 -397
- package/dist/components/ElementManager/ElementManager.svelte.d.ts +0 -43
- package/dist/components/ElementManager/index.d.ts +0 -1
- package/dist/components/ElementManager/index.js +0 -1
- package/dist/components/EmailInput/EmailInput.svelte.d.ts +0 -14
- package/dist/components/EmailInput/index.d.ts +0 -1
- package/dist/components/EmailInput/index.js +0 -1
- package/dist/components/Icon/Icon.svelte +0 -74
- package/dist/components/Icon/Icon.svelte.d.ts +0 -13
- package/dist/components/Icon/index.d.ts +0 -1
- package/dist/components/Icon/index.js +0 -1
- package/dist/components/Input/Input.svelte +0 -268
- package/dist/components/Input/Input.svelte.d.ts +0 -18
- package/dist/components/Input/index.d.ts +0 -1
- package/dist/components/Input/index.js +0 -1
- package/dist/components/List/index.d.ts +0 -1
- package/dist/components/List/index.js +0 -1
- package/dist/components/ListItem/ListItem.svelte +0 -175
- package/dist/components/ListItem/ListItem.svelte.d.ts +0 -24
- package/dist/components/ListItem/index.d.ts +0 -2
- package/dist/components/ListItem/index.js +0 -2
- package/dist/components/ListView/ListView.svelte +0 -463
- package/dist/components/ListView/ListView.svelte.d.ts +0 -37
- package/dist/components/ListView/index.d.ts +0 -2
- package/dist/components/ListView/index.js +0 -2
- package/dist/components/NodeGraph/BaseNode.d.ts +0 -57
- package/dist/components/NodeGraph/BaseNode.js +0 -30
- package/dist/components/NodeGraph/Edge.svelte +0 -60
- package/dist/components/NodeGraph/Edge.svelte.d.ts +0 -16
- package/dist/components/NodeGraph/ExpressionEvaluator.d.ts +0 -82
- package/dist/components/NodeGraph/ExpressionEvaluator.js +0 -152
- package/dist/components/NodeGraph/Node.svelte +0 -100
- package/dist/components/NodeGraph/Node.svelte.d.ts +0 -10
- package/dist/components/NodeGraph/NodeGraph.svelte +0 -52
- package/dist/components/NodeGraph/NodeGraph.svelte.d.ts +0 -14
- package/dist/components/NodeGraph/NodeInstance.svelte.d.ts +0 -80
- package/dist/components/NodeGraph/NodeInstance.svelte.js +0 -241
- package/dist/components/NodeGraph/NodePort.svelte +0 -75
- package/dist/components/NodeGraph/NodePort.svelte.d.ts +0 -11
- package/dist/components/NodeGraph/decorators.d.ts +0 -81
- package/dist/components/NodeGraph/decorators.js +0 -148
- package/dist/components/NodeGraph/index.d.ts +0 -9
- package/dist/components/NodeGraph/index.js +0 -9
- package/dist/components/NodeGraph/types.d.ts +0 -94
- package/dist/components/NodeGraph/types.js +0 -33
- package/dist/components/NotesEditor/NotesEditor.svelte +0 -203
- package/dist/components/NotesEditor/NotesEditor.svelte.d.ts +0 -9
- package/dist/components/NotesEditor/index.d.ts +0 -1
- package/dist/components/NotesEditor/index.js +0 -1
- package/dist/components/NumberInput/index.d.ts +0 -1
- package/dist/components/NumberInput/index.js +0 -1
- package/dist/components/PasswordInput/PasswordInput.svelte +0 -41
- package/dist/components/PasswordInput/PasswordInput.svelte.d.ts +0 -13
- package/dist/components/PasswordInput/index.d.ts +0 -1
- package/dist/components/PasswordInput/index.js +0 -1
- package/dist/components/Popup/index.d.ts +0 -1
- package/dist/components/Popup/index.js +0 -1
- package/dist/components/Rating/Rating.svelte.d.ts +0 -7
- package/dist/components/Rating/index.d.ts +0 -1
- package/dist/components/Rating/index.js +0 -1
- package/dist/components/ScrollView/ScrollView.svelte +0 -285
- package/dist/components/ScrollView/ScrollView.svelte.d.ts +0 -19
- package/dist/components/ScrollView/index.d.ts +0 -1
- package/dist/components/ScrollView/index.js +0 -1
- package/dist/components/SearchInput/index.d.ts +0 -1
- package/dist/components/SearchInput/index.js +0 -1
- package/dist/components/Select/index.d.ts +0 -1
- package/dist/components/Select/index.js +0 -1
- package/dist/components/TextArea/TextArea.svelte.d.ts +0 -19
- package/dist/components/TextArea/index.d.ts +0 -1
- package/dist/components/TextArea/index.js +0 -1
- package/dist/components/ThemeSelector/ThemeSelector.svelte +0 -64
- package/dist/components/ThemeSelector/ThemeSelector.svelte.d.ts +0 -3
- package/dist/components/ThemeSelector/index.d.ts +0 -1
- package/dist/components/ThemeSelector/index.js +0 -1
- package/dist/components/TooltipHandler/TooltipHandler.svelte +0 -593
- package/dist/components/TooltipHandler/TooltipHandler.svelte.d.ts +0 -10
- package/dist/components/TooltipHandler/index.d.ts +0 -1
- package/dist/components/TooltipHandler/index.js +0 -1
- package/dist/styles/sizes.d.ts +0 -78
- package/dist/styles/sizes.js +0 -120
- package/dist/styles/variants.d.ts +0 -106
- package/dist/styles/variants.js +0 -194
- package/dist/types/ManagerTypes.d.ts +0 -42
- /package/dist/{types/ManagerTypes.js → data/table/types.js} +0 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { lex } from "./lexer";
|
|
2
|
+
import { QueryError, } from "./ast";
|
|
3
|
+
function coerce(word) {
|
|
4
|
+
if (word === "true")
|
|
5
|
+
return true;
|
|
6
|
+
if (word === "false")
|
|
7
|
+
return false;
|
|
8
|
+
if (/^-?\d+$/.test(word))
|
|
9
|
+
return parseInt(word, 10);
|
|
10
|
+
if (/^-?(?:\d+\.\d*|\.\d+)$/.test(word))
|
|
11
|
+
return parseFloat(word);
|
|
12
|
+
return word; // strings, dates (2026-06-01), enum values, emails, paths, etc.
|
|
13
|
+
}
|
|
14
|
+
const CASTS = new Set(["number", "string", "bool", "date", "inet"]);
|
|
15
|
+
/**
|
|
16
|
+
* Recursive-descent parser. Whitespace is insignificant; logic is EXPLICIT only.
|
|
17
|
+
* or := and ('||' and)*
|
|
18
|
+
* and := unary ('&&' unary)*
|
|
19
|
+
* unary := '!' unary | primary
|
|
20
|
+
* primary := '(' or ')' | clause | search
|
|
21
|
+
* clause := '$' word ( op (scalar | regex) | member (set | interval) )
|
|
22
|
+
* member := '|=' (any) | '&='(all) ; operand = '[' scalar (',' scalar)* ']' | bracket interval
|
|
23
|
+
* Adjacent terms with no operator are an error. Empty input -> null.
|
|
24
|
+
*/
|
|
25
|
+
class Parser {
|
|
26
|
+
tokens;
|
|
27
|
+
pos = 0;
|
|
28
|
+
/** Casts seen across the current clause; folded to one (they must agree). */
|
|
29
|
+
casts = [];
|
|
30
|
+
constructor(tokens) {
|
|
31
|
+
this.tokens = tokens;
|
|
32
|
+
}
|
|
33
|
+
peek() {
|
|
34
|
+
return this.tokens[this.pos];
|
|
35
|
+
}
|
|
36
|
+
next() {
|
|
37
|
+
return this.tokens[this.pos++];
|
|
38
|
+
}
|
|
39
|
+
is(type) {
|
|
40
|
+
return this.peek().type === type;
|
|
41
|
+
}
|
|
42
|
+
expect(type) {
|
|
43
|
+
if (!this.is(type))
|
|
44
|
+
throw new QueryError(`Expected ${type}`, this.peek().start);
|
|
45
|
+
return this.next();
|
|
46
|
+
}
|
|
47
|
+
/** Consume an optional trailing `|cast` token, validate it, and record it for the clause. */
|
|
48
|
+
castAfter() {
|
|
49
|
+
const t = this.peek();
|
|
50
|
+
if (t.type !== "cast")
|
|
51
|
+
return undefined;
|
|
52
|
+
this.next();
|
|
53
|
+
if (!CASTS.has(t.name))
|
|
54
|
+
throw new QueryError(`Unknown cast |${t.name}`, t.start);
|
|
55
|
+
const name = t.name;
|
|
56
|
+
this.casts.push({ name, start: t.start });
|
|
57
|
+
return name;
|
|
58
|
+
}
|
|
59
|
+
/** Fold all casts used in the current clause into one — they must agree. */
|
|
60
|
+
foldCast() {
|
|
61
|
+
for (const c of this.casts) {
|
|
62
|
+
if (c.name !== this.casts[0].name)
|
|
63
|
+
throw new QueryError("A clause may use only one cast", c.start);
|
|
64
|
+
}
|
|
65
|
+
return this.casts[0]?.name;
|
|
66
|
+
}
|
|
67
|
+
parse() {
|
|
68
|
+
if (this.is("eof"))
|
|
69
|
+
return null;
|
|
70
|
+
const node = this.parseOr();
|
|
71
|
+
if (!this.is("eof"))
|
|
72
|
+
throw new QueryError("Unexpected input — use && / || to combine terms", this.peek().start);
|
|
73
|
+
return node;
|
|
74
|
+
}
|
|
75
|
+
parseOr() {
|
|
76
|
+
const parts = [this.parseAnd()];
|
|
77
|
+
while (this.is("or")) {
|
|
78
|
+
this.next();
|
|
79
|
+
parts.push(this.parseAnd());
|
|
80
|
+
}
|
|
81
|
+
return parts.length > 1 ? { kind: "or", children: parts } : parts[0];
|
|
82
|
+
}
|
|
83
|
+
parseAnd() {
|
|
84
|
+
const parts = [this.parseUnary()];
|
|
85
|
+
while (this.is("and")) {
|
|
86
|
+
this.next();
|
|
87
|
+
parts.push(this.parseUnary());
|
|
88
|
+
}
|
|
89
|
+
return parts.length > 1 ? { kind: "and", children: parts } : parts[0];
|
|
90
|
+
}
|
|
91
|
+
parseUnary() {
|
|
92
|
+
if (this.is("not")) {
|
|
93
|
+
this.next();
|
|
94
|
+
return { kind: "not", child: this.parseUnary() };
|
|
95
|
+
}
|
|
96
|
+
return this.parsePrimary();
|
|
97
|
+
}
|
|
98
|
+
parsePrimary() {
|
|
99
|
+
const t = this.peek();
|
|
100
|
+
if (t.type === "lparen") {
|
|
101
|
+
this.next();
|
|
102
|
+
const node = this.parseOr();
|
|
103
|
+
this.expect("rparen");
|
|
104
|
+
return node;
|
|
105
|
+
}
|
|
106
|
+
if (t.type === "dollar")
|
|
107
|
+
return this.parseClause();
|
|
108
|
+
if (t.type === "string" || t.type === "word") {
|
|
109
|
+
this.next();
|
|
110
|
+
return { kind: "search", text: t.value };
|
|
111
|
+
}
|
|
112
|
+
if (t.type === "regex")
|
|
113
|
+
throw new QueryError("A regex must be used with ~= or !~", t.start);
|
|
114
|
+
throw new QueryError("Expected a term", t.start);
|
|
115
|
+
}
|
|
116
|
+
parseClause() {
|
|
117
|
+
this.casts = [];
|
|
118
|
+
this.expect("dollar");
|
|
119
|
+
const colTok = this.peek();
|
|
120
|
+
if (colTok.type !== "word")
|
|
121
|
+
throw new QueryError("Expected a column name after $", colTok.start);
|
|
122
|
+
this.next();
|
|
123
|
+
const col = colTok.value;
|
|
124
|
+
this.castAfter(); // optional cast on the column itself ($ip|inet ...)
|
|
125
|
+
const node = this.parseClauseBody(col);
|
|
126
|
+
const cast = this.foldCast();
|
|
127
|
+
if (cast) {
|
|
128
|
+
if (node.kind === "match")
|
|
129
|
+
throw new QueryError("A cast cannot be used with a regex", colTok.start);
|
|
130
|
+
node.cast = cast;
|
|
131
|
+
}
|
|
132
|
+
return node;
|
|
133
|
+
}
|
|
134
|
+
parseClauseBody(col) {
|
|
135
|
+
const opTok = this.next();
|
|
136
|
+
if (opTok.type === "op") {
|
|
137
|
+
const op = opTok.op;
|
|
138
|
+
if (op === "contains" || op === "ncontains") {
|
|
139
|
+
if (this.is("regex")) {
|
|
140
|
+
const r = this.next();
|
|
141
|
+
if (r.type !== "regex")
|
|
142
|
+
throw new QueryError("Expected a regex", r.start);
|
|
143
|
+
return {
|
|
144
|
+
kind: "match",
|
|
145
|
+
col,
|
|
146
|
+
negated: op === "ncontains",
|
|
147
|
+
source: r.source,
|
|
148
|
+
flags: r.flags,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return { kind: "cmp", col, op, value: this.parseScalar() };
|
|
152
|
+
}
|
|
153
|
+
if (this.is("regex"))
|
|
154
|
+
throw new QueryError("A regex is only valid with ~= or !~", this.peek().start);
|
|
155
|
+
return { kind: "cmp", col, op, value: this.parseScalar() };
|
|
156
|
+
}
|
|
157
|
+
if (opTok.type === "member") {
|
|
158
|
+
return this.parseMemberOperand(col, opTok.mode);
|
|
159
|
+
}
|
|
160
|
+
throw new QueryError("Expected an operator after the column", opTok.start);
|
|
161
|
+
}
|
|
162
|
+
parseScalar() {
|
|
163
|
+
const t = this.next();
|
|
164
|
+
let raw;
|
|
165
|
+
let value;
|
|
166
|
+
if (t.type === "string") {
|
|
167
|
+
raw = t.value;
|
|
168
|
+
value = t.value;
|
|
169
|
+
}
|
|
170
|
+
else if (t.type === "word") {
|
|
171
|
+
raw = t.value;
|
|
172
|
+
value = coerce(t.value);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
throw new QueryError("Expected a value", t.start);
|
|
176
|
+
}
|
|
177
|
+
// A trailing `|cast` retypes the literal (number/string/bool) or just records it (date/inet,
|
|
178
|
+
// which the consumer casts in SQL). The RAW token text is used, so `007|string` -> "007", not 7.
|
|
179
|
+
const cast = this.castAfter();
|
|
180
|
+
if (cast === "number") {
|
|
181
|
+
const num = Number(raw);
|
|
182
|
+
if (Number.isNaN(num))
|
|
183
|
+
throw new QueryError(`"${raw}" is not a number`, t.start);
|
|
184
|
+
value = num;
|
|
185
|
+
}
|
|
186
|
+
else if (cast === "bool") {
|
|
187
|
+
if (raw === "true")
|
|
188
|
+
value = true;
|
|
189
|
+
else if (raw === "false")
|
|
190
|
+
value = false;
|
|
191
|
+
else
|
|
192
|
+
throw new QueryError(`"${raw}" is not a boolean`, t.start);
|
|
193
|
+
}
|
|
194
|
+
else if (cast === "string" || cast === "date" || cast === "inet") {
|
|
195
|
+
value = raw;
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
parseMemberOperand(col, mode) {
|
|
200
|
+
const open = this.next();
|
|
201
|
+
if (open.type !== "lbracket" && open.type !== "lparen") {
|
|
202
|
+
throw new QueryError("Expected '[' or '(' after |= / &=", open.start);
|
|
203
|
+
}
|
|
204
|
+
const lowerInclusive = open.type === "lbracket";
|
|
205
|
+
let lower = null;
|
|
206
|
+
if (!this.is("dotdot")) {
|
|
207
|
+
const first = this.parseScalar();
|
|
208
|
+
if (this.is("dotdot")) {
|
|
209
|
+
lower = { value: first, inclusive: lowerInclusive };
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// SET (must use square brackets).
|
|
213
|
+
if (open.type !== "lbracket")
|
|
214
|
+
throw new QueryError("A set must use '[' ']'", open.start);
|
|
215
|
+
const values = [first];
|
|
216
|
+
while (this.is("comma")) {
|
|
217
|
+
this.next();
|
|
218
|
+
values.push(this.parseScalar());
|
|
219
|
+
}
|
|
220
|
+
this.expect("rbracket");
|
|
221
|
+
return { kind: "in", col, mode, values };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// interval
|
|
225
|
+
this.expect("dotdot");
|
|
226
|
+
let upper = null;
|
|
227
|
+
if (!this.is("rbracket") && !this.is("rparen")) {
|
|
228
|
+
upper = { value: this.parseScalar(), inclusive: false };
|
|
229
|
+
}
|
|
230
|
+
const close = this.next();
|
|
231
|
+
if (close.type !== "rbracket" && close.type !== "rparen") {
|
|
232
|
+
throw new QueryError("Expected ']' or ')' to close the interval", close.start);
|
|
233
|
+
}
|
|
234
|
+
if (upper)
|
|
235
|
+
upper.inclusive = close.type === "rbracket";
|
|
236
|
+
const interval = { lower, upper };
|
|
237
|
+
return { kind: "range", col, mode, interval };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export function parseQuery(input) {
|
|
241
|
+
return new Parser(lex(input)).parse();
|
|
242
|
+
}
|
|
243
|
+
export function tryParseQuery(input) {
|
|
244
|
+
try {
|
|
245
|
+
return { ok: true, ast: parseQuery(input) };
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
if (e instanceof QueryError)
|
|
249
|
+
return { ok: false, error: e.message, position: e.position };
|
|
250
|
+
throw e;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { SvelteSet } from "svelte/reactivity";
|
|
2
|
+
import type { DataSet, QueryColumn } from "../dataset";
|
|
3
|
+
import type { Column, FetchFn, GridState, Sort } from "./types";
|
|
4
|
+
export type GridOptions<T> = {
|
|
5
|
+
columns: Column<T>[];
|
|
6
|
+
fetch: FetchFn<T>;
|
|
7
|
+
getRowId?: (row: T) => string;
|
|
8
|
+
perPage?: number;
|
|
9
|
+
perPageOptions?: number[];
|
|
10
|
+
initialQuery?: string;
|
|
11
|
+
initialSort?: Sort | null;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Shared, reactive controller for a data table. Implements {@link DataSet} (so any `<Query>` can
|
|
15
|
+
* drive it) and adds table view-state: rows, sort, pagination, selection, expansion, fetch.
|
|
16
|
+
* Created once and handed to both `<Query>` and `<DataTable>`.
|
|
17
|
+
*/
|
|
18
|
+
export declare class DataGrid<T> implements DataSet {
|
|
19
|
+
readonly columns: Column<T>[];
|
|
20
|
+
readonly fetch: FetchFn<T>;
|
|
21
|
+
readonly getRowId: (row: T) => string;
|
|
22
|
+
readonly perPageOptions: number[];
|
|
23
|
+
query: string;
|
|
24
|
+
applied: string;
|
|
25
|
+
error: string | null;
|
|
26
|
+
sort: Sort | null;
|
|
27
|
+
page: number;
|
|
28
|
+
perPage: number;
|
|
29
|
+
rows: T[];
|
|
30
|
+
total: number;
|
|
31
|
+
loading: boolean;
|
|
32
|
+
readonly selected: SvelteSet<string>;
|
|
33
|
+
readonly expanded: SvelteSet<string>;
|
|
34
|
+
constructor(opts: GridOptions<T>);
|
|
35
|
+
get queryColumns(): QueryColumn[];
|
|
36
|
+
get dirty(): boolean;
|
|
37
|
+
setDraft(raw: string): void;
|
|
38
|
+
commit(): void;
|
|
39
|
+
setQuery(raw: string): void;
|
|
40
|
+
get request(): GridState;
|
|
41
|
+
get pageCount(): number;
|
|
42
|
+
toggleSort(col: string): void;
|
|
43
|
+
setPage(p: number): void;
|
|
44
|
+
setPerPage(n: number): void;
|
|
45
|
+
get pageIds(): string[];
|
|
46
|
+
isSelected(id: string): boolean;
|
|
47
|
+
toggleRow(id: string): void;
|
|
48
|
+
get allSelected(): boolean;
|
|
49
|
+
get someSelected(): boolean;
|
|
50
|
+
toggleAll(): void;
|
|
51
|
+
clearSelection(): void;
|
|
52
|
+
isExpanded(id: string): boolean;
|
|
53
|
+
toggleExpand(id: string): void;
|
|
54
|
+
get allExpanded(): boolean;
|
|
55
|
+
toggleExpandAll(): void;
|
|
56
|
+
}
|
|
57
|
+
export declare function createGrid<T>(opts: GridOptions<T>): DataGrid<T>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { SvelteSet } from "svelte/reactivity";
|
|
2
|
+
import { tryParseQuery } from "../query";
|
|
3
|
+
/**
|
|
4
|
+
* Shared, reactive controller for a data table. Implements {@link DataSet} (so any `<Query>` can
|
|
5
|
+
* drive it) and adds table view-state: rows, sort, pagination, selection, expansion, fetch.
|
|
6
|
+
* Created once and handed to both `<Query>` and `<DataTable>`.
|
|
7
|
+
*/
|
|
8
|
+
export class DataGrid {
|
|
9
|
+
columns;
|
|
10
|
+
fetch;
|
|
11
|
+
getRowId;
|
|
12
|
+
perPageOptions;
|
|
13
|
+
query = $state("");
|
|
14
|
+
applied = $state("");
|
|
15
|
+
error = $state(null);
|
|
16
|
+
sort = $state(null);
|
|
17
|
+
page = $state(1);
|
|
18
|
+
perPage = $state(25);
|
|
19
|
+
rows = $state([]);
|
|
20
|
+
total = $state(0);
|
|
21
|
+
loading = $state(false);
|
|
22
|
+
selected = new SvelteSet();
|
|
23
|
+
expanded = new SvelteSet();
|
|
24
|
+
constructor(opts) {
|
|
25
|
+
this.columns = opts.columns;
|
|
26
|
+
this.fetch = opts.fetch;
|
|
27
|
+
this.getRowId = opts.getRowId ?? ((r) => String(r.id));
|
|
28
|
+
this.perPage = opts.perPage ?? 25;
|
|
29
|
+
this.perPageOptions = opts.perPageOptions ?? [10, 25, 50, 100];
|
|
30
|
+
this.sort = opts.initialSort ?? null;
|
|
31
|
+
if (opts.initialQuery)
|
|
32
|
+
this.setQuery(opts.initialQuery);
|
|
33
|
+
}
|
|
34
|
+
// ---- DataSet ----
|
|
35
|
+
get queryColumns() {
|
|
36
|
+
return this.columns.map((c) => ({ id: c.id, label: c.label ?? c.header, filter: c.filter }));
|
|
37
|
+
}
|
|
38
|
+
get dirty() {
|
|
39
|
+
return this.query !== this.applied;
|
|
40
|
+
}
|
|
41
|
+
setDraft(raw) {
|
|
42
|
+
this.query = raw;
|
|
43
|
+
const res = tryParseQuery(raw);
|
|
44
|
+
this.error = res.ok ? null : res.error;
|
|
45
|
+
}
|
|
46
|
+
commit() {
|
|
47
|
+
const res = tryParseQuery(this.query);
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
this.error = res.error;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.error = null;
|
|
53
|
+
if (this.query !== this.applied) {
|
|
54
|
+
this.applied = this.query;
|
|
55
|
+
this.page = 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
setQuery(raw) {
|
|
59
|
+
this.setDraft(raw);
|
|
60
|
+
this.commit();
|
|
61
|
+
}
|
|
62
|
+
// ---- table view-state ----
|
|
63
|
+
get request() {
|
|
64
|
+
return { q: this.applied, sort: this.sort, page: this.page, perPage: this.perPage };
|
|
65
|
+
}
|
|
66
|
+
get pageCount() {
|
|
67
|
+
return Math.max(1, Math.ceil(this.total / this.perPage));
|
|
68
|
+
}
|
|
69
|
+
toggleSort(col) {
|
|
70
|
+
if (!this.sort || this.sort.col !== col)
|
|
71
|
+
this.sort = { col, dir: "asc" };
|
|
72
|
+
else if (this.sort.dir === "asc")
|
|
73
|
+
this.sort = { col, dir: "desc" };
|
|
74
|
+
else
|
|
75
|
+
this.sort = null;
|
|
76
|
+
this.page = 1;
|
|
77
|
+
}
|
|
78
|
+
setPage(p) {
|
|
79
|
+
this.page = Math.min(Math.max(1, p), this.pageCount);
|
|
80
|
+
}
|
|
81
|
+
setPerPage(n) {
|
|
82
|
+
this.perPage = n;
|
|
83
|
+
this.page = 1;
|
|
84
|
+
}
|
|
85
|
+
// ---- selection (current page) ----
|
|
86
|
+
get pageIds() {
|
|
87
|
+
return this.rows.map((r) => this.getRowId(r));
|
|
88
|
+
}
|
|
89
|
+
isSelected(id) {
|
|
90
|
+
return this.selected.has(id);
|
|
91
|
+
}
|
|
92
|
+
toggleRow(id) {
|
|
93
|
+
if (this.selected.has(id))
|
|
94
|
+
this.selected.delete(id);
|
|
95
|
+
else
|
|
96
|
+
this.selected.add(id);
|
|
97
|
+
}
|
|
98
|
+
get allSelected() {
|
|
99
|
+
const ids = this.pageIds;
|
|
100
|
+
return ids.length > 0 && ids.every((id) => this.selected.has(id));
|
|
101
|
+
}
|
|
102
|
+
get someSelected() {
|
|
103
|
+
return this.pageIds.some((id) => this.selected.has(id)) && !this.allSelected;
|
|
104
|
+
}
|
|
105
|
+
toggleAll() {
|
|
106
|
+
const ids = this.pageIds;
|
|
107
|
+
if (this.allSelected)
|
|
108
|
+
ids.forEach((id) => this.selected.delete(id));
|
|
109
|
+
else
|
|
110
|
+
ids.forEach((id) => this.selected.add(id));
|
|
111
|
+
}
|
|
112
|
+
clearSelection() {
|
|
113
|
+
this.selected.clear();
|
|
114
|
+
}
|
|
115
|
+
// ---- expansion (current page) ----
|
|
116
|
+
isExpanded(id) {
|
|
117
|
+
return this.expanded.has(id);
|
|
118
|
+
}
|
|
119
|
+
toggleExpand(id) {
|
|
120
|
+
if (this.expanded.has(id))
|
|
121
|
+
this.expanded.delete(id);
|
|
122
|
+
else
|
|
123
|
+
this.expanded.add(id);
|
|
124
|
+
}
|
|
125
|
+
get allExpanded() {
|
|
126
|
+
const ids = this.pageIds;
|
|
127
|
+
return ids.length > 0 && ids.every((id) => this.expanded.has(id));
|
|
128
|
+
}
|
|
129
|
+
toggleExpandAll() {
|
|
130
|
+
const ids = this.pageIds;
|
|
131
|
+
if (this.allExpanded)
|
|
132
|
+
ids.forEach((id) => this.expanded.delete(id));
|
|
133
|
+
else
|
|
134
|
+
ids.forEach((id) => this.expanded.add(id));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function createGrid(opts) {
|
|
138
|
+
return new DataGrid(opts);
|
|
139
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Component, Snippet } from "svelte";
|
|
2
|
+
import type { QueryNode } from "../query";
|
|
3
|
+
import type { FilterDef } from "../dataset";
|
|
4
|
+
export type SortDir = "asc" | "desc";
|
|
5
|
+
export type Sort = {
|
|
6
|
+
col: string;
|
|
7
|
+
dir: SortDir;
|
|
8
|
+
};
|
|
9
|
+
/** Our custom breakpoints + Tailwind defaults. `base` = mobile-first (no prefix).
|
|
10
|
+
*
|
|
11
|
+
* Pixel boundaries (defined in theme.css `@theme`):
|
|
12
|
+
* - `4xs` 320px - `3xs` 360px - `2xs` 420px - `xs` 480px
|
|
13
|
+
* - `sm` 640px - `md` 768px - `lg` 1024px - `xl` 1280px
|
|
14
|
+
* - `2xl` 1536px - `3xl` 1920px
|
|
15
|
+
*/
|
|
16
|
+
export type Breakpoint = "base" | "4xs" | "3xs" | "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl";
|
|
17
|
+
/** Placement of a column's cell on the 12-col grid at a given breakpoint. */
|
|
18
|
+
export type GridPlacement = {
|
|
19
|
+
span?: number;
|
|
20
|
+
start?: number;
|
|
21
|
+
row?: number;
|
|
22
|
+
hidden?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type Column<T> = {
|
|
25
|
+
/** Stable id; also the `$token` used in the query language and the default sort key. */
|
|
26
|
+
id: string;
|
|
27
|
+
header?: string;
|
|
28
|
+
/** Body cell renderer (the 12-grid cell). */
|
|
29
|
+
cell: Snippet<[T]>;
|
|
30
|
+
sortable?: boolean;
|
|
31
|
+
sortKey?: string;
|
|
32
|
+
align?: "start" | "center" | "end";
|
|
33
|
+
/** Responsive placement on the 12-col grid. Defaults to full-width (`{ base: { span: 12 } }`). */
|
|
34
|
+
breakpoints?: Partial<Record<Breakpoint, GridPlacement>>;
|
|
35
|
+
/** Included in free-text search. */
|
|
36
|
+
searchable?: boolean;
|
|
37
|
+
/** Presence makes the column selectable in the `$` popup and drives value autocomplete. */
|
|
38
|
+
filter?: FilterDef;
|
|
39
|
+
/** Label shown in the `$` popup (defaults to header ?? id). */
|
|
40
|
+
label?: string;
|
|
41
|
+
};
|
|
42
|
+
/** The serializable state the table emits; your remote `query()` keys on this. */
|
|
43
|
+
export type GridState = {
|
|
44
|
+
q: string;
|
|
45
|
+
sort: Sort | null;
|
|
46
|
+
page: number;
|
|
47
|
+
perPage: number;
|
|
48
|
+
};
|
|
49
|
+
export type FetchResult<T> = {
|
|
50
|
+
rows: T[];
|
|
51
|
+
total: number;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Minimal reactive query result the table consumes. SvelteKit's `RemoteQuery` satisfies this
|
|
55
|
+
* structurally, so a remote `query()` can be passed directly — but the lib stays framework-agnostic
|
|
56
|
+
* (any object exposing reactive `current`/`loading` works).
|
|
57
|
+
*/
|
|
58
|
+
export interface QueryResult<T> {
|
|
59
|
+
readonly current?: T;
|
|
60
|
+
readonly loading: boolean;
|
|
61
|
+
readonly error?: unknown;
|
|
62
|
+
}
|
|
63
|
+
/** Server-controlled fetch: returns a reactive result carrying rows + total. */
|
|
64
|
+
export type FetchFn<T> = (state: GridState) => QueryResult<FetchResult<T>>;
|
|
65
|
+
export type ParsedQuery = QueryNode | null;
|
|
66
|
+
/**
|
|
67
|
+
* A row/bulk kebab menu entry. Convention: always render ALL items and `disabled` the invalid
|
|
68
|
+
* ones. Icons are optional — when ANY item has an icon, icon-less items reserve the icon column.
|
|
69
|
+
*/
|
|
70
|
+
export type MenuItem = {
|
|
71
|
+
label: string;
|
|
72
|
+
icon?: Component<{
|
|
73
|
+
class?: string;
|
|
74
|
+
}>;
|
|
75
|
+
onclick?: () => void;
|
|
76
|
+
href?: string;
|
|
77
|
+
disabled?: boolean;
|
|
78
|
+
danger?: boolean;
|
|
79
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,50 @@
|
|
|
1
|
+
export { default as Alert } from "./components/Alert.svelte";
|
|
2
|
+
export { default as Banner } from "./components/Banner.svelte";
|
|
1
3
|
export { default as Badge } from "./components/Badge/Badge.svelte";
|
|
2
|
-
export { default as Button } from "./components/Button
|
|
3
|
-
export { default as
|
|
4
|
-
export { default as
|
|
5
|
-
export { default as
|
|
6
|
-
export { default as
|
|
7
|
-
export { default as
|
|
8
|
-
export { default as
|
|
9
|
-
export {
|
|
10
|
-
export { default as
|
|
11
|
-
export { default as
|
|
12
|
-
export { default as
|
|
13
|
-
export { default as
|
|
14
|
-
export { default as
|
|
15
|
-
export { default as
|
|
16
|
-
export { default as
|
|
17
|
-
export { default as
|
|
18
|
-
export { default as
|
|
19
|
-
export { default as
|
|
20
|
-
export { default as
|
|
21
|
-
export {
|
|
22
|
-
export
|
|
23
|
-
export
|
|
4
|
+
export { default as Button } from "./components/Button.svelte";
|
|
5
|
+
export { default as ButtonDropdown } from "./components/ButtonDropdown.svelte";
|
|
6
|
+
export { default as Card } from "./components/Card.svelte";
|
|
7
|
+
export { default as CardHeader } from "./components/CardHeader.svelte";
|
|
8
|
+
export { default as CardBody } from "./components/CardBody.svelte";
|
|
9
|
+
export { default as CardFooter } from "./components/CardFooter.svelte";
|
|
10
|
+
export { default as Toaster } from "./components/Toast/Toaster.svelte";
|
|
11
|
+
export { toast, getToasts, dismissToast, type ToastVariant, type ToastItem, } from "./components/Toast/toast.svelte.js";
|
|
12
|
+
export { default as Dialog } from "./components/Dialog.svelte";
|
|
13
|
+
export { default as Input } from "./components/Input.svelte";
|
|
14
|
+
export { default as NotesEditor } from "./components/NotesEditor.svelte";
|
|
15
|
+
export { default as Rating } from "./components/Rating.svelte";
|
|
16
|
+
export { default as Select } from "./components/Select.svelte";
|
|
17
|
+
export { default as List } from "./components/List.svelte";
|
|
18
|
+
export { default as TextArea } from "./components/TextArea.svelte";
|
|
19
|
+
export { default as PasswordInput } from "./components/PasswordInput.svelte";
|
|
20
|
+
export { default as EmailInput } from "./components/EmailInput.svelte";
|
|
21
|
+
export { default as SearchInput } from "./components/SearchInput.svelte";
|
|
22
|
+
export { default as NumberInput } from "./components/NumberInput.svelte";
|
|
23
|
+
export { default as DateTimeInput } from "./components/DateTimeInput.svelte";
|
|
24
|
+
export { default as Calendar } from "./components/Calendar.svelte";
|
|
25
|
+
export { default as DatePicker } from "./components/DatePicker.svelte";
|
|
26
|
+
export { default as TimeSpinner } from "./components/TimeSpinner.svelte";
|
|
27
|
+
export { default as TimePicker } from "./components/TimePicker.svelte";
|
|
28
|
+
export { default as TimeRangeInput } from "./components/TimeRangeInput.svelte";
|
|
29
|
+
export { default as ThemeSelector } from "./components/ThemeSelector.svelte";
|
|
30
|
+
export { default as Popup } from "./components/Popup.svelte";
|
|
31
|
+
export { default as Menu } from "./components/Menu.svelte";
|
|
32
|
+
export { default as Checkbox } from "./components/Checkbox.svelte";
|
|
33
|
+
export { default as Toggle } from "./components/Toggle.svelte";
|
|
34
|
+
export { default as SideBarMenu } from "./components/SideBarMenu/SideBarMenu.svelte";
|
|
35
|
+
export type { SideBarMenuItem, SideBarMenuBadge, SideBarMenuBadgeVariant, } from "./components/SideBarMenu/SideBarMenu.svelte";
|
|
36
|
+
export { default as TabStrip } from "./components/TabStrip/TabStrip.svelte";
|
|
37
|
+
export type { TabDefinition } from "./components/TabStrip/TabStrip.svelte";
|
|
38
|
+
export { default as DataTable } from "./components/DataTable.svelte";
|
|
39
|
+
export { default as Query } from "./components/Query.svelte";
|
|
40
|
+
export { anchored, computePlacement, resolveBoundary } from "./positioning/anchored.js";
|
|
41
|
+
export type { Side, Align, Boundary, PlaceOptions, Placement, AnchoredParams, } from "./positioning/anchored.js";
|
|
42
|
+
export { tooltip } from "./positioning/tooltip.js";
|
|
43
|
+
export type { TooltipOptions } from "./positioning/tooltip.js";
|
|
24
44
|
export type { Variant } from "./types/variants.js";
|
|
45
|
+
export type { Size } from "./types/sizes.js";
|
|
46
|
+
export type { StepSpec } from "./types/time.js";
|
|
47
|
+
export type { Breakpoint } from "./types/breakpoints.js";
|
|
48
|
+
export { BREAKPOINT_PX } from "./types/breakpoints.js";
|
|
49
|
+
export type { Responsive } from "./types/responsive.js";
|
|
50
|
+
export { responsiveClasses, resolveScalar } from "./types/responsive.js";
|