@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.
Files changed (155) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +8 -0
  3. package/README.md +20 -0
  4. package/dist/bases-plugin.d.ts +10 -0
  5. package/dist/bases-plugin.js +118 -0
  6. package/dist/bases-view/app-database-query-source.d.ts +30 -0
  7. package/dist/bases-view/app-database-query-source.js +248 -0
  8. package/dist/bases-view/bases.svelte.d.ts +569 -0
  9. package/dist/bases-view/bases.svelte.js +910 -0
  10. package/dist/bases-view/card-view/components/card-placeholder.svelte +68 -0
  11. package/dist/bases-view/card-view/components/card-placeholder.svelte.d.ts +7 -0
  12. package/dist/bases-view/card-view/components/card.svelte +289 -0
  13. package/dist/bases-view/card-view/components/card.svelte.d.ts +7 -0
  14. package/dist/bases-view/card-view/index.d.ts +11 -0
  15. package/dist/bases-view/card-view/index.js +55 -0
  16. package/dist/bases-view/cell-editor-options.d.ts +2 -0
  17. package/dist/bases-view/cell-editor-options.js +26 -0
  18. package/dist/bases-view/cell-editor.svelte +206 -0
  19. package/dist/bases-view/cell-editor.svelte.d.ts +12 -0
  20. package/dist/bases-view/cell.svelte +132 -0
  21. package/dist/bases-view/cell.svelte.d.ts +16 -0
  22. package/dist/bases-view/column-definition.d.ts +22 -0
  23. package/dist/bases-view/column-definition.js +149 -0
  24. package/dist/bases-view/columns.d.ts +15 -0
  25. package/dist/bases-view/columns.js +103 -0
  26. package/dist/bases-view/components/Tag.svelte +14 -0
  27. package/dist/bases-view/components/Tag.svelte.d.ts +8 -0
  28. package/dist/bases-view/components/autocomplete.svelte +45 -0
  29. package/dist/bases-view/components/autocomplete.svelte.d.ts +17 -0
  30. package/dist/bases-view/components/card-image.svelte +121 -0
  31. package/dist/bases-view/components/card-image.svelte.d.ts +11 -0
  32. package/dist/bases-view/components/column-visibility.svelte +388 -0
  33. package/dist/bases-view/components/column-visibility.svelte.d.ts +13 -0
  34. package/dist/bases-view/components/datetime.svelte +35 -0
  35. package/dist/bases-view/components/datetime.svelte.d.ts +7 -0
  36. package/dist/bases-view/components/file.svelte +31 -0
  37. package/dist/bases-view/components/file.svelte.d.ts +8 -0
  38. package/dist/bases-view/components/filters/filter-editor.svelte +102 -0
  39. package/dist/bases-view/components/filters/filter-editor.svelte.d.ts +10 -0
  40. package/dist/bases-view/components/filters/filter-group.svelte +142 -0
  41. package/dist/bases-view/components/filters/filter-group.svelte.d.ts +11 -0
  42. package/dist/bases-view/components/filters/filter-properties.svelte +77 -0
  43. package/dist/bases-view/components/filters/filter-properties.svelte.d.ts +8 -0
  44. package/dist/bases-view/components/filters/filter-root.svelte +112 -0
  45. package/dist/bases-view/components/filters/filter-root.svelte.d.ts +12 -0
  46. package/dist/bases-view/components/filters/filter-row.svelte +351 -0
  47. package/dist/bases-view/components/filters/filter-row.svelte.d.ts +11 -0
  48. package/dist/bases-view/components/group/group-root.svelte +120 -0
  49. package/dist/bases-view/components/group/group-root.svelte.d.ts +8 -0
  50. package/dist/bases-view/components/limit.svelte +100 -0
  51. package/dist/bases-view/components/limit.svelte.d.ts +12 -0
  52. package/dist/bases-view/components/query-editor.svelte +230 -0
  53. package/dist/bases-view/components/query-editor.svelte.d.ts +15 -0
  54. package/dist/bases-view/components/sort/sort-direction-labels.d.ts +9 -0
  55. package/dist/bases-view/components/sort/sort-direction-labels.js +25 -0
  56. package/dist/bases-view/components/sort/sort-root.svelte +239 -0
  57. package/dist/bases-view/components/sort/sort-root.svelte.d.ts +11 -0
  58. package/dist/bases-view/components/sort/sort-row.svelte +190 -0
  59. package/dist/bases-view/components/sort/sort-row.svelte.d.ts +15 -0
  60. package/dist/bases-view/components/summary-strip.svelte +56 -0
  61. package/dist/bases-view/components/summary-strip.svelte.d.ts +8 -0
  62. package/dist/bases-view/components/tag-chip.svelte +47 -0
  63. package/dist/bases-view/components/tag-chip.svelte.d.ts +12 -0
  64. package/dist/bases-view/components/views.svelte +178 -0
  65. package/dist/bases-view/components/views.svelte.d.ts +12 -0
  66. package/dist/bases-view/data-table-adapter/data-table.svelte.d.ts +45 -0
  67. package/dist/bases-view/data-table-adapter/data-table.svelte.js +112 -0
  68. package/dist/bases-view/data-table-adapter/flex-render.svelte +43 -0
  69. package/dist/bases-view/data-table-adapter/flex-render.svelte.d.ts +30 -0
  70. package/dist/bases-view/data-table-adapter/index.d.ts +3 -0
  71. package/dist/bases-view/data-table-adapter/index.js +3 -0
  72. package/dist/bases-view/data-table-adapter/render-helpers.d.ts +101 -0
  73. package/dist/bases-view/data-table-adapter/render-helpers.js +110 -0
  74. package/dist/bases-view/db.d.ts +31 -0
  75. package/dist/bases-view/db.js +174 -0
  76. package/dist/bases-view/document-core.d.ts +2 -0
  77. package/dist/bases-view/document-core.js +166 -0
  78. package/dist/bases-view/export-core.d.ts +4 -0
  79. package/dist/bases-view/export-core.js +31 -0
  80. package/dist/bases-view/file-fields-core.d.ts +31 -0
  81. package/dist/bases-view/file-fields-core.js +114 -0
  82. package/dist/bases-view/filter-parser.d.ts +27 -0
  83. package/dist/bases-view/filter-parser.js +315 -0
  84. package/dist/bases-view/functions-core.d.ts +66 -0
  85. package/dist/bases-view/functions-core.js +265 -0
  86. package/dist/bases-view/functions.d.ts +5 -0
  87. package/dist/bases-view/functions.js +107 -0
  88. package/dist/bases-view/fuzzy-search.d.ts +6 -0
  89. package/dist/bases-view/fuzzy-search.js +15 -0
  90. package/dist/bases-view/hooks/useResizeObserver.svelte.d.ts +7 -0
  91. package/dist/bases-view/hooks/useResizeObserver.svelte.js +26 -0
  92. package/dist/bases-view/index.d.ts +66 -0
  93. package/dist/bases-view/index.js +231 -0
  94. package/dist/bases-view/list-view/index.d.ts +10 -0
  95. package/dist/bases-view/list-view/index.js +17 -0
  96. package/dist/bases-view/list-view/list.svelte +156 -0
  97. package/dist/bases-view/list-view/list.svelte.d.ts +7 -0
  98. package/dist/bases-view/map-view/index.d.ts +10 -0
  99. package/dist/bases-view/map-view/index.js +17 -0
  100. package/dist/bases-view/map-view/view.svelte +28 -0
  101. package/dist/bases-view/map-view/view.svelte.d.ts +7 -0
  102. package/dist/bases-view/metadata-invalidation-core.d.ts +34 -0
  103. package/dist/bases-view/metadata-invalidation-core.js +162 -0
  104. package/dist/bases-view/metadata-type-info.d.ts +7 -0
  105. package/dist/bases-view/metadata-type-info.js +10 -0
  106. package/dist/bases-view/models.d.ts +90 -0
  107. package/dist/bases-view/models.js +133 -0
  108. package/dist/bases-view/query-result-core.d.ts +8 -0
  109. package/dist/bases-view/query-result-core.js +16 -0
  110. package/dist/bases-view/resource-url.d.ts +2 -0
  111. package/dist/bases-view/resource-url.js +21 -0
  112. package/dist/bases-view/search-core.d.ts +9 -0
  113. package/dist/bases-view/search-core.js +20 -0
  114. package/dist/bases-view/summary-core.d.ts +7 -0
  115. package/dist/bases-view/summary-core.js +17 -0
  116. package/dist/bases-view/table-view/components/div-table.svelte +464 -0
  117. package/dist/bases-view/table-view/components/div-table.svelte.d.ts +32 -0
  118. package/dist/bases-view/table-view/components/dnd-style.d.ts +3 -0
  119. package/dist/bases-view/table-view/components/dnd-style.js +6 -0
  120. package/dist/bases-view/table-view/components/sort-header.svelte +48 -0
  121. package/dist/bases-view/table-view/components/sort-header.svelte.d.ts +11 -0
  122. package/dist/bases-view/table-view/components/table-column-tracks.d.ts +14 -0
  123. package/dist/bases-view/table-view/components/table-column-tracks.js +27 -0
  124. package/dist/bases-view/table-view/components/table-header.svelte +146 -0
  125. package/dist/bases-view/table-view/components/table-header.svelte.d.ts +10 -0
  126. package/dist/bases-view/table-view/components/table-placeholder.svelte +46 -0
  127. package/dist/bases-view/table-view/components/table-placeholder.svelte.d.ts +30 -0
  128. package/dist/bases-view/table-view/components/table-virtualizer-core.d.ts +13 -0
  129. package/dist/bases-view/table-view/components/table-virtualizer-core.js +12 -0
  130. package/dist/bases-view/table-view/components/table.svelte +997 -0
  131. package/dist/bases-view/table-view/components/table.svelte.d.ts +32 -0
  132. package/dist/bases-view/table-view/index.svelte.d.ts +10 -0
  133. package/dist/bases-view/table-view/index.svelte.js +30 -0
  134. package/dist/bases-view/unknown-view/index.d.ts +10 -0
  135. package/dist/bases-view/unknown-view/index.js +19 -0
  136. package/dist/bases-view/unknown-view/view.svelte +11 -0
  137. package/dist/bases-view/unknown-view/view.svelte.d.ts +6 -0
  138. package/dist/bases-view/view-header.svelte +142 -0
  139. package/dist/bases-view/view-header.svelte.d.ts +28 -0
  140. package/dist/bases-view/view-registration-inventory.d.ts +22 -0
  141. package/dist/bases-view/view-registration-inventory.js +7 -0
  142. package/dist/bases-view/view-settings.svelte +350 -0
  143. package/dist/bases-view/view-settings.svelte.d.ts +9 -0
  144. package/dist/bases-view/view.svelte +192 -0
  145. package/dist/bases-view/view.svelte.d.ts +15 -0
  146. package/dist/bases-view-surface.svelte +31 -0
  147. package/dist/bases-view-surface.svelte.d.ts +14 -0
  148. package/dist/index.d.ts +10 -0
  149. package/dist/index.js +8 -0
  150. package/dist/serialized-write-queue.d.ts +10 -0
  151. package/dist/serialized-write-queue.js +36 -0
  152. package/dist/styles.css +2164 -0
  153. package/manifest.json +33 -0
  154. package/package.json +90 -0
  155. package/styles.css +1 -0
@@ -0,0 +1,230 @@
1
+ <script lang="ts">
2
+ import { keywordCompletionSource, sql } from "@codemirror/lang-sql";
3
+ import { syntaxHighlighting } from "@codemirror/language";
4
+ import {
5
+ CompletionContext,
6
+ type Completion,
7
+ type CompletionResult,
8
+ } from "@codemirror/autocomplete";
9
+ import { Compartment, EditorState } from "@codemirror/state";
10
+ import {
11
+ EditorView,
12
+ ViewUpdate,
13
+ keymap,
14
+ placeholder as placeHolder,
15
+ tooltips,
16
+ } from "@codemirror/view";
17
+ import {
18
+ cn,
19
+ lapisCodeMirrorAutocomplete,
20
+ setIcon,
21
+ type BasesPropertyId,
22
+ } from "@lapis-notes/api";
23
+ import { classHighlighter } from "@lapis-notes/api/editor/extensions/class-highlighter";
24
+ import { debounce } from "lodash-es";
25
+ import type { HTMLAttributes } from "svelte/elements";
26
+ import { basesDialect } from "../db";
27
+ import { filterLabels } from "../models";
28
+ import { isOp } from "../filter-parser";
29
+ import type { QueryController } from "../bases.svelte";
30
+
31
+ let {
32
+ content = $bindable(""),
33
+ onDocChange,
34
+ onBlur,
35
+ class: className,
36
+ placeholder,
37
+ controller,
38
+ invalid = false,
39
+ describedBy,
40
+ ariaLabel = "Filter formula",
41
+ ...rest
42
+ }: HTMLAttributes<HTMLDivElement> & {
43
+ content: string;
44
+ onBlur?: (value: string) => void;
45
+ onDocChange?: (value: string) => void;
46
+ placeholder?: string;
47
+ controller: QueryController;
48
+ invalid?: boolean;
49
+ describedBy?: string;
50
+ ariaLabel?: string;
51
+ } = $props();
52
+
53
+ let editorView!: EditorView;
54
+
55
+ let reportChanges = debounce((value: string) => {
56
+ if (onDocChange) {
57
+ onDocChange(value);
58
+ }
59
+ }, 500);
60
+
61
+ let dialect = $derived(basesDialect(controller));
62
+ let completionIcons = $derived.by(() => {
63
+ return controller
64
+ .getAllColumns()
65
+ .reduce<Record<string, string>>((acc, value) => {
66
+ acc[value.id] = value.icon ?? "lucide-info";
67
+ if (value.id.startsWith("note.")) {
68
+ acc[value.id] = acc[value.id];
69
+ }
70
+ return acc;
71
+ }, {});
72
+ });
73
+
74
+ function completeFunctions(
75
+ context: CompletionContext,
76
+ ): CompletionResult | null {
77
+ let word = context.matchBefore(/[\w$]+(\.[\w$]+(\([^)]*\))?)*\.?/);
78
+ if (!word || (word.from == word.to && !context.explicit)) return null;
79
+ const parts = word.text.split(".");
80
+ if (parts.length >= 2) {
81
+ const base = parts
82
+ .slice(0, parts.length - 1)
83
+ .join(".") as BasesPropertyId;
84
+ const after = parts.length > 2 ? parts[parts.length - 1] : "";
85
+ const type = completionIcons[base]
86
+ ? (controller.getColumn(base)?.type ?? "unknown")
87
+ : "unknown";
88
+ if (filterLabels[type]) {
89
+ const completions: Completion[] = filterLabels[type]
90
+ .filter((it) => !it.value.startsWith("!") && !isOp(it.value))
91
+ .map((value) => {
92
+ return {
93
+ label: `${value.value}()`,
94
+ type: "function",
95
+ };
96
+ });
97
+ return {
98
+ from: word.to - after.length,
99
+ options: [...completions, { label: `toString()`, type: "function" }],
100
+ };
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+
106
+ type EditorInput = {
107
+ content: string;
108
+ invalid: boolean;
109
+ describedBy?: string;
110
+ ariaLabel: string;
111
+ };
112
+
113
+ function editorContentAttributes(input: EditorInput) {
114
+ return {
115
+ "aria-label": input.ariaLabel,
116
+ "aria-invalid": String(input.invalid),
117
+ ...(input.describedBy
118
+ ? { "aria-describedby": input.describedBy }
119
+ : {}),
120
+ };
121
+ }
122
+
123
+ function codeMirror(el: HTMLElement, input: EditorInput) {
124
+ const contentAttributes = new Compartment();
125
+ let previousContent = input.content;
126
+ editorView = new EditorView({
127
+ state: EditorState.create({
128
+ doc: input.content,
129
+ extensions: [
130
+ sql({
131
+ dialect: dialect,
132
+ }),
133
+ ...lapisCodeMirrorAutocomplete({
134
+ override: [keywordCompletionSource(dialect), completeFunctions],
135
+ addToOptions: [
136
+ {
137
+ render(completion) {
138
+ let icon = completionIcons[completion.label];
139
+ if (completion.type === "function") {
140
+ icon = "lucide-square-function";
141
+ }
142
+ if (icon) {
143
+ const span = createSpan(
144
+ "suggestion-flair [&_svg:not([class*='text-'])]:text-muted-foreground text-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
145
+ );
146
+ setIcon(span, icon);
147
+ return span;
148
+ }
149
+ return null;
150
+ },
151
+ position: 20,
152
+ },
153
+ ],
154
+ }),
155
+ syntaxHighlighting(classHighlighter),
156
+ tooltips({
157
+ parent: document.body,
158
+ }),
159
+ EditorView.domEventHandlers({
160
+ blur: () => {
161
+ onBlur?.(editorView.state.doc.toString());
162
+ },
163
+ }),
164
+ keymap.of([
165
+ {
166
+ key: "Enter",
167
+ run: (view) => {
168
+ // Insert newline and prevent event from bubbling to parent
169
+ view.dispatch({
170
+ changes: {
171
+ from: view.state.selection.main.head,
172
+ insert: "\n",
173
+ },
174
+ selection: { anchor: view.state.selection.main.head + 1 },
175
+ });
176
+ return true; // This prevents the event from propagating
177
+ },
178
+ },
179
+ ]),
180
+ placeholder ? placeHolder(placeholder) : [],
181
+ EditorView.editorAttributes.of({ class: "mod-inline" }),
182
+ contentAttributes.of(
183
+ EditorView.contentAttributes.of(editorContentAttributes(input)),
184
+ ),
185
+ EditorView.updateListener.of((v: ViewUpdate) => {
186
+ if (v.docChanged) {
187
+ const next = v.state.doc.toString();
188
+ reportChanges(next);
189
+ }
190
+ }),
191
+ ],
192
+ }),
193
+ parent: el,
194
+ });
195
+ return {
196
+ update: (next: EditorInput) => {
197
+ const current = editorView.state.doc.toString();
198
+ if (next.content !== previousContent && current !== next.content) {
199
+ editorView.dispatch({
200
+ changes: [
201
+ {
202
+ from: 0,
203
+ to: editorView.state.doc.length,
204
+ insert: next.content,
205
+ },
206
+ ],
207
+ });
208
+ }
209
+ previousContent = next.content;
210
+ editorView.dispatch({
211
+ effects: contentAttributes.reconfigure(
212
+ EditorView.contentAttributes.of(editorContentAttributes(next)),
213
+ ),
214
+ });
215
+ },
216
+ destroy: () => {
217
+ reportChanges.cancel();
218
+ editorView.destroy();
219
+ },
220
+ };
221
+ }
222
+ </script>
223
+
224
+ <div
225
+ {...rest}
226
+ use:codeMirror={{ content, invalid, describedBy, ariaLabel }}
227
+ data-ui-component="bases-query-editor"
228
+ data-invalid={invalid}
229
+ class={cn("bases-style-h-full-668b21", className)}
230
+ ></div>
@@ -0,0 +1,15 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import type { QueryController } from "../bases.svelte";
3
+ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
4
+ content: string;
5
+ onBlur?: (value: string) => void;
6
+ onDocChange?: (value: string) => void;
7
+ placeholder?: string;
8
+ controller: QueryController;
9
+ invalid?: boolean;
10
+ describedBy?: string;
11
+ ariaLabel?: string;
12
+ };
13
+ declare const QueryEditor: import("svelte").Component<$$ComponentProps, {}, "content">;
14
+ type QueryEditor = ReturnType<typeof QueryEditor>;
15
+ export default QueryEditor;
@@ -0,0 +1,9 @@
1
+ import type { MetadataType } from "@lapis-notes/api";
2
+ export declare const sortDirectionLabels: Partial<Record<MetadataType, Array<{
3
+ label: string;
4
+ value: "ASC" | "DESC";
5
+ }>>>;
6
+ export declare function getSortDirectionLabels(type: MetadataType | undefined): {
7
+ label: string;
8
+ value: "ASC" | "DESC";
9
+ }[];
@@ -0,0 +1,25 @@
1
+ export const sortDirectionLabels = {
2
+ unknown: [
3
+ { label: "A → Z", value: "ASC" },
4
+ { label: "Z → A", value: "DESC" },
5
+ ],
6
+ text: [
7
+ { label: "A → Z", value: "ASC" },
8
+ { label: "Z → A", value: "DESC" },
9
+ ],
10
+ date: [
11
+ { label: "Old to new", value: "ASC" },
12
+ { label: "New to old", value: "DESC" },
13
+ ],
14
+ datetime: [
15
+ { label: "Old to new", value: "ASC" },
16
+ { label: "New to old", value: "DESC" },
17
+ ],
18
+ number: [
19
+ { label: "0 → 1", value: "ASC" },
20
+ { label: "1 → 0", value: "DESC" },
21
+ ],
22
+ };
23
+ export function getSortDirectionLabels(type) {
24
+ return (sortDirectionLabels[type ?? "unknown"] ?? sortDirectionLabels.unknown ?? []);
25
+ }
@@ -0,0 +1,239 @@
1
+ <script lang="ts">
2
+ import { DragDropProvider } from "@dnd-kit/svelte";
3
+ import type { DragEndEvent } from "@dnd-kit/dom";
4
+ import { arrayMove } from "@dnd-kit/helpers";
5
+ import * as Popover from "@lapismd/design-core/shadcn/popover";
6
+ import * as Command from "@lapismd/design-core/shadcn/command";
7
+ import * as Select from "@lapismd/design-core/shadcn/select";
8
+ import SortIcon from "@lucide/svelte/icons/arrow-up-down";
9
+ import { ScrollArea } from "@lapismd/design-core/shadcn/scroll-area";
10
+ import type { Table } from "@tanstack/table-core";
11
+ import { Button } from "@lapismd/design-core/shadcn/button";
12
+ import type { SortColumn } from "../..";
13
+ import SortRow from "./sort-row.svelte";
14
+ import PlusIcon from "@lucide/svelte/icons/plus";
15
+ import { cn } from "@lapis-notes/api";
16
+ import type { QueryController } from "../../bases.svelte";
17
+ import CheckIcon from "@lucide/svelte/icons/check";
18
+ import Trash from "@lucide/svelte/icons/trash-2";
19
+ import { Icon } from "@lapis-notes/api/icon";
20
+ import { getSortDirectionLabels } from "./sort-direction-labels";
21
+
22
+ let {
23
+ class: className,
24
+ orderBy = $bindable(),
25
+ table,
26
+ controller = table as QueryController,
27
+ }: {
28
+ orderBy: Array<SortColumn>;
29
+ class?: string;
30
+ controller?: QueryController;
31
+ table?: any;
32
+ } = $props();
33
+
34
+ let empty: SortColumn = $state({ property: "", direction: "ASC" });
35
+ let groupBy = $derived(controller.selectedView.groupBy);
36
+ let selectedGroupColumn = $derived.by(() => {
37
+ return groupBy?.property
38
+ ? controller.getColumn(groupBy.property)
39
+ : undefined;
40
+ });
41
+ let groupDirections = $derived.by(() => {
42
+ return getSortDirectionLabels(selectedGroupColumn?.type);
43
+ });
44
+ let groupDirectionLabel = $derived(
45
+ groupDirections.find((direction) => direction.value === getGroupDirection())
46
+ ?.label ?? "",
47
+ );
48
+
49
+ $effect(() => {
50
+ if (empty.property && !orderBy.length) {
51
+ orderBy.push($state.snapshot(empty));
52
+ empty = { property: "", direction: "ASC" };
53
+ }
54
+ });
55
+
56
+ function handleDragEnd(event: DragEndEvent) {
57
+ const source = event.operation.source?.data;
58
+ const target = event.operation.target?.data;
59
+
60
+ if (
61
+ source?.type !== "bases-sort-row" ||
62
+ target?.type !== "bases-sort-row" ||
63
+ source.index === undefined ||
64
+ target.index === undefined ||
65
+ source.index === target.index
66
+ ) {
67
+ return;
68
+ }
69
+
70
+ orderBy = arrayMove(orderBy, source.index, target.index);
71
+ }
72
+
73
+ function deleteSort(index: number) {
74
+ orderBy.splice(index, 1);
75
+ }
76
+
77
+ function addSort() {
78
+ orderBy.push({ property: "", direction: "ASC" });
79
+ }
80
+
81
+ function setGroupBy(property: string) {
82
+ controller.selectedView.groupBy = {
83
+ property,
84
+ direction: controller.selectedView.groupBy?.direction ?? "ASC",
85
+ };
86
+ }
87
+
88
+ function getGroupDirection() {
89
+ return controller.selectedView.groupBy?.direction ?? "ASC";
90
+ }
91
+
92
+ function setGroupDirection(direction: string) {
93
+ if (!controller.selectedView.groupBy) return;
94
+ controller.selectedView.groupBy = {
95
+ ...controller.selectedView.groupBy,
96
+ direction: direction === "DESC" ? "DESC" : "ASC",
97
+ };
98
+ }
99
+ </script>
100
+
101
+ <Popover.Root>
102
+ <Popover.Trigger>
103
+ {#snippet child({ props }: { props: Record<string, any> })}
104
+ <Button
105
+ {...props}
106
+ variant="outline"
107
+ size="sm"
108
+ class={cn("bases-style-h-8-ed8a5d bases-style-border-dashed-a29b7a", className)}
109
+ >
110
+ <SortIcon /> Sort
111
+ </Button>
112
+ {/snippet}
113
+ </Popover.Trigger>
114
+ <Popover.Content
115
+ class="bases-query-popover bases-sort-popover bases-style-bg-sidebar-d3aed6 bases-style-mr-2-d2347e bases-style-max-h-70svh-d6926c bases-style-overflow-y-auto-92bf82 bases-style-p-2-7660b4"
116
+ data-bases-popover="sort"
117
+ align="start"
118
+ >
119
+ <div class="bases-style-mb-2-a77ed4 bases-style-grid-f3c543 bases-style-gap-2-77a2a2">
120
+ <div
121
+ class="bases-style-text-muted-foreground-bfa603 bases-style-px-1-d8e0e3 bases-style-text-xs-359090 bases-style-font-semibold-e83a70 bases-style-tracking-0-18em-a82f6f uppercase"
122
+ >
123
+ Group by
124
+ </div>
125
+ <div class="bases-style-flex-60fbb7 bases-style-h-9-e7a768 bases-style-items-center-3960ff bases-style-rounded-md-421ac2 bases-style-border-2-65935d">
126
+ <Popover.Root>
127
+ <Popover.Trigger>
128
+ {#snippet child({ props }: { props: Record<string, any> })}
129
+ <Button
130
+ {...props}
131
+ variant="outline"
132
+ size="sm"
133
+ class="bases-style-h-8-ed8a5d grow 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"
134
+ >
135
+ {#if selectedGroupColumn}
136
+ <Icon name={[selectedGroupColumn.icon ?? "lucide-file"]} />
137
+ {selectedGroupColumn.displayName}
138
+ {:else}
139
+ <span class="bases-style-opacity-50-0b8c50">Property</span>
140
+ {/if}
141
+ <SortIcon />
142
+ </Button>
143
+ {/snippet}
144
+ </Popover.Trigger>
145
+ <Popover.Content class="bases-style-w-max-2f935d bases-style-p-0-8a539c" align="start">
146
+ <Command.Root>
147
+ <Command.Input placeholder="Group by property" />
148
+ <Command.List class="bases-style-p-1-eb6a3c">
149
+ <Command.Empty>No results found.</Command.Empty>
150
+ {#each controller.getAllColumns() as column (column.id)}
151
+ <Command.Item onSelect={() => setGroupBy(column.id)}>
152
+ <div
153
+ data-ui-part="bases-option-indicator"
154
+ data-selected={groupBy?.property === column.id}
155
+ class={cn(
156
+ "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",
157
+ groupBy?.property === column.id
158
+ ? "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"
159
+ : "bases-style-opacity-50-0b8c50 [&_svg]:invisible",
160
+ )}
161
+ >
162
+ <CheckIcon class="bases-style-size-4-f7b5fa" />
163
+ </div>
164
+ <Icon name={[column.icon ?? "lucide-info"]} />
165
+ <span class="bases-style-overflow-hidden-2cd02d bases-style-text-ellipsis-6e2e11"
166
+ >{column.displayName}</span
167
+ >
168
+ {#if !column.id.startsWith("note.")}
169
+ <Command.Shortcut class="bases-style-font-mono-0e6570 bases-style-text-0-75em-c425cc"
170
+ >{column.id}</Command.Shortcut
171
+ >
172
+ {/if}
173
+ </Command.Item>
174
+ {/each}
175
+ </Command.List>
176
+ </Command.Root>
177
+ </Popover.Content>
178
+ </Popover.Root>
179
+ <Select.Root
180
+ type="single"
181
+ bind:value={getGroupDirection, setGroupDirection}
182
+ >
183
+ <Select.Trigger
184
+ size="sm"
185
+ class="bases-style-bg-background-e6f9e3 bases-style-h-8-ed8a5d bases-style-rounded-none-0c5e91 bases-style-border-none-4a5f0e bases-style-shadow-none-ad47d1 bases-style-outline-none-df37b1"
186
+ >
187
+ {groupDirectionLabel}
188
+ <SortIcon />
189
+ </Select.Trigger>
190
+ <Select.Content>
191
+ {#each groupDirections as direction (direction.value)}
192
+ <Select.Item value={direction.value} label={direction.label}
193
+ >{direction.label}</Select.Item
194
+ >
195
+ {/each}
196
+ </Select.Content>
197
+ </Select.Root>
198
+ <Button
199
+ variant="ghost"
200
+ size="sm"
201
+ class="bases-style-bg-background-e6f9e3 bases-style-h-8-ed8a5d 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"
202
+ disabled={!selectedGroupColumn}
203
+ onclick={() => (controller.selectedView.groupBy = undefined)}
204
+ >
205
+ <Trash />
206
+ </Button>
207
+ </div>
208
+ </div>
209
+ <div class="bases-style-mb-2-a77ed4 bases-style-border-t-b950dd"></div>
210
+ <div
211
+ class="bases-style-text-muted-foreground-bfa603 bases-style-mb-2-a77ed4 bases-style-px-1-d8e0e3 bases-style-text-xs-359090 bases-style-font-semibold-e83a70 bases-style-tracking-0-18em-a82f6f uppercase"
212
+ >
213
+ Sort by
214
+ </div>
215
+ <DragDropProvider onDragEnd={handleDragEnd}>
216
+ <ScrollArea
217
+ orientation="both"
218
+ class="bases-style-h-full-668b21 bases-style-w-full-6da6a3 bases-style-data-scroll-area-viewport-max-h-55svh-15dd46"
219
+ >
220
+ {#if !orderBy.length}
221
+ <SortRow {controller} bind:sort={empty} deleteSort={() => {}} />
222
+ {/if}
223
+ {#each orderBy as order, i}
224
+ <SortRow
225
+ {controller}
226
+ index={i}
227
+ bind:sort={orderBy[i]}
228
+ deleteSort={() => deleteSort(i)}
229
+ />
230
+ {/each}
231
+ </ScrollArea>
232
+ </DragDropProvider>
233
+ <div class="filter-group-actions bases-style-mt-2-50d0d2">
234
+ <Button size="sm" variant="ghost" onclick={addSort}>
235
+ <PlusIcon /> Add sort
236
+ </Button>
237
+ </div>
238
+ </Popover.Content>
239
+ </Popover.Root>
@@ -0,0 +1,11 @@
1
+ import type { SortColumn } from "../..";
2
+ import type { QueryController } from "../../bases.svelte";
3
+ type $$ComponentProps = {
4
+ orderBy: Array<SortColumn>;
5
+ class?: string;
6
+ controller?: QueryController;
7
+ table?: any;
8
+ };
9
+ declare const SortRoot: import("svelte").Component<$$ComponentProps, {}, "orderBy">;
10
+ type SortRoot = ReturnType<typeof SortRoot>;
11
+ export default SortRoot;