@lapis-notes/bases 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/CHANGELOG.md +6 -0
- package/LICENSE.md +8 -0
- package/README.md +20 -0
- package/dist/bases-plugin.d.ts +10 -0
- package/dist/bases-plugin.js +118 -0
- package/dist/bases-view/app-database-query-source.d.ts +30 -0
- package/dist/bases-view/app-database-query-source.js +248 -0
- package/dist/bases-view/bases.svelte.d.ts +569 -0
- package/dist/bases-view/bases.svelte.js +910 -0
- package/dist/bases-view/card-view/components/card-placeholder.svelte +68 -0
- package/dist/bases-view/card-view/components/card-placeholder.svelte.d.ts +7 -0
- package/dist/bases-view/card-view/components/card.svelte +289 -0
- package/dist/bases-view/card-view/components/card.svelte.d.ts +7 -0
- package/dist/bases-view/card-view/index.d.ts +11 -0
- package/dist/bases-view/card-view/index.js +55 -0
- package/dist/bases-view/cell-editor-options.d.ts +2 -0
- package/dist/bases-view/cell-editor-options.js +26 -0
- package/dist/bases-view/cell-editor.svelte +206 -0
- package/dist/bases-view/cell-editor.svelte.d.ts +12 -0
- package/dist/bases-view/cell.svelte +132 -0
- package/dist/bases-view/cell.svelte.d.ts +16 -0
- package/dist/bases-view/column-definition.d.ts +22 -0
- package/dist/bases-view/column-definition.js +149 -0
- package/dist/bases-view/columns.d.ts +15 -0
- package/dist/bases-view/columns.js +103 -0
- package/dist/bases-view/components/Tag.svelte +14 -0
- package/dist/bases-view/components/Tag.svelte.d.ts +8 -0
- package/dist/bases-view/components/autocomplete.svelte +45 -0
- package/dist/bases-view/components/autocomplete.svelte.d.ts +17 -0
- package/dist/bases-view/components/card-image.svelte +121 -0
- package/dist/bases-view/components/card-image.svelte.d.ts +11 -0
- package/dist/bases-view/components/column-visibility.svelte +388 -0
- package/dist/bases-view/components/column-visibility.svelte.d.ts +13 -0
- package/dist/bases-view/components/datetime.svelte +35 -0
- package/dist/bases-view/components/datetime.svelte.d.ts +7 -0
- package/dist/bases-view/components/file.svelte +31 -0
- package/dist/bases-view/components/file.svelte.d.ts +8 -0
- package/dist/bases-view/components/filters/filter-editor.svelte +102 -0
- package/dist/bases-view/components/filters/filter-editor.svelte.d.ts +10 -0
- package/dist/bases-view/components/filters/filter-group.svelte +142 -0
- package/dist/bases-view/components/filters/filter-group.svelte.d.ts +11 -0
- package/dist/bases-view/components/filters/filter-properties.svelte +77 -0
- package/dist/bases-view/components/filters/filter-properties.svelte.d.ts +8 -0
- package/dist/bases-view/components/filters/filter-root.svelte +112 -0
- package/dist/bases-view/components/filters/filter-root.svelte.d.ts +12 -0
- package/dist/bases-view/components/filters/filter-row.svelte +351 -0
- package/dist/bases-view/components/filters/filter-row.svelte.d.ts +11 -0
- package/dist/bases-view/components/group/group-root.svelte +120 -0
- package/dist/bases-view/components/group/group-root.svelte.d.ts +8 -0
- package/dist/bases-view/components/limit.svelte +100 -0
- package/dist/bases-view/components/limit.svelte.d.ts +12 -0
- package/dist/bases-view/components/query-editor.svelte +230 -0
- package/dist/bases-view/components/query-editor.svelte.d.ts +15 -0
- package/dist/bases-view/components/sort/sort-direction-labels.d.ts +9 -0
- package/dist/bases-view/components/sort/sort-direction-labels.js +25 -0
- package/dist/bases-view/components/sort/sort-root.svelte +239 -0
- package/dist/bases-view/components/sort/sort-root.svelte.d.ts +11 -0
- package/dist/bases-view/components/sort/sort-row.svelte +190 -0
- package/dist/bases-view/components/sort/sort-row.svelte.d.ts +15 -0
- package/dist/bases-view/components/summary-strip.svelte +56 -0
- package/dist/bases-view/components/summary-strip.svelte.d.ts +8 -0
- package/dist/bases-view/components/tag-chip.svelte +47 -0
- package/dist/bases-view/components/tag-chip.svelte.d.ts +12 -0
- package/dist/bases-view/components/views.svelte +178 -0
- package/dist/bases-view/components/views.svelte.d.ts +12 -0
- package/dist/bases-view/data-table-adapter/data-table.svelte.d.ts +45 -0
- package/dist/bases-view/data-table-adapter/data-table.svelte.js +112 -0
- package/dist/bases-view/data-table-adapter/flex-render.svelte +43 -0
- package/dist/bases-view/data-table-adapter/flex-render.svelte.d.ts +30 -0
- package/dist/bases-view/data-table-adapter/index.d.ts +3 -0
- package/dist/bases-view/data-table-adapter/index.js +3 -0
- package/dist/bases-view/data-table-adapter/render-helpers.d.ts +101 -0
- package/dist/bases-view/data-table-adapter/render-helpers.js +110 -0
- package/dist/bases-view/db.d.ts +31 -0
- package/dist/bases-view/db.js +174 -0
- package/dist/bases-view/document-core.d.ts +2 -0
- package/dist/bases-view/document-core.js +166 -0
- package/dist/bases-view/export-core.d.ts +4 -0
- package/dist/bases-view/export-core.js +31 -0
- package/dist/bases-view/file-fields-core.d.ts +31 -0
- package/dist/bases-view/file-fields-core.js +114 -0
- package/dist/bases-view/filter-parser.d.ts +27 -0
- package/dist/bases-view/filter-parser.js +315 -0
- package/dist/bases-view/functions-core.d.ts +66 -0
- package/dist/bases-view/functions-core.js +265 -0
- package/dist/bases-view/functions.d.ts +5 -0
- package/dist/bases-view/functions.js +107 -0
- package/dist/bases-view/fuzzy-search.d.ts +6 -0
- package/dist/bases-view/fuzzy-search.js +15 -0
- package/dist/bases-view/hooks/useResizeObserver.svelte.d.ts +7 -0
- package/dist/bases-view/hooks/useResizeObserver.svelte.js +26 -0
- package/dist/bases-view/index.d.ts +66 -0
- package/dist/bases-view/index.js +231 -0
- package/dist/bases-view/list-view/index.d.ts +10 -0
- package/dist/bases-view/list-view/index.js +17 -0
- package/dist/bases-view/list-view/list.svelte +156 -0
- package/dist/bases-view/list-view/list.svelte.d.ts +7 -0
- package/dist/bases-view/map-view/index.d.ts +10 -0
- package/dist/bases-view/map-view/index.js +17 -0
- package/dist/bases-view/map-view/view.svelte +28 -0
- package/dist/bases-view/map-view/view.svelte.d.ts +7 -0
- package/dist/bases-view/metadata-invalidation-core.d.ts +34 -0
- package/dist/bases-view/metadata-invalidation-core.js +162 -0
- package/dist/bases-view/metadata-type-info.d.ts +7 -0
- package/dist/bases-view/metadata-type-info.js +10 -0
- package/dist/bases-view/models.d.ts +90 -0
- package/dist/bases-view/models.js +133 -0
- package/dist/bases-view/query-result-core.d.ts +8 -0
- package/dist/bases-view/query-result-core.js +16 -0
- package/dist/bases-view/resource-url.d.ts +2 -0
- package/dist/bases-view/resource-url.js +21 -0
- package/dist/bases-view/search-core.d.ts +9 -0
- package/dist/bases-view/search-core.js +20 -0
- package/dist/bases-view/summary-core.d.ts +7 -0
- package/dist/bases-view/summary-core.js +17 -0
- package/dist/bases-view/table-view/components/div-table.svelte +464 -0
- package/dist/bases-view/table-view/components/div-table.svelte.d.ts +32 -0
- package/dist/bases-view/table-view/components/dnd-style.d.ts +3 -0
- package/dist/bases-view/table-view/components/dnd-style.js +6 -0
- package/dist/bases-view/table-view/components/sort-header.svelte +48 -0
- package/dist/bases-view/table-view/components/sort-header.svelte.d.ts +11 -0
- package/dist/bases-view/table-view/components/table-column-tracks.d.ts +14 -0
- package/dist/bases-view/table-view/components/table-column-tracks.js +27 -0
- package/dist/bases-view/table-view/components/table-header.svelte +146 -0
- package/dist/bases-view/table-view/components/table-header.svelte.d.ts +10 -0
- package/dist/bases-view/table-view/components/table-placeholder.svelte +46 -0
- package/dist/bases-view/table-view/components/table-placeholder.svelte.d.ts +30 -0
- package/dist/bases-view/table-view/components/table-virtualizer-core.d.ts +13 -0
- package/dist/bases-view/table-view/components/table-virtualizer-core.js +12 -0
- package/dist/bases-view/table-view/components/table.svelte +997 -0
- package/dist/bases-view/table-view/components/table.svelte.d.ts +32 -0
- package/dist/bases-view/table-view/index.svelte.d.ts +10 -0
- package/dist/bases-view/table-view/index.svelte.js +30 -0
- package/dist/bases-view/unknown-view/index.d.ts +10 -0
- package/dist/bases-view/unknown-view/index.js +19 -0
- package/dist/bases-view/unknown-view/view.svelte +11 -0
- package/dist/bases-view/unknown-view/view.svelte.d.ts +6 -0
- package/dist/bases-view/view-header.svelte +142 -0
- package/dist/bases-view/view-header.svelte.d.ts +28 -0
- package/dist/bases-view/view-registration-inventory.d.ts +22 -0
- package/dist/bases-view/view-registration-inventory.js +7 -0
- package/dist/bases-view/view-settings.svelte +350 -0
- package/dist/bases-view/view-settings.svelte.d.ts +9 -0
- package/dist/bases-view/view.svelte +192 -0
- package/dist/bases-view/view.svelte.d.ts +15 -0
- package/dist/bases-view-surface.svelte +31 -0
- package/dist/bases-view-surface.svelte.d.ts +14 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/serialized-write-queue.d.ts +10 -0
- package/dist/serialized-write-queue.js +36 -0
- package/dist/styles.css +2164 -0
- package/manifest.json +33 -0
- package/package.json +90 -0
- package/styles.css +1 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
let filterErrorSequence = 0;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import type { Table } from "@tanstack/table-core";
|
|
7
|
+
import * as Popover from "@lapismd/design-core/shadcn/popover";
|
|
8
|
+
import * as Command from "@lapismd/design-core/shadcn/command";
|
|
9
|
+
import { columnName, filterTypeFor, isFilterLine } from "../..";
|
|
10
|
+
import { Icon } from "@lapis-notes/api/icon";
|
|
11
|
+
import CheckIcon from "@lucide/svelte/icons/check";
|
|
12
|
+
import CodeXML from "@lucide/svelte/icons/code-xml";
|
|
13
|
+
import FilterIcon from "@lucide/svelte/icons/mouse-pointer-click";
|
|
14
|
+
|
|
15
|
+
import Trash from "@lucide/svelte/icons/trash-2";
|
|
16
|
+
import ArrowUpDown from "@lucide/svelte/icons/chevrons-up-down";
|
|
17
|
+
import * as Select from "@lapismd/design-core/shadcn/select";
|
|
18
|
+
|
|
19
|
+
import { Button } from "@lapismd/design-core/shadcn/button";
|
|
20
|
+
import type { FilterLine } from "../..";
|
|
21
|
+
import { onMount, untrack } from "svelte";
|
|
22
|
+
import FilterEditor from "./filter-editor.svelte";
|
|
23
|
+
import { cn } from "@lapis-notes/api";
|
|
24
|
+
import {
|
|
25
|
+
resolveFilterDraft,
|
|
26
|
+
toSQL,
|
|
27
|
+
validateFilterPredicate,
|
|
28
|
+
} from "../../filter-parser";
|
|
29
|
+
import { Notice } from "@lapis-notes/api";
|
|
30
|
+
import QueryEditor from "../query-editor.svelte";
|
|
31
|
+
import type { BasesPropertyId, QueryController } from "../../bases.svelte";
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
label,
|
|
35
|
+
controller,
|
|
36
|
+
value = $bindable(),
|
|
37
|
+
deleteFilter,
|
|
38
|
+
}: {
|
|
39
|
+
label: string;
|
|
40
|
+
controller: QueryController;
|
|
41
|
+
value: string | FilterLine;
|
|
42
|
+
deleteFilter?: () => void;
|
|
43
|
+
} = $props();
|
|
44
|
+
|
|
45
|
+
let filter: FilterLine = $state({
|
|
46
|
+
column: "",
|
|
47
|
+
op: "",
|
|
48
|
+
value: "",
|
|
49
|
+
});
|
|
50
|
+
const filterErrorId = `bases-filter-error-${++filterErrorSequence}`;
|
|
51
|
+
let customDraft = $state("");
|
|
52
|
+
let customError = $state<string | null>(null);
|
|
53
|
+
|
|
54
|
+
let column = $derived(
|
|
55
|
+
filter.column
|
|
56
|
+
? controller.getColumn(filter.column as BasesPropertyId)
|
|
57
|
+
: undefined,
|
|
58
|
+
);
|
|
59
|
+
let type = $derived(column?.type ?? "unknown");
|
|
60
|
+
let filterTypes = $derived(filterTypeFor(column));
|
|
61
|
+
let simple = $derived(typeof filter.custom !== "string");
|
|
62
|
+
let filterValue = $derived(filterTypes.find((f) => f.value === filter.op));
|
|
63
|
+
let isValid = $derived(
|
|
64
|
+
filter.op.length > 0 &&
|
|
65
|
+
filter.column.length > 0 &&
|
|
66
|
+
filterValue &&
|
|
67
|
+
(filterValue.type === "none" || String(filter.value).length > 0),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
function equalFilterLines(
|
|
71
|
+
left: FilterLine | null | undefined,
|
|
72
|
+
right: FilterLine | null | undefined,
|
|
73
|
+
): boolean {
|
|
74
|
+
if (!left || !right) {
|
|
75
|
+
return left === right;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const equalValue =
|
|
79
|
+
Array.isArray(left.value) || Array.isArray(right.value)
|
|
80
|
+
? Array.isArray(left.value) &&
|
|
81
|
+
Array.isArray(right.value) &&
|
|
82
|
+
left.value.length === right.value.length &&
|
|
83
|
+
left.value.every((value, index) => value === right.value[index])
|
|
84
|
+
: left.value === right.value;
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
left.column === right.column &&
|
|
88
|
+
left.op === right.op &&
|
|
89
|
+
left.custom === right.custom &&
|
|
90
|
+
equalValue
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function syncCustomDraft(source: string, error: string | null = null) {
|
|
95
|
+
if (untrack(() => customDraft) !== source) {
|
|
96
|
+
customDraft = source;
|
|
97
|
+
}
|
|
98
|
+
customError = error;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function updateCustomDraft(source: string) {
|
|
102
|
+
customDraft = source;
|
|
103
|
+
const result = resolveFilterDraft(filter.custom ?? "", source);
|
|
104
|
+
customError = result.error;
|
|
105
|
+
if (result.valid) {
|
|
106
|
+
filter.custom = result.applied;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
$effect(() => {
|
|
111
|
+
if (isFilterLine(value)) {
|
|
112
|
+
if (!equalFilterLines(filter, value)) {
|
|
113
|
+
filter = { ...value };
|
|
114
|
+
}
|
|
115
|
+
if (typeof value.custom === "string") {
|
|
116
|
+
syncCustomDraft(value.custom);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof filter.custom === "string" && filter.custom === value) {
|
|
122
|
+
syncCustomDraft(value);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const validation = validateFilterPredicate(value);
|
|
127
|
+
if (!validation.valid) {
|
|
128
|
+
filter = { ...filter, custom: value };
|
|
129
|
+
syncCustomDraft(value, validation.error);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const predicate = validation.predicate;
|
|
134
|
+
if (isFilterLine(predicate)) {
|
|
135
|
+
if (!equalFilterLines(filter, predicate)) {
|
|
136
|
+
filter = predicate;
|
|
137
|
+
}
|
|
138
|
+
if (typeof predicate.custom === "string") {
|
|
139
|
+
syncCustomDraft(predicate.custom);
|
|
140
|
+
} else {
|
|
141
|
+
syncCustomDraft(value);
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
filter.op = filterTypes[0].value;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
$effect(() => {
|
|
149
|
+
if (filterValue && filterValue.type === "none" && filterValue.value) {
|
|
150
|
+
filterValue.value = "";
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
onMount(() => {
|
|
155
|
+
if (!filter.column) {
|
|
156
|
+
filter.column = controller.getAllColumns()[0]?.id;
|
|
157
|
+
}
|
|
158
|
+
if (!filter.value) {
|
|
159
|
+
filter.value = "";
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
function toggleSimple() {
|
|
164
|
+
if (simple) {
|
|
165
|
+
const source = toSQL(filter);
|
|
166
|
+
filter.custom = source;
|
|
167
|
+
syncCustomDraft(source);
|
|
168
|
+
} else {
|
|
169
|
+
const validation = validateFilterPredicate(customDraft);
|
|
170
|
+
customError = validation.error;
|
|
171
|
+
if (!validation.valid) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const predicate = validation.predicate;
|
|
175
|
+
if (isFilterLine(predicate) && !predicate.custom) {
|
|
176
|
+
filter.column = predicate.column;
|
|
177
|
+
filter.op = predicate.op;
|
|
178
|
+
filter.value = predicate.value;
|
|
179
|
+
filter.custom = null;
|
|
180
|
+
} else {
|
|
181
|
+
new Notice(
|
|
182
|
+
`This filter cannot be represented by the simple filter builder`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
$effect(() => {
|
|
189
|
+
const nextValue = isValid
|
|
190
|
+
? simple
|
|
191
|
+
? { ...filter, custom: null }
|
|
192
|
+
: (filter.custom ?? "")
|
|
193
|
+
: "";
|
|
194
|
+
|
|
195
|
+
if (typeof nextValue === "string") {
|
|
196
|
+
if (value !== nextValue) {
|
|
197
|
+
value = nextValue;
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!isFilterLine(value) || !equalFilterLines(value, nextValue)) {
|
|
203
|
+
value = nextValue;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
let columnsIsOpen: boolean = $state(false);
|
|
207
|
+
</script>
|
|
208
|
+
|
|
209
|
+
<div class="filter-row bases-style-flex-60fbb7 bases-style-items-center-3960ff bases-style-gap-0-63a285">
|
|
210
|
+
<span class="filter-row__label bases-style-min-w-4-5rem-00331a bases-style-pr-1-eda955 bases-style-text-right-308fc0"
|
|
211
|
+
>{label}</span
|
|
212
|
+
>
|
|
213
|
+
<div class="filter-row__field">
|
|
214
|
+
<div
|
|
215
|
+
class={cn(
|
|
216
|
+
"filter-row__controls bases-style-bg-background-e6f9e3 bases-style-flex-60fbb7 bases-style-min-h-9-968a1e bases-style-items-center-3960ff bases-style-rounded-md-421ac2 bases-style-border-2-65935d",
|
|
217
|
+
)}
|
|
218
|
+
data-invalid={customError !== null}
|
|
219
|
+
>
|
|
220
|
+
{#if simple}
|
|
221
|
+
<Popover.Root bind:open={columnsIsOpen}>
|
|
222
|
+
<Popover.Trigger>
|
|
223
|
+
{#snippet child({ props }: { props: Record<string, any> })}
|
|
224
|
+
<Button
|
|
225
|
+
{...props}
|
|
226
|
+
variant="outline"
|
|
227
|
+
size="sm"
|
|
228
|
+
class="bases-style-min-h-inherit-05c993 bases-style-h-auto-b8f0a0 bases-style-self-stretch-18069a bases-style-rounded-none-0c5e91 bases-style-rounded-l-md-9b2e91 bases-style-border-none-4a5f0e bases-style-shadow-none-ad47d1 bases-style-outline-none-df37b1"
|
|
229
|
+
>
|
|
230
|
+
<Icon name={[column?.icon ?? "lucide-file"]} />
|
|
231
|
+
{column?.displayName}
|
|
232
|
+
<ArrowUpDown />
|
|
233
|
+
</Button>
|
|
234
|
+
{/snippet}
|
|
235
|
+
</Popover.Trigger>
|
|
236
|
+
<Popover.Content class="bases-style-w-max-2f935d bases-style-p-0-8a539c" align="start">
|
|
237
|
+
<Command.Root>
|
|
238
|
+
<Command.Input placeholder="Find or create" />
|
|
239
|
+
<Command.List class="bases-style-p-1-eb6a3c">
|
|
240
|
+
<Command.Empty>No results found.</Command.Empty>
|
|
241
|
+
{#each controller.getAllColumns() as column (column.id)}
|
|
242
|
+
<Command.Item
|
|
243
|
+
onSelect={() => {
|
|
244
|
+
filter.column = column.id;
|
|
245
|
+
columnsIsOpen = false;
|
|
246
|
+
}}
|
|
247
|
+
>
|
|
248
|
+
<div
|
|
249
|
+
class={cn(
|
|
250
|
+
"bases-style-mr-2-d2347e bases-style-flex-60fbb7 bases-style-size-4-f7b5fa bases-style-items-center-3960ff bases-style-justify-center-86843c",
|
|
251
|
+
filter.column === column.id
|
|
252
|
+
? ""
|
|
253
|
+
: "bases-style-opacity-50-0b8c50 [&_svg]:invisible",
|
|
254
|
+
)}
|
|
255
|
+
>
|
|
256
|
+
<CheckIcon class="bases-style-size-4-f7b5fa" />
|
|
257
|
+
</div>
|
|
258
|
+
<Icon name={[column?.icon ?? "lucide-info"]} />
|
|
259
|
+
<span>{column?.displayName}</span>
|
|
260
|
+
{#if !column?.id?.startsWith("note.")}
|
|
261
|
+
<Command.Shortcut class="bases-style-font-mono-0e6570 bases-style-text-0-75em-c425cc"
|
|
262
|
+
>{column?.id}</Command.Shortcut
|
|
263
|
+
>
|
|
264
|
+
{/if}
|
|
265
|
+
</Command.Item>
|
|
266
|
+
{/each}
|
|
267
|
+
</Command.List>
|
|
268
|
+
</Command.Root>
|
|
269
|
+
</Popover.Content>
|
|
270
|
+
</Popover.Root>
|
|
271
|
+
<Select.Root type="single" bind:value={filter.op}>
|
|
272
|
+
<Select.Trigger
|
|
273
|
+
size="sm"
|
|
274
|
+
data-bases-filter-control="operator-trigger"
|
|
275
|
+
class="bases-style-bg-background-e6f9e3 bases-style-min-h-inherit-05c993 bases-style-h-auto-b8f0a0 bases-style-self-stretch-18069a bases-style-rounded-none-0c5e91 bases-style-border-none-4a5f0e bases-style-shadow-none-ad47d1 bases-style-outline-none-df37b1"
|
|
276
|
+
>
|
|
277
|
+
{filterValue?.label || filter.op}
|
|
278
|
+
</Select.Trigger>
|
|
279
|
+
<Select.Content
|
|
280
|
+
align="start"
|
|
281
|
+
data-bases-filter-control="operator-content"
|
|
282
|
+
>
|
|
283
|
+
<Select.Group>
|
|
284
|
+
{#each filterTypes as filterType (filterType.value)}
|
|
285
|
+
<Select.Item value={filterType.value} label={filterType.label}
|
|
286
|
+
>{filterType.label}</Select.Item
|
|
287
|
+
>
|
|
288
|
+
{/each}
|
|
289
|
+
</Select.Group>
|
|
290
|
+
</Select.Content>
|
|
291
|
+
</Select.Root>
|
|
292
|
+
<FilterEditor
|
|
293
|
+
type={filterValue?.type ?? type}
|
|
294
|
+
{controller}
|
|
295
|
+
bind:value={filter.value}
|
|
296
|
+
/>
|
|
297
|
+
<Button
|
|
298
|
+
onclick={() => toggleSimple()}
|
|
299
|
+
data-tooltip="Simple filter"
|
|
300
|
+
aria-label="Use custom filter"
|
|
301
|
+
variant="ghost"
|
|
302
|
+
size="sm"
|
|
303
|
+
class="bases-style-bg-background-e6f9e3 bases-style-min-h-inherit-05c993 bases-style-h-auto-b8f0a0 bases-style-self-stretch-18069a bases-style-rounded-none-0c5e91 bases-style-border-none-4a5f0e bases-style-shadow-none-ad47d1 bases-style-outline-none-df37b1"
|
|
304
|
+
>
|
|
305
|
+
<CodeXML />
|
|
306
|
+
</Button>
|
|
307
|
+
{:else}
|
|
308
|
+
<QueryEditor
|
|
309
|
+
{controller}
|
|
310
|
+
onBlur={updateCustomDraft}
|
|
311
|
+
onDocChange={updateCustomDraft}
|
|
312
|
+
bind:content={customDraft}
|
|
313
|
+
invalid={customError !== null}
|
|
314
|
+
describedBy={customError ? filterErrorId : undefined}
|
|
315
|
+
class="bases-style-bg-background-e6f9e3 bases-style-h-full-668b21 grow bases-style-rounded-l-md-9b2e91 bases-style-border-none-4a5f0e bases-style-pl-1-6ad214 bases-style-outline-none-df37b1"
|
|
316
|
+
/>
|
|
317
|
+
<Button
|
|
318
|
+
onclick={() => toggleSimple()}
|
|
319
|
+
data-tooltip="Simple filter"
|
|
320
|
+
aria-label="Use simple filter"
|
|
321
|
+
variant="ghost"
|
|
322
|
+
size="sm"
|
|
323
|
+
class="bases-style-min-h-inherit-05c993 bases-style-bg-background-e6f9e3 bases-style-h-auto-b8f0a0 bases-style-self-stretch-18069a bases-style-rounded-none-0c5e91 bases-style-border-none-4a5f0e bases-style-shadow-none-ad47d1 bases-style-outline-none-df37b1"
|
|
324
|
+
>
|
|
325
|
+
<FilterIcon />
|
|
326
|
+
</Button>
|
|
327
|
+
{/if}
|
|
328
|
+
{#if deleteFilter}
|
|
329
|
+
<Button
|
|
330
|
+
variant="ghost"
|
|
331
|
+
size="sm"
|
|
332
|
+
aria-label="Delete filter"
|
|
333
|
+
class="bases-style-bg-background-e6f9e3 bases-style-min-h-inherit-05c993 bases-style-h-auto-b8f0a0 bases-style-self-stretch-18069a bases-style-rounded-none-0c5e91 bases-style-rounded-r-md-d4eb07 bases-style-border-none-4a5f0e bases-style-shadow-none-ad47d1 bases-style-outline-none-df37b1"
|
|
334
|
+
onclick={() => deleteFilter()}
|
|
335
|
+
>
|
|
336
|
+
<Trash />
|
|
337
|
+
</Button>
|
|
338
|
+
{/if}
|
|
339
|
+
</div>
|
|
340
|
+
{#if customError}
|
|
341
|
+
<p
|
|
342
|
+
id={filterErrorId}
|
|
343
|
+
class="filter-row__error"
|
|
344
|
+
data-ui-part="filter-error"
|
|
345
|
+
role="alert"
|
|
346
|
+
>
|
|
347
|
+
{customError}
|
|
348
|
+
</p>
|
|
349
|
+
{/if}
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FilterLine } from "../..";
|
|
2
|
+
import type { QueryController } from "../../bases.svelte";
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
label: string;
|
|
5
|
+
controller: QueryController;
|
|
6
|
+
value: string | FilterLine;
|
|
7
|
+
deleteFilter?: () => void;
|
|
8
|
+
};
|
|
9
|
+
declare const FilterRow: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
10
|
+
type FilterRow = ReturnType<typeof FilterRow>;
|
|
11
|
+
export default FilterRow;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as Command from "@lapismd/design-core/shadcn/command";
|
|
3
|
+
import * as Popover from "@lapismd/design-core/shadcn/popover";
|
|
4
|
+
import * as Select from "@lapismd/design-core/shadcn/select";
|
|
5
|
+
import { Button } from "@lapismd/design-core/shadcn/button";
|
|
6
|
+
import { cn } from "@lapis-notes/api";
|
|
7
|
+
import CheckIcon from "@lucide/svelte/icons/check";
|
|
8
|
+
import type { QueryController } from "../../bases.svelte";
|
|
9
|
+
import { Icon } from "@lapis-notes/api/icon";
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
controller,
|
|
13
|
+
class: className,
|
|
14
|
+
}: {
|
|
15
|
+
controller: QueryController;
|
|
16
|
+
class?: string;
|
|
17
|
+
} = $props();
|
|
18
|
+
|
|
19
|
+
const directions = [
|
|
20
|
+
{ label: "Ascending", value: "ASC" },
|
|
21
|
+
{ label: "Descending", value: "DESC" },
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
let groupBy = $derived(controller.selectedView.groupBy);
|
|
25
|
+
let selectedColumn = $derived.by(() => {
|
|
26
|
+
return groupBy?.property
|
|
27
|
+
? controller.getColumn(groupBy.property)
|
|
28
|
+
: undefined;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
function setGroupBy(property: string) {
|
|
32
|
+
controller.selectedView.groupBy = {
|
|
33
|
+
property,
|
|
34
|
+
direction: controller.selectedView.groupBy?.direction ?? "ASC",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getDirection() {
|
|
39
|
+
return controller.selectedView.groupBy?.direction ?? "ASC";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function setDirection(direction: string) {
|
|
43
|
+
if (!controller.selectedView.groupBy) return;
|
|
44
|
+
controller.selectedView.groupBy = {
|
|
45
|
+
...controller.selectedView.groupBy,
|
|
46
|
+
direction: direction === "DESC" ? "DESC" : "ASC",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<Popover.Root>
|
|
52
|
+
<Popover.Trigger>
|
|
53
|
+
{#snippet child({ props }: { props: Record<string, unknown> })}
|
|
54
|
+
<Button
|
|
55
|
+
{...props}
|
|
56
|
+
variant="outline"
|
|
57
|
+
size="sm"
|
|
58
|
+
class={cn("bases-style-h-8-ed8a5d bases-style-border-dashed-a29b7a", className, {
|
|
59
|
+
"bases-style-text-var-text-accent-69c994 bases-style-hover-bg-color-mix-in-srgb-var-interactive-4e3e8a bases-style-hover-text-var-text-accent-5b2cb5":
|
|
60
|
+
!!groupBy?.property,
|
|
61
|
+
})}
|
|
62
|
+
>
|
|
63
|
+
Group
|
|
64
|
+
</Button>
|
|
65
|
+
{/snippet}
|
|
66
|
+
</Popover.Trigger>
|
|
67
|
+
<Popover.Content class="bases-style-w-320px-57f3af bases-style-p-0-8a539c" align="start">
|
|
68
|
+
<Command.Root>
|
|
69
|
+
<Command.Input placeholder="Group by property" />
|
|
70
|
+
<Command.List class="bases-style-p-1-eb6a3c">
|
|
71
|
+
<Command.Empty>No properties found.</Command.Empty>
|
|
72
|
+
{#each controller.getAllColumns() as column (column.id)}
|
|
73
|
+
<Command.Item onSelect={() => setGroupBy(column.id)}>
|
|
74
|
+
<div
|
|
75
|
+
data-ui-part="bases-option-indicator"
|
|
76
|
+
data-selected={groupBy?.property === column.id}
|
|
77
|
+
class={cn(
|
|
78
|
+
"bases-style-mr-2-d2347e bases-style-flex-60fbb7 bases-style-size-4-f7b5fa bases-style-items-center-3960ff bases-style-justify-center-86843c bases-style-rounded-sm-36d446 border bases-style-border-var-interactive-accent-f46c93",
|
|
79
|
+
groupBy?.property === column.id
|
|
80
|
+
? "bases-style-ring-ring-3e1868 bases-style-border-none-4a5f0e bases-style-bg-var-interactive-accent-c58cc0 bases-style-text-var-text-on-accent-22b2b1 hover:ring"
|
|
81
|
+
: "bases-style-opacity-50-0b8c50 [&_svg]:invisible",
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
<CheckIcon class="bases-style-size-4-f7b5fa" />
|
|
85
|
+
</div>
|
|
86
|
+
<Icon name={[column.icon ?? "lucide-file"]} />
|
|
87
|
+
<span class="truncate">{column.displayName}</span>
|
|
88
|
+
</Command.Item>
|
|
89
|
+
{/each}
|
|
90
|
+
</Command.List>
|
|
91
|
+
<div class="bases-style-border-t-b950dd bases-style-p-2-7660b4">
|
|
92
|
+
<div class="bases-style-grid-f3c543 bases-style-grid-cols-1-d7c833 bases-style-gap-2-77a2a2">
|
|
93
|
+
<Select.Root type="single" bind:value={getDirection, setDirection}>
|
|
94
|
+
<Select.Trigger size="sm" class="bases-style-bg-background-e6f9e3 bases-style-h-8-ed8a5d bases-style-justify-between-8ef226">
|
|
95
|
+
{directions.find(
|
|
96
|
+
(direction) => direction.value === getDirection(),
|
|
97
|
+
)?.label}
|
|
98
|
+
</Select.Trigger>
|
|
99
|
+
<Select.Content>
|
|
100
|
+
{#each directions as direction (direction.value)}
|
|
101
|
+
<Select.Item value={direction.value} label={direction.label}
|
|
102
|
+
>{direction.label}</Select.Item
|
|
103
|
+
>
|
|
104
|
+
{/each}
|
|
105
|
+
</Select.Content>
|
|
106
|
+
</Select.Root>
|
|
107
|
+
<Button
|
|
108
|
+
variant="ghost"
|
|
109
|
+
size="sm"
|
|
110
|
+
class="bases-style-justify-start-4b5cc1"
|
|
111
|
+
disabled={!selectedColumn}
|
|
112
|
+
onclick={() => (controller.selectedView.groupBy = undefined)}
|
|
113
|
+
>
|
|
114
|
+
Clear grouping
|
|
115
|
+
</Button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</Command.Root>
|
|
119
|
+
</Popover.Content>
|
|
120
|
+
</Popover.Root>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { QueryController } from "../../bases.svelte";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
controller: QueryController;
|
|
4
|
+
class?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const GroupRoot: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type GroupRoot = ReturnType<typeof GroupRoot>;
|
|
8
|
+
export default GroupRoot;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as Popover from "@lapismd/design-core/shadcn/popover";
|
|
3
|
+
import * as Command from "@lapismd/design-core/shadcn/command";
|
|
4
|
+
import { Button } from "@lapismd/design-core/shadcn/button";
|
|
5
|
+
import { Label } from "@lapismd/design-core/shadcn/label";
|
|
6
|
+
import { Input } from "@lapismd/design-core/shadcn/input";
|
|
7
|
+
import type { BasesViewBase } from "../models";
|
|
8
|
+
import { cn } from "@lapis-notes/api";
|
|
9
|
+
import Copy from "@lucide/svelte/icons/copy";
|
|
10
|
+
import Download from "@lucide/svelte/icons/download";
|
|
11
|
+
import ShowAll from "@lucide/svelte/icons/rotate-ccw";
|
|
12
|
+
let {
|
|
13
|
+
count,
|
|
14
|
+
view = $bindable(),
|
|
15
|
+
onCopyCsv,
|
|
16
|
+
onExportCsv,
|
|
17
|
+
table,
|
|
18
|
+
class: className,
|
|
19
|
+
}: {
|
|
20
|
+
count: number;
|
|
21
|
+
view: BasesViewBase;
|
|
22
|
+
onCopyCsv?: () => void | Promise<void>;
|
|
23
|
+
onExportCsv?: () => void;
|
|
24
|
+
table?: any;
|
|
25
|
+
class?: string;
|
|
26
|
+
} = $props();
|
|
27
|
+
|
|
28
|
+
let showAll = $derived.by(() => {
|
|
29
|
+
return !view.limit || view.limit <= 0;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function changeLimit(event: FocusEvent) {
|
|
33
|
+
const target = event.target as HTMLInputElement;
|
|
34
|
+
const value = +target.value;
|
|
35
|
+
if (value !== view.limit) {
|
|
36
|
+
view.limit = value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<Popover.Root>
|
|
42
|
+
<Popover.Trigger>
|
|
43
|
+
{#snippet child({ props }: { props: Record<string, unknown> })}
|
|
44
|
+
<Button
|
|
45
|
+
{...props}
|
|
46
|
+
variant="outline"
|
|
47
|
+
size="sm"
|
|
48
|
+
class={cn("bases-style-h-8-ed8a5d bases-style-border-dashed-a29b7a", className)}
|
|
49
|
+
>
|
|
50
|
+
{count} results
|
|
51
|
+
</Button>
|
|
52
|
+
{/snippet}
|
|
53
|
+
</Popover.Trigger>
|
|
54
|
+
<Popover.Content class="bases-style-w-270px-448793 bases-style-min-w-220px-cbc208 bases-style-p-0-8a539c" align="start">
|
|
55
|
+
<Command.Root>
|
|
56
|
+
<div class="bases-style-p-2-7660b4">
|
|
57
|
+
<div class="bases-style-grid-f3c543 bases-style-grid-cols-1-d7c833 bases-style-items-center-3960ff bases-style-gap-2-77a2a2 bases-style-px-2-d5eab2 bases-style-pt-1-6b7d6e">
|
|
58
|
+
<Label for="column_name" class="bases-style-text-sm-fc7473 bases-style-opacity-50-0b8c50"
|
|
59
|
+
>Limit number of results</Label
|
|
60
|
+
>
|
|
61
|
+
<Input
|
|
62
|
+
type="number"
|
|
63
|
+
placeholder="e.g 10"
|
|
64
|
+
id="column_name"
|
|
65
|
+
onblur={(evt) => changeLimit(evt)}
|
|
66
|
+
value={showAll ? "" : view.limit}
|
|
67
|
+
class="bases-style-col-span-2-40efc0 bases-style-h-8-ed8a5d"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="bases-style-mt-2-50d0d2 bases-style-grid-f3c543 bases-style-grid-cols-1-d7c833 bases-style-items-center-3960ff bases-style-gap-2-77a2a2 bases-style-px-2-d5eab2 bases-style-pt-1-6b7d6e">
|
|
71
|
+
<Button
|
|
72
|
+
disabled={showAll}
|
|
73
|
+
size="sm"
|
|
74
|
+
onclick={() => (view.limit = 0)}
|
|
75
|
+
class={cn("bases-style-w-full-6da6a3 bases-style-justify-start-4b5cc1")}
|
|
76
|
+
variant="ghost"
|
|
77
|
+
>
|
|
78
|
+
<ShowAll /> Show all
|
|
79
|
+
</Button>
|
|
80
|
+
<Button
|
|
81
|
+
size="sm"
|
|
82
|
+
onclick={() => onCopyCsv?.()}
|
|
83
|
+
class={cn("bases-style-w-full-6da6a3 bases-style-justify-start-4b5cc1")}
|
|
84
|
+
variant="ghost"
|
|
85
|
+
>
|
|
86
|
+
<Copy /> Copy to clipboard
|
|
87
|
+
</Button>
|
|
88
|
+
<Button
|
|
89
|
+
size="sm"
|
|
90
|
+
onclick={() => onExportCsv?.()}
|
|
91
|
+
class={cn("bases-style-w-full-6da6a3 bases-style-justify-start-4b5cc1")}
|
|
92
|
+
variant="ghost"
|
|
93
|
+
>
|
|
94
|
+
<Download /> Export CSV...
|
|
95
|
+
</Button>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</Command.Root>
|
|
99
|
+
</Popover.Content>
|
|
100
|
+
</Popover.Root>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BasesViewBase } from "../models";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
count: number;
|
|
4
|
+
view: BasesViewBase;
|
|
5
|
+
onCopyCsv?: () => void | Promise<void>;
|
|
6
|
+
onExportCsv?: () => void;
|
|
7
|
+
table?: any;
|
|
8
|
+
class?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const Limit: import("svelte").Component<$$ComponentProps, {}, "view">;
|
|
11
|
+
type Limit = ReturnType<typeof Limit>;
|
|
12
|
+
export default Limit;
|