@lobb-js/studio 0.32.0 → 0.34.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/dist/actions.d.ts +4 -0
- package/dist/applyUITheme.d.ts +2 -0
- package/dist/applyUITheme.js +36 -0
- package/dist/components/LlmButton.svelte +4 -2
- package/dist/components/LlmButton.svelte.d.ts +1 -0
- package/dist/components/Studio.svelte +15 -5
- package/dist/components/canAccess.svelte +52 -0
- package/dist/components/canAccess.svelte.d.ts +10 -0
- package/dist/components/createManyButton.svelte +2 -2
- package/dist/components/dataTable/dataTable.svelte +47 -28
- package/dist/components/dataTable/dataTable.svelte.d.ts +4 -0
- package/dist/components/dataTable/dataTableTabs.svelte +1 -1
- package/dist/components/dataTable/filter.svelte +3 -2
- package/dist/components/dataTable/filterButton.svelte +1 -1
- package/dist/components/dataTable/footer.svelte +1 -1
- package/dist/components/dataTable/header.svelte +20 -26
- package/dist/components/dataTable/header.svelte.d.ts +0 -1
- package/dist/components/dataTable/listViewChildren.svelte +1 -1
- package/dist/components/dataTable/sort.svelte +1 -1
- package/dist/components/dataTable/sortButton.svelte +1 -1
- package/dist/components/dataTable/table.svelte +4 -4
- package/dist/components/dataTablePopup/dataTablePopup.svelte +4 -1
- package/dist/components/dataTablePopup/dataTablePopup.svelte.d.ts +4 -0
- package/dist/components/detailView/create/createDetailView.svelte +2 -2
- package/dist/components/detailView/create/createDetailViewButton.svelte +2 -0
- package/dist/components/detailView/create/createDetailViewButton.svelte.d.ts +1 -0
- package/dist/components/detailView/create/createDetailViewChildren.svelte +3 -3
- package/dist/components/detailView/create/createManyView.svelte +2 -2
- package/dist/components/detailView/update/updateDetailView.svelte +2 -2
- package/dist/components/detailView/update/updateDetailViewButton.svelte +2 -0
- package/dist/components/detailView/update/updateDetailViewButton.svelte.d.ts +1 -0
- package/dist/components/detailView/update/updateDetailViewChildren.svelte +3 -3
- package/dist/components/drawer.svelte +2 -2
- package/dist/components/horizontalNav.svelte +85 -0
- package/dist/components/horizontalNav.svelte.d.ts +3 -0
- package/dist/components/importButton.svelte +6 -6
- package/dist/components/mainNav.svelte +15 -0
- package/dist/components/mainNav.svelte.d.ts +6 -0
- package/dist/components/mainNavShared.d.ts +10 -0
- package/dist/components/mainNavShared.js +62 -0
- package/dist/components/rangeCalendarButton.svelte +1 -2
- package/dist/components/routes/home.svelte +1 -1
- package/dist/components/routes/workflows/workflows.svelte +1 -1
- package/dist/components/setServerPage.svelte +1 -1
- package/dist/components/sidebar/sidebar.svelte +2 -2
- package/dist/components/sidebar/sidebarElements.svelte +3 -3
- package/dist/components/singletone.svelte +2 -2
- package/dist/components/ui/skeleton/skeleton.svelte +1 -1
- package/dist/components/ui/tooltip/tooltip-content.svelte +1 -1
- package/dist/components/verticalNav.svelte +174 -0
- package/dist/components/verticalNav.svelte.d.ts +3 -0
- package/dist/components/workflowEditor.svelte +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/store.types.d.ts +8 -0
- package/package.json +2 -2
- package/src/app.css +52 -75
- package/src/lib/actions.ts +1 -0
- package/src/lib/applyUITheme.ts +38 -0
- package/src/lib/components/LlmButton.svelte +4 -2
- package/src/lib/components/Studio.svelte +15 -5
- package/src/lib/components/canAccess.svelte +52 -0
- package/src/lib/components/createManyButton.svelte +2 -2
- package/src/lib/components/dataTable/dataTable.svelte +47 -28
- package/src/lib/components/dataTable/dataTableTabs.svelte +1 -1
- package/src/lib/components/dataTable/filter.svelte +3 -2
- package/src/lib/components/dataTable/filterButton.svelte +1 -1
- package/src/lib/components/dataTable/footer.svelte +1 -1
- package/src/lib/components/dataTable/header.svelte +20 -26
- package/src/lib/components/dataTable/listViewChildren.svelte +1 -1
- package/src/lib/components/dataTable/sort.svelte +1 -1
- package/src/lib/components/dataTable/sortButton.svelte +1 -1
- package/src/lib/components/dataTable/table.svelte +4 -4
- package/src/lib/components/dataTablePopup/dataTablePopup.svelte +4 -1
- package/src/lib/components/detailView/create/createDetailView.svelte +2 -2
- package/src/lib/components/detailView/create/createDetailViewButton.svelte +2 -0
- package/src/lib/components/detailView/create/createDetailViewChildren.svelte +3 -3
- package/src/lib/components/detailView/create/createManyView.svelte +2 -2
- package/src/lib/components/detailView/update/updateDetailView.svelte +2 -2
- package/src/lib/components/detailView/update/updateDetailViewButton.svelte +2 -0
- package/src/lib/components/detailView/update/updateDetailViewChildren.svelte +3 -3
- package/src/lib/components/drawer.svelte +2 -2
- package/src/lib/components/horizontalNav.svelte +85 -0
- package/src/lib/components/importButton.svelte +6 -6
- package/src/lib/components/mainNav.svelte +15 -0
- package/src/lib/components/mainNavShared.ts +67 -0
- package/src/lib/components/rangeCalendarButton.svelte +1 -2
- package/src/lib/components/routes/home.svelte +1 -1
- package/src/lib/components/routes/workflows/workflows.svelte +1 -1
- package/src/lib/components/setServerPage.svelte +1 -1
- package/src/lib/components/sidebar/sidebar.svelte +2 -2
- package/src/lib/components/sidebar/sidebarElements.svelte +3 -3
- package/src/lib/components/singletone.svelte +2 -2
- package/src/lib/components/ui/skeleton/skeleton.svelte +1 -1
- package/src/lib/components/ui/tooltip/tooltip-content.svelte +1 -1
- package/src/lib/components/verticalNav.svelte +174 -0
- package/src/lib/components/workflowEditor.svelte +2 -2
- package/src/lib/index.ts +2 -0
- package/src/lib/store.types.ts +6 -0
- package/dist/components/miniSidebar.svelte +0 -300
- package/dist/components/miniSidebar.svelte.d.ts +0 -5
- package/src/lib/components/miniSidebar.svelte +0 -300
package/dist/actions.d.ts
CHANGED
|
@@ -20,6 +20,10 @@ export interface OpenDataTablePopupProps {
|
|
|
20
20
|
showHeader?: boolean;
|
|
21
21
|
showFooter?: boolean;
|
|
22
22
|
tabs?: CollectionTab[];
|
|
23
|
+
view?: {
|
|
24
|
+
id: string;
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
};
|
|
23
27
|
}
|
|
24
28
|
export declare function showDialog(title: string, description: string): Promise<boolean>;
|
|
25
29
|
export declare function openCreateDetailView(studioContext: StudioContext, props: CreateDetailViewProp): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// UI theme injection. Writes the configured CSS-variable overrides into
|
|
2
|
+
// a single <style> tag — light overrides under `:root`, dark under
|
|
3
|
+
// `.dark` — so each mode picks up its own variant. Idempotent: re-runs
|
|
4
|
+
// replace the previous block.
|
|
5
|
+
const STYLE_ID = "lobb-ui-theme";
|
|
6
|
+
function buildDeclarations(vars) {
|
|
7
|
+
if (!vars)
|
|
8
|
+
return "";
|
|
9
|
+
const out = [];
|
|
10
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
11
|
+
if (!key.startsWith("--") || !value)
|
|
12
|
+
continue;
|
|
13
|
+
out.push(`${key}: ${value};`);
|
|
14
|
+
}
|
|
15
|
+
return out.join(" ");
|
|
16
|
+
}
|
|
17
|
+
export function applyUITheme(theme) {
|
|
18
|
+
if (typeof document === "undefined")
|
|
19
|
+
return;
|
|
20
|
+
document.getElementById(STYLE_ID)?.remove();
|
|
21
|
+
if (!theme)
|
|
22
|
+
return;
|
|
23
|
+
const light = buildDeclarations(theme.light);
|
|
24
|
+
const dark = buildDeclarations(theme.dark);
|
|
25
|
+
if (!light && !dark)
|
|
26
|
+
return;
|
|
27
|
+
const rules = [];
|
|
28
|
+
if (light)
|
|
29
|
+
rules.push(`:root { ${light} }`);
|
|
30
|
+
if (dark)
|
|
31
|
+
rules.push(`.dark { ${dark} }`);
|
|
32
|
+
const style = document.createElement("style");
|
|
33
|
+
style.id = STYLE_ID;
|
|
34
|
+
style.textContent = rules.join(" ");
|
|
35
|
+
document.head.appendChild(style);
|
|
36
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
format?: any;
|
|
17
17
|
messages?: any[];
|
|
18
18
|
variant?: ButtonProps["variant"];
|
|
19
|
+
size?: ButtonProps["size"];
|
|
19
20
|
class?: ButtonProps["class"];
|
|
20
21
|
Icon?: ButtonProps["Icon"];
|
|
21
22
|
children?: ButtonProps["children"];
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
description,
|
|
29
30
|
placeholder = "write prompt description",
|
|
30
31
|
variant = "default",
|
|
32
|
+
size,
|
|
31
33
|
Icon = Brain,
|
|
32
34
|
onApiResponseComplete,
|
|
33
35
|
messages,
|
|
@@ -98,7 +100,7 @@
|
|
|
98
100
|
{#if ctx.meta.extensions.llm && ctx.meta.collections.llm_chat}
|
|
99
101
|
<Popover.Root bind:open={popoverOpen}>
|
|
100
102
|
<Popover.Trigger>
|
|
101
|
-
<Button {variant} class={props.class}>
|
|
103
|
+
<Button {variant} {size} class={props.class}>
|
|
102
104
|
{#if loading}
|
|
103
105
|
<LoaderIcon class="animate-spin" />
|
|
104
106
|
{:else}
|
|
@@ -129,7 +131,7 @@
|
|
|
129
131
|
<Button
|
|
130
132
|
type="submit"
|
|
131
133
|
Icon={Send}
|
|
132
|
-
|
|
134
|
+
size="sm">Submit</Button
|
|
133
135
|
>
|
|
134
136
|
</form>
|
|
135
137
|
</Popover.Content>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { createLobb } from "../store.svelte";
|
|
6
6
|
import { setStudioContext } from "../context";
|
|
7
7
|
import { LoaderCircle, ServerOff } from "lucide-svelte";
|
|
8
|
-
import
|
|
8
|
+
import MainNav from "./mainNav.svelte";
|
|
9
9
|
import * as Tooltip from "./ui/tooltip";
|
|
10
10
|
import { page } from "$app/state";
|
|
11
11
|
import { afterNavigate } from "$app/navigation";
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
} from "../extensions/extensionUtils";
|
|
18
18
|
import extensionMap from 'virtual:lobb-studio-extensions';
|
|
19
19
|
import { mediaQueries } from "../utils";
|
|
20
|
+
import { applyUITheme } from "../applyUITheme";
|
|
20
21
|
import Home from "./routes/home.svelte";
|
|
21
22
|
import DataModel from "./routes/data_model/dataModel.svelte";
|
|
22
23
|
import Collections from "./routes/collections/collections.svelte";
|
|
@@ -43,6 +44,12 @@
|
|
|
43
44
|
|
|
44
45
|
let status: "loading" | "error" | "ready" = $state("loading");
|
|
45
46
|
let isSmallScreen = $derived(!mediaQueries.sm.current);
|
|
47
|
+
// Horizontal nav forces the layout to row mode (top bar instead of
|
|
48
|
+
// left rail). Falls back to vertical when small-screen mode is on,
|
|
49
|
+
// since there's no useful horizontal layout below 640px.
|
|
50
|
+
const horizontalNav = $derived(
|
|
51
|
+
(ctx.meta as any)?.ui?.horizontalNav === true && !isSmallScreen,
|
|
52
|
+
);
|
|
46
53
|
|
|
47
54
|
onMount(async () => {
|
|
48
55
|
// Remove the static loading screen defined in app.html — it shows instantly
|
|
@@ -50,6 +57,7 @@
|
|
|
50
57
|
document.getElementById("app-loading")?.remove();
|
|
51
58
|
try {
|
|
52
59
|
ctx.meta = await lobb.getMeta();
|
|
60
|
+
applyUITheme(ctx.meta.ui?.theme);
|
|
53
61
|
ctx.extensions = await loadExtensions(lobb, ctx, extensionMap);
|
|
54
62
|
await executeExtensionsOnStartup(lobb, ctx);
|
|
55
63
|
loadExtensionWorkflows(ctx as any);
|
|
@@ -102,11 +110,13 @@
|
|
|
102
110
|
{:else}
|
|
103
111
|
<Tooltip.Provider delayDuration={0} disableHoverableContent={true}>
|
|
104
112
|
<main
|
|
105
|
-
class="bg-muted h-screen w-screen"
|
|
106
|
-
style=
|
|
113
|
+
class="bg-muted h-screen w-screen overflow-hidden"
|
|
114
|
+
style={horizontalNav
|
|
115
|
+
? "display: grid; grid-template-rows: 3rem 1fr;"
|
|
116
|
+
: `display: grid; grid-template-columns: ${isSmallScreen ? '1fr' : '3.5rem 1fr'};`}
|
|
107
117
|
>
|
|
108
|
-
<
|
|
109
|
-
<div class="min-h-0 h-
|
|
118
|
+
<MainNav orientation={horizontalNav ? "horizontal" : "vertical"} />
|
|
119
|
+
<div class="min-h-0 h-full overflow-hidden">
|
|
110
120
|
{#if page.url.pathname.replace(/\/$/, "") === "/studio"}
|
|
111
121
|
<Home />
|
|
112
122
|
{:else if page.url.pathname.startsWith("/studio/collections")}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Declarative permission gate. Replaces the recurring pattern of
|
|
3
|
+
// `let canX = $state(false); onMount(async () => canX = await emitEvent
|
|
4
|
+
// ("auth.canAccess", { collection, action }) === true);` plus an `{#if canX}`.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// <CanAccess collection="risks" action="update">
|
|
8
|
+
// <EditButton ... />
|
|
9
|
+
// </CanAccess>
|
|
10
|
+
//
|
|
11
|
+
// Optional `fallback` snippet renders when the user is NOT allowed
|
|
12
|
+
// (defaults to nothing). The brief in-flight window before the answer
|
|
13
|
+
// is known renders nothing so we don't flash unauthorized content.
|
|
14
|
+
import type { Snippet } from "svelte";
|
|
15
|
+
import { onMount } from "svelte";
|
|
16
|
+
import { getStudioContext } from "../context";
|
|
17
|
+
import { emitEvent } from "../eventSystem";
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
collection: string;
|
|
21
|
+
action: "read" | "create" | "update" | "delete" | string;
|
|
22
|
+
children: Snippet;
|
|
23
|
+
fallback?: Snippet;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { collection, action, children, fallback }: Props = $props();
|
|
27
|
+
const { lobb, ctx } = getStudioContext();
|
|
28
|
+
|
|
29
|
+
// null = "haven't checked yet" — distinguished from false so the fallback
|
|
30
|
+
// doesn't flash during the async resolution.
|
|
31
|
+
let allowed = $state<boolean | null>(null);
|
|
32
|
+
|
|
33
|
+
onMount(async () => {
|
|
34
|
+
try {
|
|
35
|
+
const result = await emitEvent({ lobb, ctx }, "auth.canAccess", {
|
|
36
|
+
collection,
|
|
37
|
+
action,
|
|
38
|
+
});
|
|
39
|
+
allowed = result === true;
|
|
40
|
+
} catch {
|
|
41
|
+
// No handler registered (auth extension not loaded), or the
|
|
42
|
+
// handler threw — fail closed.
|
|
43
|
+
allowed = false;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
{#if allowed === true}
|
|
49
|
+
{@render children()}
|
|
50
|
+
{:else if allowed === false && fallback}
|
|
51
|
+
{@render fallback()}
|
|
52
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface Props {
|
|
3
|
+
collection: string;
|
|
4
|
+
action: "read" | "create" | "update" | "delete" | string;
|
|
5
|
+
children: Snippet;
|
|
6
|
+
fallback?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const CanAccess: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type CanAccess = ReturnType<typeof CanAccess>;
|
|
10
|
+
export default CanAccess;
|
|
@@ -90,14 +90,14 @@
|
|
|
90
90
|
<Button
|
|
91
91
|
variant="outline"
|
|
92
92
|
onclick={() => hideDrawer()}
|
|
93
|
-
|
|
93
|
+
size="sm"
|
|
94
94
|
Icon={X}
|
|
95
95
|
>
|
|
96
96
|
Cancel
|
|
97
97
|
</Button>
|
|
98
98
|
<Button
|
|
99
99
|
variant="default"
|
|
100
|
-
|
|
100
|
+
size="sm"
|
|
101
101
|
Icon={Plus}
|
|
102
102
|
onclick={handleCreateMany}
|
|
103
103
|
>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import Header from "./header.svelte";
|
|
14
14
|
import Table, { type TableProps } from "./table.svelte";
|
|
15
15
|
import { getCollectionColumns, getCollectionParamsFields } from "./utils";
|
|
16
|
+
import CanAccess from "../canAccess.svelte";
|
|
16
17
|
import { Pencil, Trash, Unlink } from "lucide-svelte";
|
|
17
18
|
import ListViewChildren from "./listViewChildren.svelte";
|
|
18
19
|
import FieldCell from "./fieldCell.svelte";
|
|
@@ -24,8 +25,7 @@
|
|
|
24
25
|
import type { ChildrenChanges } from "../detailView/utils";
|
|
25
26
|
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
26
27
|
import { getExtensionUtils, loadExtensionComponents } from "../../extensions/extensionUtils";
|
|
27
|
-
import {
|
|
28
|
-
import { onMount, untrack } from "svelte";
|
|
28
|
+
import { untrack } from "svelte";
|
|
29
29
|
import Tabs from "./dataTableTabs.svelte";
|
|
30
30
|
import { fade } from "svelte/transition";
|
|
31
31
|
import type { CollectionTab } from "../../store.types";
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
tableProps?: Partial<TableProps>;
|
|
49
49
|
tabs?: CollectionTab[];
|
|
50
50
|
headerLeft?: Snippet<[]>;
|
|
51
|
+
view?: { id: string; [key: string]: any };
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
let {
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
tableProps,
|
|
67
68
|
tabs,
|
|
68
69
|
headerLeft,
|
|
70
|
+
view,
|
|
69
71
|
}: Props = $props();
|
|
70
72
|
|
|
71
73
|
const isRecordingMode = onChanges !== undefined;
|
|
@@ -73,19 +75,6 @@
|
|
|
73
75
|
untrack(() => changes) ?? { created: [], updated: [], deleted: [], linked: [], unlinked: [] }
|
|
74
76
|
);
|
|
75
77
|
|
|
76
|
-
// Gate row/header buttons by the current user's permissions:
|
|
77
|
-
// - showUpdate → per-row edit button
|
|
78
|
-
// - showCreate → header's Create + Import buttons (passed to Header)
|
|
79
|
-
let showUpdate = $state(false);
|
|
80
|
-
let showCreate = $state(false);
|
|
81
|
-
onMount(async () => {
|
|
82
|
-
const [update, create] = await Promise.all([
|
|
83
|
-
emitEvent({ lobb, ctx }, "auth.canAccess", { collection: collectionName, action: "update" }),
|
|
84
|
-
emitEvent({ lobb, ctx }, "auth.canAccess", { collection: collectionName, action: "create" }),
|
|
85
|
-
]);
|
|
86
|
-
showUpdate = update === true;
|
|
87
|
-
showCreate = create === true;
|
|
88
|
-
});
|
|
89
78
|
|
|
90
79
|
// Derives the displayed rows by applying localChanges on top of server data.
|
|
91
80
|
const data = $derived.by(() => {
|
|
@@ -122,6 +111,23 @@
|
|
|
122
111
|
|
|
123
112
|
let activeTabFilter = $state<any>(undefined);
|
|
124
113
|
|
|
114
|
+
// Named-view lookup: when a `view` prop is supplied, resolve the
|
|
115
|
+
// matching `dataTable.view.<view.id>` registration. Exact key match,
|
|
116
|
+
// no `when` predicate — the caller already picked the view they want.
|
|
117
|
+
const customViewComponent = $derived.by(() => {
|
|
118
|
+
if (!view?.id) return null;
|
|
119
|
+
const key = `dataTable.view.${view.id}`;
|
|
120
|
+
for (const ext of Object.values(ctx.extensions ?? {})) {
|
|
121
|
+
const components = (ext as any)?.components ?? {};
|
|
122
|
+
const entry = components[key];
|
|
123
|
+
if (!entry) continue;
|
|
124
|
+
return entry && typeof entry === "object" && "component" in entry
|
|
125
|
+
? entry.component
|
|
126
|
+
: entry;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
});
|
|
130
|
+
|
|
125
131
|
// Canonicalize the incoming filter so values like `{ status: "Open" }`
|
|
126
132
|
// become `{ status: { $eq: "Open" } }`. The Filter UI and the server
|
|
127
133
|
// both expect operator objects, so doing this once at the boundary
|
|
@@ -273,7 +279,7 @@
|
|
|
273
279
|
|
|
274
280
|
<div
|
|
275
281
|
bind:clientWidth={dataTableContainerWidth}
|
|
276
|
-
class="flex flex-col overflow-auto h-full w-full"
|
|
282
|
+
class="flex flex-col overflow-auto h-full w-full bg-card"
|
|
277
283
|
>
|
|
278
284
|
{#snippet rowActionsSnippet(entry: Record<string, any>)}
|
|
279
285
|
<ExtensionsComponents
|
|
@@ -291,7 +297,6 @@
|
|
|
291
297
|
{collectionName}
|
|
292
298
|
bind:selectedRecords
|
|
293
299
|
{showImport}
|
|
294
|
-
{showCreate}
|
|
295
300
|
{parentContext}
|
|
296
301
|
onLink={isRecordingMode ? handleLink : undefined}
|
|
297
302
|
onCreate={isRecordingMode ? handleCreate : undefined}
|
|
@@ -311,6 +316,18 @@
|
|
|
311
316
|
<Skeleton class="h-8 w-[80%]" />
|
|
312
317
|
<Skeleton class="h-8 w-[60%]" />
|
|
313
318
|
</div>
|
|
319
|
+
{:else if customViewComponent}
|
|
320
|
+
{@const CustomView = customViewComponent}
|
|
321
|
+
<CustomView
|
|
322
|
+
{collectionName}
|
|
323
|
+
{data}
|
|
324
|
+
{columns}
|
|
325
|
+
bind:params
|
|
326
|
+
{loading}
|
|
327
|
+
refresh={() => { params = { ...params }; }}
|
|
328
|
+
{view}
|
|
329
|
+
utils={getExtensionUtils(lobb, ctx)}
|
|
330
|
+
/>
|
|
314
331
|
{:else}
|
|
315
332
|
<Table
|
|
316
333
|
{data}
|
|
@@ -325,17 +342,19 @@
|
|
|
325
342
|
{...tableProps}
|
|
326
343
|
rowActions={hasRowActions ? rowActionsSnippet : undefined}>
|
|
327
344
|
{#snippet tools(entry)}
|
|
328
|
-
{#if
|
|
329
|
-
<
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
345
|
+
{#if showEdit}
|
|
346
|
+
<CanAccess collection={collectionName} action="update">
|
|
347
|
+
<UpdateDetailViewButton
|
|
348
|
+
{collectionName}
|
|
349
|
+
recordId={entry.id}
|
|
350
|
+
variant="ghost"
|
|
351
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
352
|
+
Icon={Pencil}
|
|
353
|
+
changes={isRecordingMode ? localChanges.updated.find((u) => String(u.id) === String(entry.id))?.changes : undefined}
|
|
354
|
+
onChanges={isRecordingMode ? (c) => handleUpdate(String(entry.id), c) : undefined}
|
|
355
|
+
onSuccessfullSave={!isRecordingMode ? async () => { params = { ...params }; } : undefined}
|
|
356
|
+
></UpdateDetailViewButton>
|
|
357
|
+
</CanAccess>
|
|
339
358
|
{/if}
|
|
340
359
|
{#if parentContext}
|
|
341
360
|
<Button
|
|
@@ -22,6 +22,10 @@ interface Props {
|
|
|
22
22
|
tableProps?: Partial<TableProps>;
|
|
23
23
|
tabs?: CollectionTab[];
|
|
24
24
|
headerLeft?: Snippet<[]>;
|
|
25
|
+
view?: {
|
|
26
|
+
id: string;
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
};
|
|
25
29
|
}
|
|
26
30
|
declare const DataTable: import("svelte").Component<Props, {}, "">;
|
|
27
31
|
type DataTable = ReturnType<typeof DataTable>;
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
</script>
|
|
45
45
|
|
|
46
46
|
{#if tabs}
|
|
47
|
-
<div class="flex items-center gap-1 px-3 py-1.5 border-b shrink-0 bg-
|
|
47
|
+
<div class="flex items-center gap-1 px-3 py-1.5 border-b shrink-0 bg-card">
|
|
48
48
|
{#each tabs as tab}
|
|
49
49
|
{@const key = tab.id ?? tab.label}
|
|
50
50
|
{@const isActive = activeTab ? activeTab === key : (tab.default ?? tabs[0] === tab)}
|
|
@@ -71,7 +71,8 @@
|
|
|
71
71
|
<Popover.Trigger
|
|
72
72
|
class={buttonVariants({
|
|
73
73
|
variant: "ghost",
|
|
74
|
-
|
|
74
|
+
size: "sm",
|
|
75
|
+
class: "text-muted-foreground",
|
|
75
76
|
})}
|
|
76
77
|
>
|
|
77
78
|
<Plus />
|
|
@@ -184,7 +185,7 @@
|
|
|
184
185
|
<Popover.Trigger
|
|
185
186
|
class={buttonVariants({
|
|
186
187
|
variant: "ghost",
|
|
187
|
-
|
|
188
|
+
size: "sm",
|
|
188
189
|
})}
|
|
189
190
|
>
|
|
190
191
|
<Plus />
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { getStudioContext } from "../../context";
|
|
3
3
|
import type { Changes } from "../detailView/utils";
|
|
4
4
|
import type { ParentContext } from "./dataTable.svelte";
|
|
5
|
+
import CanAccess from "../canAccess.svelte";
|
|
5
6
|
import { Download, ListRestart, Plus, Trash, Link } from "lucide-svelte";
|
|
6
|
-
import * as Tooltip from "../ui/tooltip";
|
|
7
7
|
import LlmButton from "../LlmButton.svelte";
|
|
8
8
|
import FilterButton from "./filterButton.svelte";
|
|
9
9
|
import SortButton from "./sortButton.svelte";
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
onLink?: (record: any) => void;
|
|
27
27
|
onCreate?: (changes: Changes) => void;
|
|
28
28
|
showImport?: boolean;
|
|
29
|
-
showCreate?: boolean;
|
|
30
29
|
left?: Snippet<[]>;
|
|
31
30
|
}
|
|
32
31
|
|
|
@@ -38,7 +37,6 @@
|
|
|
38
37
|
onLink,
|
|
39
38
|
onCreate,
|
|
40
39
|
showImport = true,
|
|
41
|
-
showCreate = false,
|
|
42
40
|
left
|
|
43
41
|
}: Props = $props();
|
|
44
42
|
|
|
@@ -106,7 +104,7 @@
|
|
|
106
104
|
</script>
|
|
107
105
|
|
|
108
106
|
<div
|
|
109
|
-
class="flex justify-between items-center gap-2 p-2 border-b bg-
|
|
107
|
+
class="flex justify-between items-center gap-2 p-2 border-b bg-card h-12"
|
|
110
108
|
bind:clientWidth={headerWidth}
|
|
111
109
|
>
|
|
112
110
|
<div class="flex items-center gap-1">
|
|
@@ -116,7 +114,7 @@
|
|
|
116
114
|
Icon={Trash}
|
|
117
115
|
onclick={handleDeleteButton}
|
|
118
116
|
variant="outline"
|
|
119
|
-
|
|
117
|
+
size="sm"
|
|
120
118
|
>
|
|
121
119
|
Delete {selectedRecords.length}
|
|
122
120
|
{selectedRecords.length > 1 ? "records" : "record"}
|
|
@@ -136,7 +134,7 @@
|
|
|
136
134
|
variant="outline"
|
|
137
135
|
title="Filter table with AI"
|
|
138
136
|
description="Tell the AI how do you want to filter the table"
|
|
139
|
-
|
|
137
|
+
size="sm"
|
|
140
138
|
format={{
|
|
141
139
|
type: "json_object",
|
|
142
140
|
}}
|
|
@@ -162,27 +160,23 @@
|
|
|
162
160
|
<div>
|
|
163
161
|
<Button
|
|
164
162
|
variant="ghost"
|
|
165
|
-
|
|
163
|
+
size="sm"
|
|
164
|
+
class="text-muted-foreground"
|
|
166
165
|
Icon={ListRestart}
|
|
167
166
|
onclick={() => (params = { ...params })}
|
|
168
167
|
>
|
|
169
168
|
{headerIsSmall ? "" : "Refresh"}
|
|
170
169
|
</Button>
|
|
171
|
-
{#if showImport
|
|
172
|
-
<
|
|
173
|
-
<
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
/>
|
|
182
|
-
</Tooltip.Trigger>
|
|
183
|
-
<Tooltip.Content>Import</Tooltip.Content>
|
|
184
|
-
</Tooltip.Root>
|
|
185
|
-
</Tooltip.Provider>
|
|
170
|
+
{#if showImport}
|
|
171
|
+
<CanAccess collection={collectionName} action="create">
|
|
172
|
+
<ImportButton
|
|
173
|
+
{collectionName}
|
|
174
|
+
variant="outline"
|
|
175
|
+
size="sm"
|
|
176
|
+
Icon={Download}
|
|
177
|
+
onSuccessfullSave={() => (params = { ...params })}
|
|
178
|
+
/>
|
|
179
|
+
</CanAccess>
|
|
186
180
|
{/if}
|
|
187
181
|
<ExtensionsComponents
|
|
188
182
|
name="listView.header.actions"
|
|
@@ -194,24 +188,24 @@
|
|
|
194
188
|
<SelectRecord
|
|
195
189
|
{collectionName}
|
|
196
190
|
variant="outline"
|
|
197
|
-
|
|
191
|
+
size="sm"
|
|
198
192
|
Icon={Link}
|
|
199
193
|
onSelect={handleLink}
|
|
200
194
|
>
|
|
201
195
|
{headerIsSmall ? "" : "Link"}
|
|
202
196
|
</SelectRecord>
|
|
203
197
|
{/if}
|
|
204
|
-
{
|
|
198
|
+
<CanAccess collection={collectionName} action="create">
|
|
205
199
|
<CreateDetailViewButton
|
|
206
200
|
{collectionName}
|
|
207
201
|
variant="default"
|
|
208
|
-
|
|
202
|
+
size="sm"
|
|
209
203
|
Icon={Plus}
|
|
210
204
|
onChanges={onCreate ? handleCreate : undefined}
|
|
211
205
|
onSuccessfullSave={onCreate ? undefined : handleCreate}
|
|
212
206
|
>
|
|
213
207
|
{headerIsSmall ? "" : "Create"}
|
|
214
208
|
</CreateDetailViewButton>
|
|
215
|
-
|
|
209
|
+
</CanAccess>
|
|
216
210
|
</div>
|
|
217
211
|
</div>
|
|
@@ -9,7 +9,6 @@ interface Props {
|
|
|
9
9
|
onLink?: (record: any) => void;
|
|
10
10
|
onCreate?: (changes: Changes) => void;
|
|
11
11
|
showImport?: boolean;
|
|
12
|
-
showCreate?: boolean;
|
|
13
12
|
left?: Snippet<[]>;
|
|
14
13
|
}
|
|
15
14
|
declare const Header: import("svelte").Component<Props, {}, "selectedRecords" | "params">;
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<CreateDetailViewButton
|
|
62
62
|
collectionName={child.collection}
|
|
63
63
|
variant="ghost"
|
|
64
|
-
|
|
64
|
+
size="sm"
|
|
65
65
|
Icon={Plus}
|
|
66
66
|
values={{ [child.field]: recordId }}
|
|
67
67
|
onSuccessfullSave={async () => { refreshDataTable = !refreshDataTable; }}
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
class="
|
|
250
250
|
sticky left-0
|
|
251
251
|
flex items-center p-2.5 text-xs h-10
|
|
252
|
-
bg-
|
|
252
|
+
bg-card
|
|
253
253
|
border-r gap-2
|
|
254
254
|
"
|
|
255
255
|
>
|
|
@@ -295,8 +295,8 @@
|
|
|
295
295
|
}}
|
|
296
296
|
class="
|
|
297
297
|
flex items-center p-2.5 text-xs h-10 text-nowrap overflow-clip
|
|
298
|
-
{select ? 'cursor-pointer' : ''}
|
|
299
|
-
bg-
|
|
298
|
+
{select ? 'cursor-pointer hover:bg-accent' : ''}
|
|
299
|
+
bg-card
|
|
300
300
|
{lastColumn && !showLastColumnBorder ? '' : 'border-r'}
|
|
301
301
|
"
|
|
302
302
|
>
|
|
@@ -313,7 +313,7 @@
|
|
|
313
313
|
sticky right-0 z-10
|
|
314
314
|
flex items-center p-2.5 text-xs h-10
|
|
315
315
|
border-l gap-2
|
|
316
|
-
bg-
|
|
316
|
+
bg-card
|
|
317
317
|
"
|
|
318
318
|
>
|
|
319
319
|
{@render rowActions?.(entry, index)}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
showFooter?: boolean;
|
|
20
20
|
tableProps?: Partial<TableProps>;
|
|
21
21
|
tabs?: CollectionTab[];
|
|
22
|
+
view?: { id: string; [key: string]: any };
|
|
22
23
|
onClose?: () => void;
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
showFooter = true,
|
|
33
34
|
tableProps,
|
|
34
35
|
tabs,
|
|
36
|
+
view,
|
|
35
37
|
onClose,
|
|
36
38
|
}: Props = $props();
|
|
37
39
|
|
|
@@ -57,7 +59,7 @@
|
|
|
57
59
|
|
|
58
60
|
<div
|
|
59
61
|
transition:scale={{ duration: 200, easing: cubicOut, start: 0.95 }}
|
|
60
|
-
class="fixed left-1/2 top-1/2 z-40 flex h-[85vh] w-[95vw] max-w-7xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-lg border bg-
|
|
62
|
+
class="fixed left-1/2 top-1/2 z-40 flex h-[85vh] w-[95vw] max-w-7xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-lg border bg-card shadow-2xl"
|
|
61
63
|
>
|
|
62
64
|
<div class="flex h-12 shrink-0 items-center justify-between gap-4 border-b px-4">
|
|
63
65
|
<div class="text-sm font-medium">{title ?? collectionName}</div>
|
|
@@ -78,6 +80,7 @@
|
|
|
78
80
|
{showFooter}
|
|
79
81
|
{tableProps}
|
|
80
82
|
{tabs}
|
|
83
|
+
{view}
|
|
81
84
|
/>
|
|
82
85
|
</div>
|
|
83
86
|
</div>
|