@lobb-js/studio 0.33.0 → 0.35.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/applyUITheme.d.ts +2 -0
- package/dist/{applyStudioTheme.js → applyUITheme.js} +4 -4
- package/dist/components/LlmButton.svelte +4 -2
- package/dist/components/LlmButton.svelte.d.ts +1 -0
- package/dist/components/Studio.svelte +15 -7
- package/dist/components/codeEditor.svelte +1 -1
- package/dist/components/createManyButton.svelte +2 -2
- package/dist/components/dataTable/dataTable.svelte +1 -1
- 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 +14 -21
- 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 +8 -8
- package/dist/components/dataTable/utils.js +2 -1
- package/dist/components/dataTablePopup/dataTablePopup.svelte +1 -1
- package/dist/components/detailView/create/children.svelte +1 -1
- 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 +4 -4
- package/dist/components/detailView/create/createManyView.svelte +4 -4
- package/dist/components/detailView/detailView.svelte +2 -1
- package/dist/components/detailView/fieldInput.svelte +10 -10
- package/dist/components/detailView/fieldInputReplacement.svelte +7 -7
- package/dist/components/detailView/passwordInput.svelte +1 -1
- 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 +4 -4
- package/dist/components/diffViewer.svelte +1 -1
- package/dist/components/drawer.svelte +2 -2
- package/dist/components/foreingKeyInput.svelte +2 -2
- package/dist/components/horizontalNav.svelte +85 -0
- package/dist/components/horizontalNav.svelte.d.ts +3 -0
- package/dist/components/importButton.svelte +7 -7
- 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/polymorphicInput.svelte +1 -1
- package/dist/components/rangeCalendarButton.svelte +11 -12
- package/dist/components/richTextEditor.svelte +1 -1
- package/dist/components/routes/extensions/publicExtension.svelte +1 -1
- 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 +3 -3
- package/dist/components/sidebar/sidebarElements.svelte +4 -4
- 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/store.types.d.ts +4 -3
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +12 -0
- package/package.json +2 -2
- package/src/app.css +50 -76
- package/src/lib/{applyStudioTheme.ts → applyUITheme.ts} +5 -5
- package/src/lib/components/LlmButton.svelte +4 -2
- package/src/lib/components/Studio.svelte +15 -7
- package/src/lib/components/codeEditor.svelte +1 -1
- package/src/lib/components/createManyButton.svelte +2 -2
- package/src/lib/components/dataTable/dataTable.svelte +1 -1
- 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 +14 -21
- 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 +8 -8
- package/src/lib/components/dataTable/utils.ts +2 -1
- package/src/lib/components/dataTablePopup/dataTablePopup.svelte +1 -1
- package/src/lib/components/detailView/create/children.svelte +1 -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 +4 -4
- package/src/lib/components/detailView/create/createManyView.svelte +4 -4
- package/src/lib/components/detailView/detailView.svelte +2 -1
- package/src/lib/components/detailView/fieldInput.svelte +10 -10
- package/src/lib/components/detailView/fieldInputReplacement.svelte +7 -7
- package/src/lib/components/detailView/passwordInput.svelte +1 -1
- 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 +4 -4
- package/src/lib/components/diffViewer.svelte +1 -1
- package/src/lib/components/drawer.svelte +2 -2
- package/src/lib/components/foreingKeyInput.svelte +2 -2
- package/src/lib/components/horizontalNav.svelte +85 -0
- package/src/lib/components/importButton.svelte +7 -7
- package/src/lib/components/mainNav.svelte +15 -0
- package/src/lib/components/mainNavShared.ts +67 -0
- package/src/lib/components/polymorphicInput.svelte +1 -1
- package/src/lib/components/rangeCalendarButton.svelte +11 -12
- package/src/lib/components/richTextEditor.svelte +1 -1
- package/src/lib/components/routes/extensions/publicExtension.svelte +1 -1
- 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 +3 -3
- package/src/lib/components/sidebar/sidebarElements.svelte +4 -4
- 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/store.types.ts +2 -2
- package/src/lib/utils.ts +12 -0
- package/dist/applyStudioTheme.d.ts +0 -2
- package/dist/components/miniSidebar.svelte +0 -300
- package/dist/components/miniSidebar.svelte.d.ts +0 -5
- package/src/lib/components/miniSidebar.svelte +0 -300
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { page } from "$app/state";
|
|
4
|
+
import Button from "./ui/button/button.svelte";
|
|
5
|
+
import Separator from "./ui/separator/separator.svelte";
|
|
6
|
+
import * as Popover from "./ui/popover";
|
|
7
|
+
import { getStudioContext } from "../context";
|
|
8
|
+
import { buildNavSections, isItemActive, type NavItem } from "./mainNavShared";
|
|
9
|
+
|
|
10
|
+
const { lobb, ctx } = getStudioContext();
|
|
11
|
+
|
|
12
|
+
let sections: NavItem[][] = $state([[], [], []]);
|
|
13
|
+
onMount(async () => {
|
|
14
|
+
sections = await buildNavSections(lobb, ctx);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const currentPath = $derived(page.url.pathname.replace(/\/$/, "") || "/");
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
{#snippet section(section: NavItem[])}
|
|
21
|
+
<div class="flex flex-row items-center gap-1">
|
|
22
|
+
{#each section as item}
|
|
23
|
+
{@const active = isItemActive(currentPath, item)}
|
|
24
|
+
{#if !item.navs}
|
|
25
|
+
<Button
|
|
26
|
+
onclick={() => { if (item.onclick) item.onclick(); }}
|
|
27
|
+
href={item.href}
|
|
28
|
+
class="h-8 px-2.5 text-xs font-normal {active
|
|
29
|
+
? 'bg-accent text-foreground'
|
|
30
|
+
: 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}"
|
|
31
|
+
variant="ghost"
|
|
32
|
+
Icon={item.icon}
|
|
33
|
+
>
|
|
34
|
+
{item.label}
|
|
35
|
+
</Button>
|
|
36
|
+
{:else}
|
|
37
|
+
<Popover.Root>
|
|
38
|
+
<Popover.Trigger>
|
|
39
|
+
<Button
|
|
40
|
+
class="h-8 px-2.5 text-xs font-normal {active
|
|
41
|
+
? 'bg-accent text-accent-foreground'
|
|
42
|
+
: 'text-muted-foreground'}"
|
|
43
|
+
variant="ghost"
|
|
44
|
+
Icon={item.icon}
|
|
45
|
+
>
|
|
46
|
+
{item.label}
|
|
47
|
+
</Button>
|
|
48
|
+
</Popover.Trigger>
|
|
49
|
+
<Popover.Content sideOffset={6} side="bottom" align="start" class="w-60 p-0">
|
|
50
|
+
<div class="py-1">
|
|
51
|
+
{#each item.navs as childItem}
|
|
52
|
+
{@const childActive = isItemActive(currentPath, childItem)}
|
|
53
|
+
<div class="px-1 text-xs text-muted-foreground">
|
|
54
|
+
<Button
|
|
55
|
+
variant="ghost"
|
|
56
|
+
class="flex h-7 w-full justify-start p-2 text-xs font-normal {childActive
|
|
57
|
+
? 'bg-accent text-accent-foreground'
|
|
58
|
+
: 'text-muted-foreground'}"
|
|
59
|
+
Icon={childItem.icon}
|
|
60
|
+
onclick={() => { if (childItem.onclick) childItem.onclick(); }}
|
|
61
|
+
href={childItem.href}
|
|
62
|
+
>
|
|
63
|
+
{childItem.label}
|
|
64
|
+
</Button>
|
|
65
|
+
</div>
|
|
66
|
+
{/each}
|
|
67
|
+
</div>
|
|
68
|
+
</Popover.Content>
|
|
69
|
+
</Popover.Root>
|
|
70
|
+
{/if}
|
|
71
|
+
{/each}
|
|
72
|
+
</div>
|
|
73
|
+
{/snippet}
|
|
74
|
+
|
|
75
|
+
<div class="relative border-b border-border bg-card text-foreground h-12 w-full px-3 flex items-center justify-between gap-2">
|
|
76
|
+
<div class="flex flex-row items-center gap-2">
|
|
77
|
+
{@render section(sections[0])}
|
|
78
|
+
{#if sections[1].length > 0}
|
|
79
|
+
<Separator orientation="vertical" class="h-6 bg-border" />
|
|
80
|
+
{@render section(sections[1])}
|
|
81
|
+
{/if}
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
{@render section(sections[2])}
|
|
85
|
+
</div>
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
}
|
|
171
171
|
</script>
|
|
172
172
|
|
|
173
|
-
<Button variant={rest.variant} class={rest.class} onclick={showDrawer}>
|
|
173
|
+
<Button variant={rest.variant} size={rest.size} class={rest.class} onclick={showDrawer}>
|
|
174
174
|
<ExtensionsComponents
|
|
175
175
|
name="collections.import.button.content"
|
|
176
176
|
utils={getExtensionUtils(lobb, ctx)}
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
/>
|
|
250
250
|
{:else}
|
|
251
251
|
<textarea
|
|
252
|
-
class="block h-56 w-full resize-none rounded-md border bg-muted
|
|
252
|
+
class="block h-56 w-full resize-none rounded-md border bg-muted p-3 font-mono text-sm focus:outline-none focus:ring-1 focus:ring-ring"
|
|
253
253
|
placeholder="Paste CSV or JSON here..."
|
|
254
254
|
bind:value={pasteContent}
|
|
255
255
|
></textarea>
|
|
@@ -264,11 +264,11 @@
|
|
|
264
264
|
</div>
|
|
265
265
|
|
|
266
266
|
<div class="flex h-12 shrink-0 items-center justify-end gap-2 border-t px-4">
|
|
267
|
-
<Button variant="outline" onclick={hideDrawer}
|
|
267
|
+
<Button variant="outline" onclick={hideDrawer} size="sm" Icon={X}>
|
|
268
268
|
Cancel
|
|
269
269
|
</Button>
|
|
270
270
|
{#if activeTab === "paste"}
|
|
271
|
-
<Button onclick={() => processContent(pasteContent)}
|
|
271
|
+
<Button onclick={() => processContent(pasteContent)} size="sm" Icon={FileText}>
|
|
272
272
|
Parse
|
|
273
273
|
</Button>
|
|
274
274
|
{/if}
|
|
@@ -289,10 +289,10 @@
|
|
|
289
289
|
</div>
|
|
290
290
|
|
|
291
291
|
<div class="flex h-12 shrink-0 items-center justify-end gap-2 border-t px-4">
|
|
292
|
-
<Button variant="outline" onclick={() => (step = "input")}
|
|
292
|
+
<Button variant="outline" onclick={() => (step = "input")} size="sm" Icon={X}>
|
|
293
293
|
Cancel
|
|
294
294
|
</Button>
|
|
295
|
-
<Button onclick={handleImport}
|
|
295
|
+
<Button onclick={handleImport} size="sm" Icon={Check}>
|
|
296
296
|
Import {transformedRows.length} records
|
|
297
297
|
</Button>
|
|
298
298
|
</div>
|
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
</div>
|
|
346
346
|
{/if}
|
|
347
347
|
<div class="flex h-12 shrink-0 items-center justify-end gap-2 border-t px-4">
|
|
348
|
-
<Button onclick={hideDrawer}
|
|
348
|
+
<Button onclick={hideDrawer} size="sm" Icon={Check}>
|
|
349
349
|
Done
|
|
350
350
|
</Button>
|
|
351
351
|
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import VerticalNav from "./verticalNav.svelte";
|
|
3
|
+
import HorizontalNav from "./horizontalNav.svelte";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
orientation?: "vertical" | "horizontal";
|
|
7
|
+
}
|
|
8
|
+
const { orientation = "vertical" }: Props = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
{#if orientation === "horizontal"}
|
|
12
|
+
<HorizontalNav />
|
|
13
|
+
{:else}
|
|
14
|
+
<VerticalNav />
|
|
15
|
+
{/if}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { House, Layers, Library, Workflow } from "lucide-svelte";
|
|
2
|
+
import { emitEvent } from "../eventSystem";
|
|
3
|
+
import { getDashboardNavs } from "../extensions/extensionUtils";
|
|
4
|
+
|
|
5
|
+
export type NavItem = {
|
|
6
|
+
label: string;
|
|
7
|
+
href?: string;
|
|
8
|
+
icon: any;
|
|
9
|
+
represents?: string;
|
|
10
|
+
onclick?: () => void;
|
|
11
|
+
navs?: NavItem[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
async function isItemVisible(lobb: any, ctx: any, item: NavItem): Promise<boolean> {
|
|
15
|
+
if (!item.represents) return true;
|
|
16
|
+
const res = await emitEvent({ lobb, ctx }, "auth.canAccess", {
|
|
17
|
+
collection: item.represents,
|
|
18
|
+
action: "read",
|
|
19
|
+
});
|
|
20
|
+
return res === true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function buildNavSections(lobb: any, ctx: any): Promise<NavItem[][]> {
|
|
24
|
+
const rawSections: NavItem[][] = [
|
|
25
|
+
[
|
|
26
|
+
{ label: "Home", href: "/studio", icon: House },
|
|
27
|
+
{ label: "Collections", href: "/studio/collections", icon: Library },
|
|
28
|
+
{ label: "Data Model", href: "/studio/datamodel", icon: Layers, represents: "core_data_model" },
|
|
29
|
+
{ label: "Workflows", href: "/studio/workflows", icon: Workflow, represents: "core_workflows" },
|
|
30
|
+
],
|
|
31
|
+
[],
|
|
32
|
+
[],
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const navs = getDashboardNavs(ctx);
|
|
36
|
+
if (navs.top) rawSections[0].push(...(navs.top as NavItem[]));
|
|
37
|
+
if (navs.middle) rawSections[1].push(...(navs.middle as NavItem[]));
|
|
38
|
+
if (navs.bottom) rawSections[2].push(...(navs.bottom as NavItem[]));
|
|
39
|
+
|
|
40
|
+
const result: NavItem[][] = [[], [], []];
|
|
41
|
+
for (let i = 0; i < rawSections.length; i++) {
|
|
42
|
+
for (const item of rawSections[i]) {
|
|
43
|
+
if (item.navs) {
|
|
44
|
+
const visibleChildren: NavItem[] = [];
|
|
45
|
+
for (const child of item.navs) {
|
|
46
|
+
if (await isItemVisible(lobb, ctx, child)) visibleChildren.push(child);
|
|
47
|
+
}
|
|
48
|
+
if (visibleChildren.length && (await isItemVisible(lobb, ctx, item))) {
|
|
49
|
+
result[i].push({ ...item, navs: visibleChildren });
|
|
50
|
+
}
|
|
51
|
+
} else if (await isItemVisible(lobb, ctx, item)) {
|
|
52
|
+
result[i].push(item);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function isItemActive(currentPath: string, item: NavItem): boolean {
|
|
60
|
+
if (item.navs) return item.navs.some((c) => isItemActive(currentPath, c));
|
|
61
|
+
if (!item.href) return false;
|
|
62
|
+
const itemHref = item.href.replace(/\/$/, "") || "/";
|
|
63
|
+
// "/studio" needs an exact match — it's a prefix of every other
|
|
64
|
+
// route, so startsWith would always light it up.
|
|
65
|
+
if (itemHref === "/studio") return currentPath === "/studio";
|
|
66
|
+
return currentPath === itemHref || currentPath.startsWith(itemHref + "/");
|
|
67
|
+
}
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
}
|
|
71
71
|
</script>
|
|
72
72
|
|
|
73
|
-
<div class="flex h-9 w-full items-center gap-1.5 rounded-md border pl-1.5 pr-9 text-xs bg-muted
|
|
73
|
+
<div class="flex h-9 w-full items-center gap-1.5 rounded-md border pl-1.5 pr-9 text-xs bg-muted {destructive ? 'border-destructive bg-destructive/10' : ''}">
|
|
74
74
|
<!-- Collection picker -->
|
|
75
75
|
<Popover.Root bind:open={collectionPopoverOpen}>
|
|
76
76
|
<Popover.Trigger>
|
|
@@ -33,9 +33,8 @@
|
|
|
33
33
|
<Popover.Root>
|
|
34
34
|
<Popover.Trigger
|
|
35
35
|
class={cn(
|
|
36
|
-
buttonVariants({ variant: "outline" }),
|
|
36
|
+
buttonVariants({ variant: "outline", size: "sm" }),
|
|
37
37
|
!value && "text-muted-foreground",
|
|
38
|
-
"h-7 px-3 text-xs font-normal",
|
|
39
38
|
)}
|
|
40
39
|
>
|
|
41
40
|
<CalendarIcon class="mr-2 size-4" />
|
|
@@ -59,7 +58,7 @@
|
|
|
59
58
|
class="flex flex-col overflow-hidden text-muted-foreground"
|
|
60
59
|
>
|
|
61
60
|
<button
|
|
62
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
61
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
63
62
|
onclick={() => {
|
|
64
63
|
const currentDate = today(getLocalTimeZone());
|
|
65
64
|
value = {
|
|
@@ -71,7 +70,7 @@
|
|
|
71
70
|
Today
|
|
72
71
|
</button>
|
|
73
72
|
<button
|
|
74
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
73
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
75
74
|
onclick={() => {
|
|
76
75
|
const currentDate = today(getLocalTimeZone());
|
|
77
76
|
value = {
|
|
@@ -83,7 +82,7 @@
|
|
|
83
82
|
Yesterday
|
|
84
83
|
</button>
|
|
85
84
|
<button
|
|
86
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
85
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
87
86
|
onclick={() => {
|
|
88
87
|
const currentDate = today(getLocalTimeZone());
|
|
89
88
|
const weekStart = startOfWeek(currentDate, "en-US");
|
|
@@ -96,7 +95,7 @@
|
|
|
96
95
|
This week (Sun - Today)
|
|
97
96
|
</button>
|
|
98
97
|
<button
|
|
99
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
98
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
100
99
|
onclick={() => {
|
|
101
100
|
const currentDate = today(getLocalTimeZone());
|
|
102
101
|
const thisWeekStart = startOfWeek(
|
|
@@ -119,7 +118,7 @@
|
|
|
119
118
|
Last week (Sun - Sat)
|
|
120
119
|
</button>
|
|
121
120
|
<button
|
|
122
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
121
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
123
122
|
onclick={() => {
|
|
124
123
|
const currentDate = today(getLocalTimeZone());
|
|
125
124
|
value = {
|
|
@@ -131,7 +130,7 @@
|
|
|
131
130
|
Last 7 days
|
|
132
131
|
</button>
|
|
133
132
|
<button
|
|
134
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
133
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
135
134
|
onclick={() => {
|
|
136
135
|
const currentDate = today(getLocalTimeZone());
|
|
137
136
|
value = {
|
|
@@ -143,7 +142,7 @@
|
|
|
143
142
|
Last 30 days
|
|
144
143
|
</button>
|
|
145
144
|
<button
|
|
146
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
145
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
147
146
|
onclick={() => {
|
|
148
147
|
const currentDate = today(getLocalTimeZone());
|
|
149
148
|
value = {
|
|
@@ -155,7 +154,7 @@
|
|
|
155
154
|
Last 90 days
|
|
156
155
|
</button>
|
|
157
156
|
<button
|
|
158
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
157
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
159
158
|
onclick={() => {
|
|
160
159
|
const currentDate = today(getLocalTimeZone());
|
|
161
160
|
value = {
|
|
@@ -167,7 +166,7 @@
|
|
|
167
166
|
Last 12 months
|
|
168
167
|
</button>
|
|
169
168
|
<button
|
|
170
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
169
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
171
170
|
onclick={() => {
|
|
172
171
|
const currentDate = today(getLocalTimeZone());
|
|
173
172
|
const lastYearStart = currentDate
|
|
@@ -185,7 +184,7 @@
|
|
|
185
184
|
Last Calendar year
|
|
186
185
|
</button>
|
|
187
186
|
<button
|
|
188
|
-
class="text-start text-sm py-2 px-2 hover:bg-muted
|
|
187
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted hover:text-primary"
|
|
189
188
|
onclick={() => {
|
|
190
189
|
const currentDate = today(getLocalTimeZone());
|
|
191
190
|
const yearStart = currentDate.set({
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const { lobb, ctx } = getStudioContext();
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
|
-
<div class="grid h-full w-full overflow-
|
|
11
|
+
<div class="grid h-full w-full overflow-hidden bg-background">
|
|
12
12
|
{#key extension && page}
|
|
13
13
|
<ExtensionsComponents
|
|
14
14
|
name="publicPages.{page}"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<div class="text-4xl">Welcome back</div>
|
|
35
35
|
<div>Sign in to your account</div>
|
|
36
36
|
</div>
|
|
37
|
-
<div class="flex flex-col gap-6 rounded-md border bg-
|
|
37
|
+
<div class="flex flex-col gap-6 rounded-md border bg-card p-6">
|
|
38
38
|
<div class="flex flex-col gap-2">
|
|
39
39
|
<div>
|
|
40
40
|
<div class="mb-1 text-sm font-medium">Server</div>
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
>
|
|
91
91
|
<div
|
|
92
92
|
class="
|
|
93
|
-
bg-
|
|
93
|
+
bg-card border-r overflow-hidden
|
|
94
94
|
"
|
|
95
95
|
style="
|
|
96
96
|
{sidebarProperties.collapsed ? 'border-right-width: 0px; padding: 0px;' : ''}
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
{#if showSearch}
|
|
108
108
|
<div class="p-2">
|
|
109
109
|
<div
|
|
110
|
-
class="flex items-center px-4 py-1 text-muted-foreground bg-muted
|
|
110
|
+
class="flex items-center px-4 py-1 text-muted-foreground bg-muted border rounded-md"
|
|
111
111
|
>
|
|
112
112
|
<input
|
|
113
113
|
type="text"
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
</div>
|
|
124
124
|
<div
|
|
125
125
|
class="
|
|
126
|
-
text-
|
|
126
|
+
text-foreground p-2 overflow-y-auto overflow-x-clip
|
|
127
127
|
"
|
|
128
128
|
style="max-height: {sidebarBodyHeight}px;"
|
|
129
129
|
>
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
{#each data as node, index}
|
|
59
59
|
{#if node.type === "directory"}
|
|
60
60
|
<button
|
|
61
|
-
class="flex items-center justify-between p-2 gap-2 text-muted-foreground rounded-md hover:bg-muted
|
|
61
|
+
class="flex items-center justify-between p-2 gap-2 text-muted-foreground rounded-md hover:bg-muted cursor-pointer"
|
|
62
62
|
onclick={() => toggleDir(index)}
|
|
63
63
|
>
|
|
64
64
|
<div class="flex items-center gap-2">
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
href={node.href}
|
|
90
90
|
variant="ghost"
|
|
91
91
|
class="
|
|
92
|
-
flex items-center justify-between p-2 gap-2
|
|
93
|
-
rounded-md {isselected ? 'bg-
|
|
92
|
+
flex items-center justify-between p-2 gap-2 text-muted-foreground
|
|
93
|
+
rounded-md {isselected ? 'bg-accent' : 'hover:bg-muted'}
|
|
94
94
|
"
|
|
95
95
|
title={node.name}
|
|
96
96
|
>
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
{#if node.icon}
|
|
99
99
|
<node.icon size="17.5" />
|
|
100
100
|
{/if}
|
|
101
|
-
<div class="text-xs {isselected ? 'text-
|
|
101
|
+
<div class="text-xs {isselected ? 'text-foreground font-medium' : ''}">
|
|
102
102
|
{node.name}
|
|
103
103
|
</div>
|
|
104
104
|
</div>
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
</script>
|
|
40
40
|
|
|
41
41
|
<div class="flex flex-col h-full bg-background">
|
|
42
|
-
<div class="flex justify-between items-center gap-2 p-2 border-b h-
|
|
42
|
+
<div class="flex justify-between items-center gap-2 p-2 border-b h-12 shrink-0">
|
|
43
43
|
<div class="flex items-center gap-1">
|
|
44
44
|
<SidebarTrigger />
|
|
45
45
|
</div>
|
|
46
46
|
<div>
|
|
47
|
-
<Button
|
|
47
|
+
<Button size="sm" Icon={Save} onclick={handleSave}>
|
|
48
48
|
Save
|
|
49
49
|
</Button>
|
|
50
50
|
</div>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
bind:ref
|
|
15
15
|
{sideOffset}
|
|
16
16
|
class={cn(
|
|
17
|
-
"bg-
|
|
17
|
+
"bg-popover text-popover-foreground border shadow-sm animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md px-3 py-1.5 text-xs",
|
|
18
18
|
className
|
|
19
19
|
)}
|
|
20
20
|
{...restProps}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { X } from "lucide-svelte";
|
|
3
|
+
import { onMount } from "svelte";
|
|
4
|
+
import { page } from "$app/state";
|
|
5
|
+
import { goto } from "$app/navigation";
|
|
6
|
+
import Button from "./ui/button/button.svelte";
|
|
7
|
+
import Separator from "./ui/separator/separator.svelte";
|
|
8
|
+
import * as Tooltip from "./ui/tooltip";
|
|
9
|
+
import * as Accordion from "./ui/accordion/index.js";
|
|
10
|
+
import * as Popover from "./ui/popover";
|
|
11
|
+
import { getStudioContext } from "../context";
|
|
12
|
+
import { mediaQueries } from "../utils";
|
|
13
|
+
import { buildNavSections, isItemActive, type NavItem } from "./mainNavShared";
|
|
14
|
+
|
|
15
|
+
const { lobb, ctx } = getStudioContext();
|
|
16
|
+
|
|
17
|
+
let isSmallScreen = $derived(!mediaQueries.sm.current);
|
|
18
|
+
let isCollapsed = $state(true);
|
|
19
|
+
$effect(() => {
|
|
20
|
+
isCollapsed = isSmallScreen;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
let sections: NavItem[][] = $state([[], [], []]);
|
|
24
|
+
onMount(async () => {
|
|
25
|
+
sections = await buildNavSections(lobb, ctx);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const currentPath = $derived(page.url.pathname.replace(/\/$/, "") || "/");
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
{#snippet section(section: NavItem[])}
|
|
32
|
+
<div class="flex flex-col {isSmallScreen ? 'gap-0' : 'gap-2'}">
|
|
33
|
+
{#each section as item}
|
|
34
|
+
{@const active = isItemActive(currentPath, item)}
|
|
35
|
+
{#if isSmallScreen}
|
|
36
|
+
{#if !item.navs}
|
|
37
|
+
<Button
|
|
38
|
+
onclick={() => {
|
|
39
|
+
if (item.onclick) item.onclick();
|
|
40
|
+
else if (item.href) goto(item.href);
|
|
41
|
+
isCollapsed = true;
|
|
42
|
+
}}
|
|
43
|
+
class="flex items-center justify-start flex-nowrap text-nowrap h-10 w-full {active
|
|
44
|
+
? 'bg-accent text-foreground'
|
|
45
|
+
: 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}"
|
|
46
|
+
variant="ghost"
|
|
47
|
+
size="icon"
|
|
48
|
+
Icon={item.icon}
|
|
49
|
+
>
|
|
50
|
+
{item.label}
|
|
51
|
+
</Button>
|
|
52
|
+
{:else}
|
|
53
|
+
<Accordion.Root type="single">
|
|
54
|
+
<Accordion.Item class="border-b-0">
|
|
55
|
+
<Accordion.Trigger class="justify-between p-0 h-10">
|
|
56
|
+
<div
|
|
57
|
+
class="flex items-center gap-2 {active
|
|
58
|
+
? 'text-accent-foreground'
|
|
59
|
+
: 'text-foreground/70'}"
|
|
60
|
+
>
|
|
61
|
+
<item.icon size="18" />
|
|
62
|
+
<div class="text-nowrap">{item.label}</div>
|
|
63
|
+
</div>
|
|
64
|
+
</Accordion.Trigger>
|
|
65
|
+
<Accordion.Content class="pl-2 border-l border-border">
|
|
66
|
+
{#each item.navs as childItem}
|
|
67
|
+
{@const childActive = isItemActive(currentPath, childItem)}
|
|
68
|
+
<Button
|
|
69
|
+
onclick={() => {
|
|
70
|
+
if (childItem.onclick) childItem.onclick();
|
|
71
|
+
else if (childItem.href) goto(childItem.href);
|
|
72
|
+
isCollapsed = true;
|
|
73
|
+
}}
|
|
74
|
+
class="flex items-center justify-start flex-nowrap text-nowrap h-8 w-full {childActive
|
|
75
|
+
? 'bg-accent text-foreground'
|
|
76
|
+
: 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}"
|
|
77
|
+
variant="ghost"
|
|
78
|
+
size="icon"
|
|
79
|
+
Icon={childItem.icon}
|
|
80
|
+
>
|
|
81
|
+
{childItem.label}
|
|
82
|
+
</Button>
|
|
83
|
+
{/each}
|
|
84
|
+
</Accordion.Content>
|
|
85
|
+
</Accordion.Item>
|
|
86
|
+
</Accordion.Root>
|
|
87
|
+
{/if}
|
|
88
|
+
{:else}
|
|
89
|
+
<Tooltip.Root>
|
|
90
|
+
<Tooltip.Trigger>
|
|
91
|
+
{#if !item.navs}
|
|
92
|
+
<Button
|
|
93
|
+
onclick={() => { if (item.onclick) item.onclick(); }}
|
|
94
|
+
href={item.href}
|
|
95
|
+
class={active
|
|
96
|
+
? 'bg-accent text-foreground'
|
|
97
|
+
: 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}
|
|
98
|
+
variant="ghost"
|
|
99
|
+
size="icon"
|
|
100
|
+
Icon={item.icon}
|
|
101
|
+
></Button>
|
|
102
|
+
{:else}
|
|
103
|
+
<Popover.Root>
|
|
104
|
+
<Popover.Trigger>
|
|
105
|
+
<Button
|
|
106
|
+
class={active
|
|
107
|
+
? 'bg-accent text-foreground'
|
|
108
|
+
: 'text-foreground/70 hover:bg-accent/50 hover:text-foreground'}
|
|
109
|
+
variant="ghost"
|
|
110
|
+
size="icon"
|
|
111
|
+
Icon={item.icon}
|
|
112
|
+
></Button>
|
|
113
|
+
</Popover.Trigger>
|
|
114
|
+
<Popover.Content sideOffset={17.5} side="right" class="popover_content w-60 mb-4 p-0">
|
|
115
|
+
<div class="py-1">
|
|
116
|
+
{#each item.navs as childItem}
|
|
117
|
+
{@const childActive = isItemActive(currentPath, childItem)}
|
|
118
|
+
<div class="px-1 text-xs text-muted-foreground">
|
|
119
|
+
<Button
|
|
120
|
+
variant="ghost"
|
|
121
|
+
class="flex h-7 w-full justify-start p-2 text-xs font-normal {childActive
|
|
122
|
+
? 'bg-accent text-accent-foreground'
|
|
123
|
+
: 'text-muted-foreground'}"
|
|
124
|
+
Icon={childItem.icon}
|
|
125
|
+
onclick={() => { if (childItem.onclick) childItem.onclick(); }}
|
|
126
|
+
href={childItem.href}
|
|
127
|
+
>
|
|
128
|
+
{childItem.label}
|
|
129
|
+
</Button>
|
|
130
|
+
</div>
|
|
131
|
+
{/each}
|
|
132
|
+
</div>
|
|
133
|
+
</Popover.Content>
|
|
134
|
+
</Popover.Root>
|
|
135
|
+
{/if}
|
|
136
|
+
</Tooltip.Trigger>
|
|
137
|
+
<Tooltip.Content side="right" sideOffset={15}>
|
|
138
|
+
{item.label}
|
|
139
|
+
</Tooltip.Content>
|
|
140
|
+
</Tooltip.Root>
|
|
141
|
+
{/if}
|
|
142
|
+
{/each}
|
|
143
|
+
</div>
|
|
144
|
+
{/snippet}
|
|
145
|
+
|
|
146
|
+
<div
|
|
147
|
+
class="
|
|
148
|
+
{isSmallScreen ? 'fixed top-0 left-0 h-full z-50' : 'relative'}
|
|
149
|
+
border-r border-border bg-card text-foreground w-screen
|
|
150
|
+
{isCollapsed
|
|
151
|
+
? 'max-w-0 p-0'
|
|
152
|
+
: `max-w-14 ${isSmallScreen ? 'px-3 pb-3' : 'p-2'}`}"
|
|
153
|
+
style="transition: max-width 150ms, padding 150ms; {isSmallScreen && !isCollapsed ? 'max-width: 100vw' : ''}"
|
|
154
|
+
>
|
|
155
|
+
{#if isSmallScreen && !isCollapsed}
|
|
156
|
+
<X
|
|
157
|
+
class="absolute h-10 text-foreground/70 hover:bg-transparent cursor-pointer hover:text-foreground"
|
|
158
|
+
style="left: 0.6rem; top: 0rem;"
|
|
159
|
+
size="18"
|
|
160
|
+
onclick={() => (isCollapsed = !isCollapsed)}
|
|
161
|
+
/>
|
|
162
|
+
{/if}
|
|
163
|
+
<div class="flex h-full flex-col justify-between gap-2 w-full overflow-hidden">
|
|
164
|
+
<div class="flex flex-col gap-2 {isSmallScreen ? 'pt-8' : ''}">
|
|
165
|
+
{@render section(sections[0])}
|
|
166
|
+
<Separator class="bg-border" />
|
|
167
|
+
{@render section(sections[1])}
|
|
168
|
+
</div>
|
|
169
|
+
<div class="flex flex-col gap-2">
|
|
170
|
+
<Separator class="bg-border" />
|
|
171
|
+
{@render section(sections[2])}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
<div>
|
|
158
158
|
{#if workflow.id}
|
|
159
159
|
<Button
|
|
160
|
-
class="
|
|
160
|
+
size="sm" class="w-full"
|
|
161
161
|
variant="default"
|
|
162
162
|
onclick={handleUpdateWorkflow}
|
|
163
163
|
Icon={Edit}
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
</Button>
|
|
168
168
|
{:else}
|
|
169
169
|
<Button
|
|
170
|
-
class="
|
|
170
|
+
size="sm" class="w-full"
|
|
171
171
|
variant="default"
|
|
172
172
|
onclick={handleCreateWorkflow}
|
|
173
173
|
Icon={Plus}
|