@lobb-js/studio 0.1.31
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/.env.example +1 -0
- package/.storybook/main.ts +31 -0
- package/.storybook/preview.ts +21 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/README.md +47 -0
- package/components.json +16 -0
- package/docker-entrypoint.sh +7 -0
- package/dockerfile +27 -0
- package/index.html +13 -0
- package/package.json +77 -0
- package/public/lobb.svg +15 -0
- package/src/Studio.svelte +150 -0
- package/src/app.css +121 -0
- package/src/components-export.ts +21 -0
- package/src/extensions/extension.types.ts +93 -0
- package/src/extensions/extensionUtils.ts +192 -0
- package/src/lib/Lobb.ts +241 -0
- package/src/lib/components/LlmButton.svelte +136 -0
- package/src/lib/components/alertView.svelte +20 -0
- package/src/lib/components/breadCrumbs.svelte +60 -0
- package/src/lib/components/combobox.svelte +92 -0
- package/src/lib/components/confirmationDialog/confirmationDialog.svelte +33 -0
- package/src/lib/components/confirmationDialog/store.svelte.ts +28 -0
- package/src/lib/components/createManyButton.svelte +107 -0
- package/src/lib/components/dataTable/childRecords.svelte +140 -0
- package/src/lib/components/dataTable/dataTable.svelte +223 -0
- package/src/lib/components/dataTable/fieldCell.svelte +74 -0
- package/src/lib/components/dataTable/filter.svelte +282 -0
- package/src/lib/components/dataTable/filterButton.svelte +39 -0
- package/src/lib/components/dataTable/footer.svelte +84 -0
- package/src/lib/components/dataTable/header.svelte +154 -0
- package/src/lib/components/dataTable/sort.svelte +171 -0
- package/src/lib/components/dataTable/sortButton.svelte +36 -0
- package/src/lib/components/dataTable/table.svelte +337 -0
- package/src/lib/components/dataTable/utils.ts +127 -0
- package/src/lib/components/detailView/create/children.svelte +68 -0
- package/src/lib/components/detailView/create/createDetailView.svelte +226 -0
- package/src/lib/components/detailView/create/createDetailViewButton.svelte +32 -0
- package/src/lib/components/detailView/create/createManyView.svelte +250 -0
- package/src/lib/components/detailView/create/subRecords.svelte +48 -0
- package/src/lib/components/detailView/detailViewForm.svelte +104 -0
- package/src/lib/components/detailView/fieldCustomInput.svelte +23 -0
- package/src/lib/components/detailView/fieldInput.svelte +287 -0
- package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
- package/src/lib/components/detailView/store.svelte.ts +61 -0
- package/src/lib/components/detailView/update/children.svelte +94 -0
- package/src/lib/components/detailView/update/updateDetailView.svelte +175 -0
- package/src/lib/components/detailView/update/updateDetailViewButton.svelte +32 -0
- package/src/lib/components/detailView/utils.ts +177 -0
- package/src/lib/components/diffViewer.svelte +102 -0
- package/src/lib/components/drawer.svelte +28 -0
- package/src/lib/components/extensionsComponents.svelte +31 -0
- package/src/lib/components/foreingKeyInput.svelte +80 -0
- package/src/lib/components/header.svelte +45 -0
- package/src/lib/components/loadingTypesForMonacoEditor.ts +36 -0
- package/src/lib/components/miniSidebar.svelte +238 -0
- package/src/lib/components/monacoEditor.svelte +181 -0
- package/src/lib/components/rangeCalendarButton.svelte +257 -0
- package/src/lib/components/selectRecord.svelte +126 -0
- package/src/lib/components/setServerPage.svelte +48 -0
- package/src/lib/components/sidebar/index.ts +4 -0
- package/src/lib/components/sidebar/sidebar.svelte +149 -0
- package/src/lib/components/sidebar/sidebarElements.svelte +144 -0
- package/src/lib/components/sidebar/sidebarTrigger.svelte +33 -0
- package/src/lib/components/singletone.svelte +69 -0
- package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
- package/src/lib/components/ui/accordion/accordion-item.svelte +12 -0
- package/src/lib/components/ui/accordion/accordion-trigger.svelte +31 -0
- package/src/lib/components/ui/accordion/index.ts +17 -0
- package/src/lib/components/ui/alert/alert-description.svelte +16 -0
- package/src/lib/components/ui/alert/alert-title.svelte +24 -0
- package/src/lib/components/ui/alert/alert.svelte +39 -0
- package/src/lib/components/ui/alert/index.ts +14 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +13 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +17 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +26 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +16 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +20 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +19 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +18 -0
- package/src/lib/components/ui/alert-dialog/index.ts +40 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +16 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +15 -0
- package/src/lib/components/ui/breadcrumb/index.ts +25 -0
- package/src/lib/components/ui/button/button.svelte +110 -0
- package/src/lib/components/ui/button/index.ts +17 -0
- package/src/lib/components/ui/checkbox/checkbox.svelte +35 -0
- package/src/lib/components/ui/checkbox/index.ts +6 -0
- package/src/lib/components/ui/command/command-dialog.svelte +35 -0
- package/src/lib/components/ui/command/command-empty.svelte +12 -0
- package/src/lib/components/ui/command/command-group.svelte +31 -0
- package/src/lib/components/ui/command/command-input.svelte +25 -0
- package/src/lib/components/ui/command/command-item.svelte +19 -0
- package/src/lib/components/ui/command/command-link-item.svelte +19 -0
- package/src/lib/components/ui/command/command-list.svelte +16 -0
- package/src/lib/components/ui/command/command-separator.svelte +12 -0
- package/src/lib/components/ui/command/command-shortcut.svelte +20 -0
- package/src/lib/components/ui/command/command.svelte +21 -0
- package/src/lib/components/ui/command/index.ts +40 -0
- package/src/lib/components/ui/dialog/dialog-content.svelte +38 -0
- package/src/lib/components/ui/dialog/dialog-description.svelte +16 -0
- package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-overlay.svelte +19 -0
- package/src/lib/components/ui/dialog/dialog-title.svelte +16 -0
- package/src/lib/components/ui/dialog/index.ts +37 -0
- package/src/lib/components/ui/input/index.ts +7 -0
- package/src/lib/components/ui/input/input.svelte +46 -0
- package/src/lib/components/ui/label/index.ts +7 -0
- package/src/lib/components/ui/label/label.svelte +19 -0
- package/src/lib/components/ui/popover/index.ts +17 -0
- package/src/lib/components/ui/popover/popover-content.svelte +28 -0
- package/src/lib/components/ui/range-calendar/index.ts +30 -0
- package/src/lib/components/ui/range-calendar/range-calendar-cell.svelte +19 -0
- package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +35 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-body.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-head.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-header.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-heading.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-months.svelte +20 -0
- package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +27 -0
- package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +27 -0
- package/src/lib/components/ui/range-calendar/range-calendar.svelte +57 -0
- package/src/lib/components/ui/select/index.ts +34 -0
- package/src/lib/components/ui/select/select-content.svelte +38 -0
- package/src/lib/components/ui/select/select-group-heading.svelte +16 -0
- package/src/lib/components/ui/select/select-item.svelte +37 -0
- package/src/lib/components/ui/select/select-scroll-down-button.svelte +19 -0
- package/src/lib/components/ui/select/select-scroll-up-button.svelte +19 -0
- package/src/lib/components/ui/select/select-separator.svelte +13 -0
- package/src/lib/components/ui/select/select-trigger.svelte +24 -0
- package/src/lib/components/ui/separator/index.ts +7 -0
- package/src/lib/components/ui/separator/separator.svelte +22 -0
- package/src/lib/components/ui/skeleton/index.ts +7 -0
- package/src/lib/components/ui/skeleton/skeleton.svelte +22 -0
- package/src/lib/components/ui/sonner/index.ts +1 -0
- package/src/lib/components/ui/sonner/sonner.svelte +20 -0
- package/src/lib/components/ui/switch/index.ts +7 -0
- package/src/lib/components/ui/switch/switch.svelte +27 -0
- package/src/lib/components/ui/textarea/index.ts +7 -0
- package/src/lib/components/ui/textarea/textarea.svelte +22 -0
- package/src/lib/components/ui/tooltip/index.ts +18 -0
- package/src/lib/components/ui/tooltip/tooltip-content.svelte +21 -0
- package/src/lib/components/workflowEditor.svelte +187 -0
- package/src/lib/eventSystem.ts +38 -0
- package/src/lib/index.ts +40 -0
- package/src/lib/store.svelte.ts +21 -0
- package/src/lib/store.types.ts +28 -0
- package/src/lib/utils.ts +84 -0
- package/src/main.ts +18 -0
- package/src/routes/collections/collection.svelte +46 -0
- package/src/routes/collections/collections.svelte +43 -0
- package/src/routes/data_model/dataModel.svelte +40 -0
- package/src/routes/data_model/flow.css +22 -0
- package/src/routes/data_model/flow.svelte +82 -0
- package/src/routes/data_model/syncManager.svelte +93 -0
- package/src/routes/data_model/utils.ts +35 -0
- package/src/routes/extensions/extension.svelte +16 -0
- package/src/routes/home.svelte +36 -0
- package/src/routes/workflows/workflows.svelte +135 -0
- package/src/stories/Configure.mdx +364 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
- package/src/stories/examples/Button.stories.svelte +31 -0
- package/src/stories/examples/Button.svelte +30 -0
- package/src/stories/examples/Header.stories.svelte +26 -0
- package/src/stories/examples/Header.svelte +45 -0
- package/src/stories/examples/Page.stories.svelte +29 -0
- package/src/stories/examples/Page.svelte +70 -0
- package/src/stories/examples/button.css +30 -0
- package/src/stories/examples/header.css +32 -0
- package/src/stories/examples/page.css +68 -0
- package/src/vite-env.d.ts +2 -0
- package/svelte.config.js +7 -0
- package/todo.md +24 -0
- package/tsconfig.app.json +25 -0
- package/tsconfig.json +14 -0
- package/tsconfig.node.json +24 -0
- package/vite-plugin-contextual-lib.js +66 -0
- package/vite.build.svelte.config.ts +18 -0
- package/vite.config.ts +84 -0
- package/vite.extension.config.ts +81 -0
- package/vite_utils.ts +28 -0
- package/vitest.shims.d.ts +1 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CheckIcon from "@lucide/svelte/icons/check";
|
|
3
|
+
import ChevronsUpDownIcon from "@lucide/svelte/icons/chevrons-up-down";
|
|
4
|
+
import { tick } from "svelte";
|
|
5
|
+
import * as Command from "$lib/components/ui/command/index.js";
|
|
6
|
+
import * as Popover from "$lib/components/ui/popover/index.js";
|
|
7
|
+
import { Button } from "$lib/components/ui/button/index.js";
|
|
8
|
+
import { cn } from "$lib/utils.js";
|
|
9
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
10
|
+
|
|
11
|
+
interface Option {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
placeholder: string;
|
|
18
|
+
searchPlaceholder: string;
|
|
19
|
+
options: Option[];
|
|
20
|
+
value: string;
|
|
21
|
+
buttonClass?: HTMLButtonAttributes["class"];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
placeholder,
|
|
26
|
+
searchPlaceholder,
|
|
27
|
+
options,
|
|
28
|
+
value = $bindable(),
|
|
29
|
+
buttonClass,
|
|
30
|
+
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
let open = $state(false);
|
|
33
|
+
let triggerRef = $state<HTMLButtonElement>(null!);
|
|
34
|
+
|
|
35
|
+
const selectedValue = $derived(
|
|
36
|
+
options.find((option) => option.value === value)?.label,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// We want to refocus the trigger button when the user selects
|
|
40
|
+
// an item from the list so users can continue navigating the
|
|
41
|
+
// rest of the form with the keyboard.
|
|
42
|
+
function closeAndFocusTrigger() {
|
|
43
|
+
open = false;
|
|
44
|
+
tick().then(() => {
|
|
45
|
+
triggerRef.focus();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<Popover.Root bind:open>
|
|
51
|
+
<Popover.Trigger bind:ref={triggerRef}>
|
|
52
|
+
{#snippet child({ props })}
|
|
53
|
+
<Button
|
|
54
|
+
variant="outline"
|
|
55
|
+
class="w-full justify-between {buttonClass}"
|
|
56
|
+
{...props}
|
|
57
|
+
role="combobox"
|
|
58
|
+
aria-expanded={open}
|
|
59
|
+
>
|
|
60
|
+
{selectedValue || placeholder}
|
|
61
|
+
<ChevronsUpDownIcon class="opacity-50" />
|
|
62
|
+
</Button>
|
|
63
|
+
{/snippet}
|
|
64
|
+
</Popover.Trigger>
|
|
65
|
+
<Popover.Content class="w-[320px] p-0" align="start">
|
|
66
|
+
<Command.Root>
|
|
67
|
+
<Command.Input placeholder={searchPlaceholder} />
|
|
68
|
+
<Command.List>
|
|
69
|
+
<Command.Empty>No result found.</Command.Empty>
|
|
70
|
+
<Command.Group value="frameworks">
|
|
71
|
+
{#each options as option (option.value)}
|
|
72
|
+
<Command.Item
|
|
73
|
+
value={option.value}
|
|
74
|
+
onSelect={() => {
|
|
75
|
+
value = option.value;
|
|
76
|
+
closeAndFocusTrigger();
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<CheckIcon
|
|
80
|
+
class={cn(
|
|
81
|
+
value !== option.value &&
|
|
82
|
+
"text-transparent",
|
|
83
|
+
)}
|
|
84
|
+
/>
|
|
85
|
+
{option.label}
|
|
86
|
+
</Command.Item>
|
|
87
|
+
{/each}
|
|
88
|
+
</Command.Group>
|
|
89
|
+
</Command.List>
|
|
90
|
+
</Command.Root>
|
|
91
|
+
</Popover.Content>
|
|
92
|
+
</Popover.Root>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as AlertDialog from "$lib/components/ui/alert-dialog/index";
|
|
3
|
+
import { Check, X } from "lucide-svelte";
|
|
4
|
+
import Button from "../ui/button/button.svelte";
|
|
5
|
+
|
|
6
|
+
interface DialogProps {
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
onDecision: (result: boolean) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { title, description, onDecision }: DialogProps = $props();
|
|
13
|
+
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<AlertDialog.Root open={true}>
|
|
17
|
+
<AlertDialog.Content>
|
|
18
|
+
<AlertDialog.Header>
|
|
19
|
+
<AlertDialog.Title>{title}</AlertDialog.Title>
|
|
20
|
+
<AlertDialog.Description>
|
|
21
|
+
{description}
|
|
22
|
+
</AlertDialog.Description>
|
|
23
|
+
</AlertDialog.Header>
|
|
24
|
+
<AlertDialog.Footer>
|
|
25
|
+
<Button variant="outline" Icon={X} onclick={() => onDecision(false)}>
|
|
26
|
+
Cancel
|
|
27
|
+
</Button>
|
|
28
|
+
<Button Icon={Check} onclick={() => onDecision(true)}>
|
|
29
|
+
Continue
|
|
30
|
+
</Button>
|
|
31
|
+
</AlertDialog.Footer>
|
|
32
|
+
</AlertDialog.Content>
|
|
33
|
+
</AlertDialog.Root>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { mount, unmount } from "svelte";
|
|
2
|
+
import ConfirmationDialog from "./confirmationDialog.svelte";
|
|
3
|
+
|
|
4
|
+
export function showDialog(title: string, description: string): Promise<boolean> {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const targetElement = document.querySelector('main');
|
|
7
|
+
|
|
8
|
+
if (!targetElement) {
|
|
9
|
+
throw new Error("main html element doesn't exist for some reason");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const mountedElement = mount(ConfirmationDialog, {
|
|
13
|
+
target: targetElement,
|
|
14
|
+
props: {
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
onDecision: async (result: boolean) => {
|
|
18
|
+
resolve(result);
|
|
19
|
+
await unmount(mountedElement, {
|
|
20
|
+
outro: true
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
console.log("the dialog is mounted man")
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { fade, fly } from "svelte/transition";
|
|
3
|
+
import { ArrowLeft, Plus, X } from "lucide-svelte";
|
|
4
|
+
import Button, { type ButtonProps } from "./ui/button/button.svelte";
|
|
5
|
+
import { toast } from "svelte-sonner";
|
|
6
|
+
import MonacoEditor from "./monacoEditor.svelte";
|
|
7
|
+
import { lobb } from "$lib";
|
|
8
|
+
import { calculateDrawerWidth } from "$lib/utils";
|
|
9
|
+
import Drawer from "./drawer.svelte";
|
|
10
|
+
|
|
11
|
+
interface LocalProps extends ButtonProps {
|
|
12
|
+
collectionName: string;
|
|
13
|
+
onSuccessfullSave?: () => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { collectionName, onSuccessfullSave, ...rest }: LocalProps = $props();
|
|
17
|
+
|
|
18
|
+
let openDrawer = $state(false);
|
|
19
|
+
let createManyPayload = $state("");
|
|
20
|
+
|
|
21
|
+
async function handleCreateMany() {
|
|
22
|
+
const res = await lobb.createMany(
|
|
23
|
+
collectionName,
|
|
24
|
+
JSON.parse(createManyPayload),
|
|
25
|
+
);
|
|
26
|
+
if (res.status >= 400) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (onSuccessfullSave) {
|
|
30
|
+
await onSuccessfullSave();
|
|
31
|
+
}
|
|
32
|
+
hideDrawer();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function showDrawer() {
|
|
36
|
+
if (!collectionName) {
|
|
37
|
+
toast.error("No collection is selected");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
openDrawer = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function hideDrawer() {
|
|
44
|
+
openDrawer = false;
|
|
45
|
+
createManyPayload = "";
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<!-- THE SELECT BUTTON -->
|
|
50
|
+
<Button
|
|
51
|
+
variant={rest.variant}
|
|
52
|
+
class={rest.class}
|
|
53
|
+
Icon={rest.Icon}
|
|
54
|
+
onclick={showDrawer}
|
|
55
|
+
>
|
|
56
|
+
{#if rest.children}
|
|
57
|
+
{@render rest.children()}
|
|
58
|
+
{/if}
|
|
59
|
+
</Button>
|
|
60
|
+
|
|
61
|
+
<!-- THE SELECT DRAWER -->
|
|
62
|
+
{#if openDrawer}
|
|
63
|
+
<Drawer onHide={async () => hideDrawer()}>
|
|
64
|
+
<div class="flex h-12 items-center gap-4 border-b px-4">
|
|
65
|
+
<Button
|
|
66
|
+
variant="outline"
|
|
67
|
+
onclick={() => (openDrawer = false)}
|
|
68
|
+
class=" h-8 w-8 rounded-full text-xs font-normal"
|
|
69
|
+
Icon={ArrowLeft}
|
|
70
|
+
></Button>
|
|
71
|
+
<div class="flex items-center gap-2">
|
|
72
|
+
<div class="text-sm">Create Many On</div>
|
|
73
|
+
<span class="rounded-md border bg-muted px-2 py-0.5 text-sm">
|
|
74
|
+
{collectionName}
|
|
75
|
+
</span>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="flex-1 overflow-y-auto">
|
|
79
|
+
<MonacoEditor
|
|
80
|
+
name={collectionName}
|
|
81
|
+
type="json"
|
|
82
|
+
bind:value={createManyPayload}
|
|
83
|
+
class="h-full border-0"
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="flex h-12 items-center justify-end gap-2 border-t px-4">
|
|
87
|
+
<div class="flex gap-3">
|
|
88
|
+
<Button
|
|
89
|
+
variant="outline"
|
|
90
|
+
onclick={() => hideDrawer()}
|
|
91
|
+
class="h-7 px-3 text-xs font-normal"
|
|
92
|
+
Icon={X}
|
|
93
|
+
>
|
|
94
|
+
Cancel
|
|
95
|
+
</Button>
|
|
96
|
+
<Button
|
|
97
|
+
variant="default"
|
|
98
|
+
class="h-7 px-3 text-xs font-normal"
|
|
99
|
+
Icon={Plus}
|
|
100
|
+
onclick={handleCreateMany}
|
|
101
|
+
>
|
|
102
|
+
Create Many
|
|
103
|
+
</Button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</Drawer>
|
|
107
|
+
{/if}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ctx } from "$lib/store.svelte";
|
|
3
|
+
import { ChevronRight, Plus, Smartphone, Table } from "lucide-svelte";
|
|
4
|
+
import DataTable from "./dataTable.svelte";
|
|
5
|
+
import CreateDetailViewButton from "../detailView/create/createDetailViewButton.svelte";
|
|
6
|
+
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
7
|
+
import { getExtensionUtils } from "../../../extensions/extensionUtils";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
collectionName: string;
|
|
11
|
+
recordId: string;
|
|
12
|
+
width: number;
|
|
13
|
+
unifiedBgColor?: "bg-soft" | "bg-background";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { collectionName, recordId, width, unifiedBgColor }: Props = $props();
|
|
17
|
+
|
|
18
|
+
const relations = ctx.meta.relations.filter(
|
|
19
|
+
(relation) => relation.to.collection === collectionName,
|
|
20
|
+
);
|
|
21
|
+
let expandedRows: boolean[] = $state(
|
|
22
|
+
new Array(relations.length).fill(false),
|
|
23
|
+
);
|
|
24
|
+
let refreshDataTable = $state(true);
|
|
25
|
+
let tableHeaderWidth = $state(0);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<div class="flex" style="width: {width}px;">
|
|
29
|
+
<div
|
|
30
|
+
class="
|
|
31
|
+
flex justify-center border-r
|
|
32
|
+
{unifiedBgColor ? unifiedBgColor : 'bg-background'}
|
|
33
|
+
"
|
|
34
|
+
style="width: 40px"
|
|
35
|
+
></div>
|
|
36
|
+
<div class="flex-1 flex flex-col">
|
|
37
|
+
{#each relations as relation, index}
|
|
38
|
+
{@const lastRow = relations.length - 1 === index}
|
|
39
|
+
{@const fromCollection = relation.from.collection}
|
|
40
|
+
{@const fromField = relation.from.field}
|
|
41
|
+
<div
|
|
42
|
+
class="
|
|
43
|
+
overflow-hidden
|
|
44
|
+
{unifiedBgColor ? unifiedBgColor : 'bg-background'}
|
|
45
|
+
"
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
bind:clientWidth={tableHeaderWidth}
|
|
49
|
+
class="
|
|
50
|
+
flex justify-between items-center gap-2 text-sm h-10
|
|
51
|
+
{expandedRows[index] || !lastRow ? 'border-b' : ''}
|
|
52
|
+
"
|
|
53
|
+
>
|
|
54
|
+
<button
|
|
55
|
+
class="flex gap-2 px-2 flex-1 h-full items-center"
|
|
56
|
+
onclick={() => {
|
|
57
|
+
expandedRows[index] = !expandedRows[index];
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<ChevronRight
|
|
61
|
+
size="17.5"
|
|
62
|
+
class="text-muted-foreground transition-transform"
|
|
63
|
+
style={expandedRows[index]
|
|
64
|
+
? "transform: rotate(90deg);"
|
|
65
|
+
: "transform: rotate(0deg);"}
|
|
66
|
+
/>
|
|
67
|
+
<Table size="17.5" class="text-muted-foreground" />
|
|
68
|
+
<div class="text-muted-foreground">
|
|
69
|
+
{relation.from.collection}
|
|
70
|
+
</div>
|
|
71
|
+
</button>
|
|
72
|
+
<div class="flex items-center px-2">
|
|
73
|
+
<CreateDetailViewButton
|
|
74
|
+
collectionName={relation.from.collection}
|
|
75
|
+
variant="ghost"
|
|
76
|
+
class="h-7 px-3 text-xs font-normal"
|
|
77
|
+
Icon={Plus}
|
|
78
|
+
values={{
|
|
79
|
+
[fromField]: {
|
|
80
|
+
id: recordId,
|
|
81
|
+
},
|
|
82
|
+
}}
|
|
83
|
+
onSuccessfullSave={async () => {
|
|
84
|
+
refreshDataTable = !refreshDataTable;
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
Create
|
|
88
|
+
</CreateDetailViewButton>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
{#if expandedRows[index]}
|
|
92
|
+
<div
|
|
93
|
+
class="
|
|
94
|
+
flex max-h-96 overflow-auto
|
|
95
|
+
{lastRow ? '' : 'border-b'}
|
|
96
|
+
"
|
|
97
|
+
>
|
|
98
|
+
<div
|
|
99
|
+
class="border-r {unifiedBgColor
|
|
100
|
+
? unifiedBgColor
|
|
101
|
+
: ''}"
|
|
102
|
+
style="width: 100vw; max-width: 40px"
|
|
103
|
+
></div>
|
|
104
|
+
<div
|
|
105
|
+
class="flex-1"
|
|
106
|
+
style="width: {tableHeaderWidth - 40}px;"
|
|
107
|
+
>
|
|
108
|
+
{#key refreshDataTable}
|
|
109
|
+
<ExtensionsComponents
|
|
110
|
+
name="listView.entry.children.{fromCollection}"
|
|
111
|
+
collectionName={fromCollection}
|
|
112
|
+
filter={{
|
|
113
|
+
[fromField]: recordId,
|
|
114
|
+
}}
|
|
115
|
+
utils={getExtensionUtils()}
|
|
116
|
+
>
|
|
117
|
+
<DataTable
|
|
118
|
+
collectionName={fromCollection}
|
|
119
|
+
filter={{
|
|
120
|
+
[fromField]: recordId,
|
|
121
|
+
}}
|
|
122
|
+
showHeader={false}
|
|
123
|
+
showFooter={false}
|
|
124
|
+
showDelete={true}
|
|
125
|
+
{unifiedBgColor}
|
|
126
|
+
tableProps={{
|
|
127
|
+
showLastRowBorder: false,
|
|
128
|
+
showLastColumnBorder: false,
|
|
129
|
+
showCheckboxes: false,
|
|
130
|
+
}}
|
|
131
|
+
/>
|
|
132
|
+
</ExtensionsComponents>
|
|
133
|
+
{/key}
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
{/if}
|
|
137
|
+
</div>
|
|
138
|
+
{/each}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { lobb } from "$lib";
|
|
4
|
+
import Footer from "./footer.svelte";
|
|
5
|
+
import Header from "./header.svelte";
|
|
6
|
+
import Table, { type TableProps } from "./table.svelte";
|
|
7
|
+
import { getCollectionColumns, getCollectionParamsFields } from "./utils";
|
|
8
|
+
import { Pencil, Trash } from "lucide-svelte";
|
|
9
|
+
import * as icons from "lucide-svelte";
|
|
10
|
+
import ChildRecords from "./childRecords.svelte";
|
|
11
|
+
import FieldCell from "./fieldCell.svelte";
|
|
12
|
+
import Skeleton from "../ui/skeleton/skeleton.svelte";
|
|
13
|
+
import { ctx } from "$lib/store.svelte";
|
|
14
|
+
import Button from "../ui/button/button.svelte";
|
|
15
|
+
import { showDialog } from "../confirmationDialog/store.svelte";
|
|
16
|
+
import UpdateDetailViewButton from "../detailView/update/updateDetailViewButton.svelte";
|
|
17
|
+
import { emitEvent } from "$lib/eventSystem";
|
|
18
|
+
import type { Snippet } from "svelte";
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
collectionName: string;
|
|
22
|
+
filter?: any;
|
|
23
|
+
showHeader?: boolean;
|
|
24
|
+
showFooter?: boolean;
|
|
25
|
+
unifiedBgColor?: "bg-soft" | "bg-background";
|
|
26
|
+
showDelete?: boolean;
|
|
27
|
+
tableProps?: Partial<TableProps>;
|
|
28
|
+
headerLeft?: Snippet<[]>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
collectionName,
|
|
33
|
+
filter,
|
|
34
|
+
showHeader = true,
|
|
35
|
+
showFooter = true,
|
|
36
|
+
unifiedBgColor,
|
|
37
|
+
showDelete = false,
|
|
38
|
+
tableProps,
|
|
39
|
+
headerLeft,
|
|
40
|
+
}: Props = $props();
|
|
41
|
+
|
|
42
|
+
const fields = getCollectionParamsFields(collectionName);
|
|
43
|
+
let params = $state({
|
|
44
|
+
fields: fields,
|
|
45
|
+
filter: filter ?? {},
|
|
46
|
+
sort: {},
|
|
47
|
+
limit: "100",
|
|
48
|
+
page: 1,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
let selectedRecords = $state([]);
|
|
52
|
+
let totalCount = $state(0);
|
|
53
|
+
let data: TableProps["data"] = $state([]);
|
|
54
|
+
let loading = $state(true);
|
|
55
|
+
const columns: TableProps["columns"] = $state(
|
|
56
|
+
getCollectionColumns(collectionName),
|
|
57
|
+
);
|
|
58
|
+
let dataTableContainerWidth: number = $state(0);
|
|
59
|
+
let dataTableWidth: number = $state(0);
|
|
60
|
+
const doesCollectionHasChildren = Boolean(
|
|
61
|
+
ctx.meta.relations.find(
|
|
62
|
+
(relation) => relation.to.collection === collectionName,
|
|
63
|
+
),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// requests the data from the server when the params is changed
|
|
67
|
+
$effect(() => {
|
|
68
|
+
loadData(params);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
async function loadData(params: any) {
|
|
72
|
+
// parsing sort before sending the request
|
|
73
|
+
const paramsCopy = $state.snapshot(params);
|
|
74
|
+
const sort: TableProps["sort"] = paramsCopy.sort;
|
|
75
|
+
const sortStrings: string[] = [];
|
|
76
|
+
if (sort) {
|
|
77
|
+
for (const [key, value] of Object.entries(sort)) {
|
|
78
|
+
sortStrings.push(`${value === "asc" ? "" : "-"}${key}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
paramsCopy.sort = sortStrings.join(",");
|
|
82
|
+
|
|
83
|
+
// sending the request
|
|
84
|
+
const response = await lobb.findAll(collectionName, paramsCopy);
|
|
85
|
+
const res = await response.json();
|
|
86
|
+
|
|
87
|
+
data = res.data;
|
|
88
|
+
totalCount = res.meta.totalCount;
|
|
89
|
+
|
|
90
|
+
loading = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function handleDelete(entryId: string) {
|
|
94
|
+
const result = await showDialog(
|
|
95
|
+
"Are you sure?",
|
|
96
|
+
"This will delete the record you selected.",
|
|
97
|
+
);
|
|
98
|
+
if (result) {
|
|
99
|
+
await lobb.deleteOne(collectionName, entryId);
|
|
100
|
+
params = { ...params };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function getWorkflowTools(
|
|
105
|
+
entry: Record<string, any>,
|
|
106
|
+
): Promise<any[]> {
|
|
107
|
+
// TODO: instead of firing the events like this. get them all the fire them one by one to get their results
|
|
108
|
+
const eventResult = await emitEvent(
|
|
109
|
+
"studio.collections.listView.tools",
|
|
110
|
+
{
|
|
111
|
+
collectionName,
|
|
112
|
+
entry,
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
if (eventResult) {
|
|
117
|
+
return eventResult.tools;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<div
|
|
125
|
+
bind:clientWidth={dataTableContainerWidth}
|
|
126
|
+
class="
|
|
127
|
+
flex flex-col overflow-auto h-full w-full
|
|
128
|
+
{unifiedBgColor ? unifiedBgColor : ''}
|
|
129
|
+
"
|
|
130
|
+
>
|
|
131
|
+
{#if showHeader}
|
|
132
|
+
<Header bind:params {collectionName} bind:selectedRecords>
|
|
133
|
+
{#snippet left()}
|
|
134
|
+
{@render headerLeft?.()}
|
|
135
|
+
{/snippet}
|
|
136
|
+
</Header>
|
|
137
|
+
{/if}
|
|
138
|
+
<div class="relative flex-1 overflow-auto w-full">
|
|
139
|
+
{#if loading}
|
|
140
|
+
<div class="flex flex-col gap-2 p-2 w-full">
|
|
141
|
+
<Skeleton class="h-8 w-full" />
|
|
142
|
+
<Skeleton class="h-8 w-[80%]" />
|
|
143
|
+
<Skeleton class="h-8 w-[60%]" />
|
|
144
|
+
</div>
|
|
145
|
+
{:else}
|
|
146
|
+
<Table
|
|
147
|
+
{data}
|
|
148
|
+
{columns}
|
|
149
|
+
showCollapsible={doesCollectionHasChildren}
|
|
150
|
+
selectByColumn="id"
|
|
151
|
+
showLastRowBorder={true}
|
|
152
|
+
showLastColumnBorder={true}
|
|
153
|
+
bind:sort={params.sort}
|
|
154
|
+
bind:selectedRecords
|
|
155
|
+
{unifiedBgColor}
|
|
156
|
+
bind:tableWidth={dataTableWidth}
|
|
157
|
+
{...tableProps}
|
|
158
|
+
>
|
|
159
|
+
{#snippet tools(entry)}
|
|
160
|
+
<UpdateDetailViewButton
|
|
161
|
+
{collectionName}
|
|
162
|
+
recordId={entry.id}
|
|
163
|
+
variant="ghost"
|
|
164
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
165
|
+
Icon={Pencil}
|
|
166
|
+
onSuccessfullSave={async () => {
|
|
167
|
+
params = { ...params };
|
|
168
|
+
}}
|
|
169
|
+
></UpdateDetailViewButton>
|
|
170
|
+
{#if showDelete}
|
|
171
|
+
<Button
|
|
172
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
173
|
+
variant="ghost"
|
|
174
|
+
size="icon"
|
|
175
|
+
onclick={() => handleDelete(entry.id)}
|
|
176
|
+
Icon={Trash}
|
|
177
|
+
></Button>
|
|
178
|
+
{/if}
|
|
179
|
+
{#await getWorkflowTools($state.snapshot(entry))}
|
|
180
|
+
<div></div>
|
|
181
|
+
{:then workflowTools}
|
|
182
|
+
{#each workflowTools as workflowTool}
|
|
183
|
+
<Button
|
|
184
|
+
variant="ghost"
|
|
185
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
186
|
+
Icon={icons[
|
|
187
|
+
workflowTool.icon as keyof typeof icons
|
|
188
|
+
]}
|
|
189
|
+
onclick={workflowTool.onclick}
|
|
190
|
+
></Button>
|
|
191
|
+
{/each}
|
|
192
|
+
{/await}
|
|
193
|
+
{/snippet}
|
|
194
|
+
{#snippet overrideCell(value, column, entry)}
|
|
195
|
+
<FieldCell
|
|
196
|
+
{collectionName}
|
|
197
|
+
fieldName={column.id}
|
|
198
|
+
{value}
|
|
199
|
+
{entry}
|
|
200
|
+
tableParams={params}
|
|
201
|
+
/>
|
|
202
|
+
{/snippet}
|
|
203
|
+
{#snippet collapsible(entry)}
|
|
204
|
+
<ChildRecords
|
|
205
|
+
{collectionName}
|
|
206
|
+
recordId={entry.id}
|
|
207
|
+
width={dataTableWidth > dataTableContainerWidth
|
|
208
|
+
? dataTableContainerWidth
|
|
209
|
+
: dataTableWidth}
|
|
210
|
+
unifiedBgColor={unifiedBgColor ?? "bg-background"}
|
|
211
|
+
/>
|
|
212
|
+
{/snippet}
|
|
213
|
+
</Table>
|
|
214
|
+
{/if}
|
|
215
|
+
</div>
|
|
216
|
+
{#if showFooter}
|
|
217
|
+
<Footer
|
|
218
|
+
bind:currentPage={params.page}
|
|
219
|
+
bind:limit={params.limit}
|
|
220
|
+
{totalCount}
|
|
221
|
+
/>
|
|
222
|
+
{/if}
|
|
223
|
+
</div>
|