@lobb-js/studio 0.7.1 → 0.7.3
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/package.json +3 -2
- package/src/App.svelte +5 -0
- package/src/app.css +124 -0
- package/src/lib/components/LlmButton.svelte +137 -0
- package/src/lib/components/Studio.svelte +129 -0
- package/src/lib/components/alertView.svelte +20 -0
- package/src/lib/components/breadCrumbs.svelte +60 -0
- package/src/lib/components/codeEditor.svelte +152 -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 +109 -0
- package/src/lib/components/dataTable/childRecords.svelte +142 -0
- package/src/lib/components/dataTable/dataTable.svelte +225 -0
- package/src/lib/components/dataTable/fieldCell.svelte +77 -0
- package/src/lib/components/dataTable/filter.svelte +284 -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 +155 -0
- package/src/lib/components/dataTable/sort.svelte +173 -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 +70 -0
- package/src/lib/components/detailView/create/createDetailView.svelte +228 -0
- package/src/lib/components/detailView/create/createDetailViewButton.svelte +37 -0
- package/src/lib/components/detailView/create/createManyView.svelte +252 -0
- package/src/lib/components/detailView/create/subRecords.svelte +50 -0
- package/src/lib/components/detailView/detailViewForm.svelte +104 -0
- package/src/lib/components/detailView/fieldCustomInput.svelte +26 -0
- package/src/lib/components/detailView/fieldInput.svelte +258 -0
- package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
- package/src/lib/components/detailView/store.svelte.ts +59 -0
- package/src/lib/components/detailView/update/children.svelte +96 -0
- package/src/lib/components/detailView/update/updateDetailView.svelte +176 -0
- package/src/lib/components/detailView/update/updateDetailViewButton.svelte +56 -0
- package/src/lib/components/detailView/utils.ts +176 -0
- package/src/lib/components/diffViewer.svelte +105 -0
- package/src/lib/components/drawer.svelte +28 -0
- package/src/lib/components/extensionsComponents.svelte +33 -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 +226 -0
- package/src/lib/components/rangeCalendarButton.svelte +257 -0
- package/src/lib/components/richTextEditor.svelte +284 -0
- package/src/lib/components/routes/collections/collection.svelte +57 -0
- package/src/lib/components/routes/collections/collections.svelte +45 -0
- package/src/lib/components/routes/data_model/dataModel.svelte +40 -0
- package/src/lib/components/routes/data_model/flow.css +22 -0
- package/src/lib/components/routes/data_model/flow.svelte +84 -0
- package/src/lib/components/routes/data_model/syncManager.svelte +94 -0
- package/src/lib/components/routes/data_model/utils.ts +35 -0
- package/src/lib/components/routes/extensions/extension.svelte +19 -0
- package/src/lib/components/routes/home.svelte +40 -0
- package/src/lib/components/routes/workflows/workflows.svelte +136 -0
- package/src/lib/components/selectRecord.svelte +130 -0
- package/src/lib/components/setServerPage.svelte +50 -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 +71 -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 +188 -0
- package/src/lib/context.ts +22 -0
- package/src/lib/eventSystem.ts +40 -0
- package/src/lib/extensions/extension.types.ts +92 -0
- package/src/lib/extensions/extensionUtils.ts +156 -0
- package/src/lib/index.ts +24 -0
- package/src/lib/store.svelte.ts +13 -0
- package/src/lib/store.types.ts +28 -0
- package/src/lib/utils.ts +68 -0
- package/src/main.ts +18 -0
- package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
- package/src/vite-env.d.ts +2 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Input from "./ui/input/input.svelte";
|
|
3
|
+
import SelectRecord from "./selectRecord.svelte";
|
|
4
|
+
import UpdateDetailViewButton from "./detailView/update/updateDetailViewButton.svelte";
|
|
5
|
+
import { ExternalLink } from "lucide-svelte";
|
|
6
|
+
|
|
7
|
+
interface LocalProps {
|
|
8
|
+
parentCollectionName: string;
|
|
9
|
+
collectionName: string;
|
|
10
|
+
fieldName: string;
|
|
11
|
+
value?: any;
|
|
12
|
+
destructive?: boolean;
|
|
13
|
+
entry: Record<string, any>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
parentCollectionName,
|
|
18
|
+
collectionName,
|
|
19
|
+
fieldName,
|
|
20
|
+
value = $bindable(),
|
|
21
|
+
destructive,
|
|
22
|
+
entry,
|
|
23
|
+
}: LocalProps = $props();
|
|
24
|
+
|
|
25
|
+
const idIsZero = $derived(value?.id === 0);
|
|
26
|
+
const refrenceId = $derived(value ? value.id : null);
|
|
27
|
+
const primaryField = $derived(value ? Object.values(value)[1] : null);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<!-- THE SELECT BUTTON -->
|
|
31
|
+
{#if !idIsZero}
|
|
32
|
+
<div class="relative">
|
|
33
|
+
<div
|
|
34
|
+
class="flex gap-2 absolute right-0 top-0 mr-9 h-full items-center text-xs"
|
|
35
|
+
>
|
|
36
|
+
{#if value !== null}
|
|
37
|
+
<UpdateDetailViewButton
|
|
38
|
+
collectionName={collectionName}
|
|
39
|
+
recordId={value.id}
|
|
40
|
+
variant="ghost"
|
|
41
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
42
|
+
Icon={ExternalLink}
|
|
43
|
+
></UpdateDetailViewButton>
|
|
44
|
+
{/if}
|
|
45
|
+
{#if primaryField}
|
|
46
|
+
<div
|
|
47
|
+
class="flex items-center bg-background rounded-full border h-6 px-3 shadow-sm"
|
|
48
|
+
>
|
|
49
|
+
{primaryField}
|
|
50
|
+
</div>
|
|
51
|
+
{/if}
|
|
52
|
+
<SelectRecord
|
|
53
|
+
class="h-6 px-2 font-normal text-xs"
|
|
54
|
+
variant="outline"
|
|
55
|
+
{parentCollectionName}
|
|
56
|
+
{collectionName}
|
|
57
|
+
{fieldName}
|
|
58
|
+
bind:value
|
|
59
|
+
{entry}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
<Input
|
|
63
|
+
placeholder={"NULL"}
|
|
64
|
+
type="number"
|
|
65
|
+
class="
|
|
66
|
+
bg-muted/30 text-xs
|
|
67
|
+
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
68
|
+
"
|
|
69
|
+
bind:value={() => refrenceId, (v) => (value.id = v)}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
{:else}
|
|
73
|
+
<div class="relative z-10">
|
|
74
|
+
<Input
|
|
75
|
+
placeholder={"PARENT ID"}
|
|
76
|
+
class="bg-muted/30 text-xs"
|
|
77
|
+
disabled={true}
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
{/if}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import * as Tooltip from "./ui/tooltip";
|
|
3
|
+
import Button from "./ui/button/button.svelte";
|
|
4
|
+
import { Menu, Moon, Sun } from "lucide-svelte";
|
|
5
|
+
import BreadCrumbs from "./breadCrumbs.svelte";
|
|
6
|
+
import { toggleMode, mode } from "mode-watcher";
|
|
7
|
+
import { mediaQueries } from "../utils";
|
|
8
|
+
import { expandMiniSideBar } from "./miniSidebar.svelte";
|
|
9
|
+
|
|
10
|
+
let isSmallScreen = $derived(!mediaQueries.sm.current);
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
class="flex items-center justify-between border-b border-input bg-background px-3"
|
|
15
|
+
>
|
|
16
|
+
<div class="flex items-center gap-4">
|
|
17
|
+
{#if isSmallScreen}
|
|
18
|
+
<Menu
|
|
19
|
+
class="h-10 text-muted-foreground hover:bg-transparent cursor-pointer hover:text-foreground"
|
|
20
|
+
style="left: 0.6rem; top: 0rem;"
|
|
21
|
+
size="18"
|
|
22
|
+
onclick={() => expandMiniSideBar()}
|
|
23
|
+
/>
|
|
24
|
+
{/if}
|
|
25
|
+
<BreadCrumbs />
|
|
26
|
+
</div>
|
|
27
|
+
<div class="flex h-full items-center gap-3">
|
|
28
|
+
<Tooltip.Root>
|
|
29
|
+
<Tooltip.Trigger>
|
|
30
|
+
<Button
|
|
31
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
32
|
+
variant="ghost"
|
|
33
|
+
size="icon"
|
|
34
|
+
onclick={toggleMode}
|
|
35
|
+
Icon={$mode === "light" ? Moon : Sun}
|
|
36
|
+
></Button>
|
|
37
|
+
</Tooltip.Trigger>
|
|
38
|
+
<Tooltip.Content side="bottom" sideOffset={7.5}
|
|
39
|
+
>{$mode === "light"
|
|
40
|
+
? "Night Mode"
|
|
41
|
+
: "Light Mode"}</Tooltip.Content
|
|
42
|
+
>
|
|
43
|
+
</Tooltip.Root>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO
|
|
3
|
+
*
|
|
4
|
+
* this file shows an example of downloading the types files of a library from npm to use them in adding type support of that library in the vscode editor
|
|
5
|
+
* so the whole point is that for users to be able to paste a url from npm or jsr of a library and have all ts support from its types
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { gunzipSync } from "fflate";
|
|
9
|
+
import { untar } from "@andrewbranch/untar.js";
|
|
10
|
+
|
|
11
|
+
async function extractTgz(buffer: ArrayBuffer) {
|
|
12
|
+
const tarBuffer = gunzipSync(new Uint8Array(buffer));
|
|
13
|
+
|
|
14
|
+
const cleanBuffer = tarBuffer.buffer.slice(
|
|
15
|
+
tarBuffer.byteOffset,
|
|
16
|
+
tarBuffer.byteOffset + tarBuffer.byteLength,
|
|
17
|
+
);
|
|
18
|
+
const files = await untar(cleanBuffer as ArrayBuffer);
|
|
19
|
+
|
|
20
|
+
const dtsFiles = [];
|
|
21
|
+
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
if (file.filename.endsWith(".d.ts")) {
|
|
24
|
+
const content = new TextDecoder().decode(file.fileData);
|
|
25
|
+
dtsFiles.push({ name: file.filename, content });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return dtsFiles;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const tgzURL = "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz";
|
|
33
|
+
const response = await fetch(tgzURL);
|
|
34
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
35
|
+
|
|
36
|
+
const files = await extractTgz(arrayBuffer);
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
let isSmallScreen = $derived(!mediaQueries.sm.current);
|
|
3
|
+
let isCollapsed = $derived(isSmallScreen);
|
|
4
|
+
|
|
5
|
+
export let collapseMiniSideBar = () => {
|
|
6
|
+
isCollapsed = true;
|
|
7
|
+
};
|
|
8
|
+
export let expandMiniSideBar = () => {
|
|
9
|
+
isCollapsed = false;
|
|
10
|
+
};
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { House, Layers, Library, Workflow, X } from "lucide-svelte";
|
|
15
|
+
import Button from "../components/ui/button/button.svelte";
|
|
16
|
+
import Separator from "../components/ui/separator/separator.svelte";
|
|
17
|
+
import * as Tooltip from "../components/ui/tooltip";
|
|
18
|
+
import * as Accordion from "../components/ui/accordion/index.js";
|
|
19
|
+
|
|
20
|
+
import { getStudioContext } from "../context";
|
|
21
|
+
import { getDashboardNavs } from "../extensions/extensionUtils";
|
|
22
|
+
|
|
23
|
+
const { ctx } = getStudioContext();
|
|
24
|
+
import { mediaQueries } from "../utils";
|
|
25
|
+
import * as Popover from "./ui/popover";
|
|
26
|
+
import { location } from "@wjfe/n-savant";
|
|
27
|
+
|
|
28
|
+
const sections: any = [
|
|
29
|
+
[
|
|
30
|
+
{
|
|
31
|
+
label: "Home",
|
|
32
|
+
href: "/studio",
|
|
33
|
+
icon: House,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: "Collections",
|
|
37
|
+
href: "/studio/collections",
|
|
38
|
+
icon: Library,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: "Data Model",
|
|
42
|
+
href: "/studio/datamodel/graph",
|
|
43
|
+
icon: Layers,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: "Workflows",
|
|
47
|
+
href: "/studio/workflows",
|
|
48
|
+
icon: Workflow,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
[],
|
|
52
|
+
[],
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const navs = getDashboardNavs(ctx);
|
|
56
|
+
|
|
57
|
+
if (navs.top) {
|
|
58
|
+
sections[0] = [...sections[0], ...navs.top];
|
|
59
|
+
}
|
|
60
|
+
if (navs.middle) {
|
|
61
|
+
sections[1] = [...sections[1], ...navs.middle];
|
|
62
|
+
}
|
|
63
|
+
if (navs.bottom) {
|
|
64
|
+
sections[2] = [...sections[2], ...navs.bottom];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
{#snippet section(section: any)}
|
|
70
|
+
<div class="flex flex-col {isSmallScreen ? 'gap-0' : 'gap-2'}">
|
|
71
|
+
{#each section as item}
|
|
72
|
+
{#if isSmallScreen}
|
|
73
|
+
{#if !item.navs}
|
|
74
|
+
<Button
|
|
75
|
+
onclick={() => {
|
|
76
|
+
if (item.onclick) {
|
|
77
|
+
item.onclick();
|
|
78
|
+
} else {
|
|
79
|
+
location.navigate(item.href);
|
|
80
|
+
}
|
|
81
|
+
isCollapsed = true;
|
|
82
|
+
}}
|
|
83
|
+
class="flex items-center justify-start flex-nowrap text-muted-foreground text-nowrap h-10 w-full"
|
|
84
|
+
variant="ghost"
|
|
85
|
+
size="icon"
|
|
86
|
+
Icon={item.icon}
|
|
87
|
+
>
|
|
88
|
+
{item.label}
|
|
89
|
+
</Button>
|
|
90
|
+
{:else}
|
|
91
|
+
<Accordion.Root type="single">
|
|
92
|
+
<Accordion.Item class="border-b-0">
|
|
93
|
+
<Accordion.Trigger class="justify-between p-0 h-10">
|
|
94
|
+
<div
|
|
95
|
+
class="flex items-center gap-2 text-muted-foreground"
|
|
96
|
+
>
|
|
97
|
+
<item.icon size="18" />
|
|
98
|
+
<div class="text-nowrap">{item.label}</div>
|
|
99
|
+
</div>
|
|
100
|
+
</Accordion.Trigger>
|
|
101
|
+
<Accordion.Content class="pl-2 border-l">
|
|
102
|
+
{#each item.navs as childItem}
|
|
103
|
+
<Button
|
|
104
|
+
onclick={() => {
|
|
105
|
+
if (childItem.onclick) {
|
|
106
|
+
childItem.onclick();
|
|
107
|
+
} else {
|
|
108
|
+
location.navigate(item.href);
|
|
109
|
+
}
|
|
110
|
+
isCollapsed = true;
|
|
111
|
+
}}
|
|
112
|
+
class="flex items-center justify-start flex-nowrap text-muted-foreground text-nowrap h-8 w-full"
|
|
113
|
+
variant="ghost"
|
|
114
|
+
size="icon"
|
|
115
|
+
Icon={childItem.icon}
|
|
116
|
+
>
|
|
117
|
+
{childItem.label}
|
|
118
|
+
</Button>
|
|
119
|
+
{/each}
|
|
120
|
+
</Accordion.Content>
|
|
121
|
+
</Accordion.Item>
|
|
122
|
+
</Accordion.Root>
|
|
123
|
+
{/if}
|
|
124
|
+
{:else}
|
|
125
|
+
<Tooltip.Root>
|
|
126
|
+
<Tooltip.Trigger>
|
|
127
|
+
{#if !item.navs}
|
|
128
|
+
<Button
|
|
129
|
+
onclick={() => {
|
|
130
|
+
if (item.onclick) {
|
|
131
|
+
item.onclick();
|
|
132
|
+
}
|
|
133
|
+
isCollapsed = true;
|
|
134
|
+
}}
|
|
135
|
+
href={item.href}
|
|
136
|
+
class="text-muted-foreground"
|
|
137
|
+
variant="ghost"
|
|
138
|
+
size="icon"
|
|
139
|
+
Icon={item.icon}
|
|
140
|
+
></Button>
|
|
141
|
+
{:else}
|
|
142
|
+
<Popover.Root>
|
|
143
|
+
<Popover.Trigger>
|
|
144
|
+
<Button
|
|
145
|
+
class="text-muted-foreground"
|
|
146
|
+
variant="ghost"
|
|
147
|
+
size="icon"
|
|
148
|
+
Icon={item.icon}
|
|
149
|
+
></Button>
|
|
150
|
+
</Popover.Trigger>
|
|
151
|
+
<Popover.Content
|
|
152
|
+
sideOffset={17.5}
|
|
153
|
+
side="right"
|
|
154
|
+
class="popover_content w-60 mb-4 p-0"
|
|
155
|
+
>
|
|
156
|
+
<div class="py-1">
|
|
157
|
+
{#each item.navs as childItem}
|
|
158
|
+
<div
|
|
159
|
+
class="px-1 text-xs text-muted-foreground"
|
|
160
|
+
>
|
|
161
|
+
<Button
|
|
162
|
+
variant="ghost"
|
|
163
|
+
class="flex h-7 w-full justify-start p-2 text-xs font-normal text-muted-foreground"
|
|
164
|
+
Icon={childItem.icon}
|
|
165
|
+
onclick={() => {
|
|
166
|
+
if (childItem.onclick) {
|
|
167
|
+
childItem.onclick();
|
|
168
|
+
}
|
|
169
|
+
}}
|
|
170
|
+
href={childItem.href}
|
|
171
|
+
>
|
|
172
|
+
{childItem.label}
|
|
173
|
+
</Button>
|
|
174
|
+
</div>
|
|
175
|
+
{/each}
|
|
176
|
+
</div>
|
|
177
|
+
</Popover.Content>
|
|
178
|
+
</Popover.Root>
|
|
179
|
+
{/if}
|
|
180
|
+
</Tooltip.Trigger>
|
|
181
|
+
<Tooltip.Content side="right" sideOffset={15}>
|
|
182
|
+
{item.label}
|
|
183
|
+
</Tooltip.Content>
|
|
184
|
+
</Tooltip.Root>
|
|
185
|
+
{/if}
|
|
186
|
+
{/each}
|
|
187
|
+
</div>
|
|
188
|
+
{/snippet}
|
|
189
|
+
|
|
190
|
+
<div
|
|
191
|
+
class="
|
|
192
|
+
{isSmallScreen ? 'fixed top-0 left-0 h-full z-50' : 'relative'}
|
|
193
|
+
border-r bg-background w-screen
|
|
194
|
+
{isCollapsed
|
|
195
|
+
? 'max-w-0 p-0'
|
|
196
|
+
: `max-w-14 ${isSmallScreen ? 'px-3 pb-3' : 'p-2'}`}"
|
|
197
|
+
style="transition: max-width 150ms, padding 150ms; {isSmallScreen &&
|
|
198
|
+
!isCollapsed
|
|
199
|
+
? 'max-width: 100vw'
|
|
200
|
+
: ''}"
|
|
201
|
+
>
|
|
202
|
+
{#if isSmallScreen}
|
|
203
|
+
{#if !isCollapsed}
|
|
204
|
+
<X
|
|
205
|
+
class="absolute h-10 text-muted-foreground hover:bg-transparent cursor-pointer hover:text-foreground"
|
|
206
|
+
style="left: 0.6rem; top: 0rem;"
|
|
207
|
+
size="18"
|
|
208
|
+
onclick={() => (isCollapsed = !isCollapsed)}
|
|
209
|
+
/>
|
|
210
|
+
{/if}
|
|
211
|
+
{/if}
|
|
212
|
+
<div
|
|
213
|
+
class="flex h-full flex-col justify-between gap-2 w-full overflow-hidden"
|
|
214
|
+
>
|
|
215
|
+
<!-- upper part -->
|
|
216
|
+
<div class="flex flex-col gap-2 {isSmallScreen ? 'pt-8' : ''}">
|
|
217
|
+
{@render section(sections[0])}
|
|
218
|
+
<Separator />
|
|
219
|
+
{@render section(sections[1])}
|
|
220
|
+
</div>
|
|
221
|
+
<div class="flex flex-col gap-2">
|
|
222
|
+
<Separator />
|
|
223
|
+
{@render section(sections[2])}
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CalendarIcon from "@lucide/svelte/icons/calendar";
|
|
3
|
+
import type { DateRange } from "bits-ui";
|
|
4
|
+
import {
|
|
5
|
+
CalendarDate,
|
|
6
|
+
DateFormatter,
|
|
7
|
+
type DateValue,
|
|
8
|
+
getLocalTimeZone,
|
|
9
|
+
parseDate,
|
|
10
|
+
startOfWeek,
|
|
11
|
+
today,
|
|
12
|
+
} from "@internationalized/date";
|
|
13
|
+
import { cn } from "../utils.js";
|
|
14
|
+
import { buttonVariants } from "../components/ui/button/index.js";
|
|
15
|
+
import { RangeCalendar } from "../components/ui/range-calendar/index.js";
|
|
16
|
+
import * as Popover from "../components/ui/popover/index.js";
|
|
17
|
+
import Input from "./ui/input/input.svelte";
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
value: DateRange;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let { value = $bindable() }: Props = $props();
|
|
24
|
+
|
|
25
|
+
const df = new DateFormatter(navigator.language, {
|
|
26
|
+
dateStyle: "medium",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let startValue: DateValue | undefined = $state(undefined);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="grid gap-2">
|
|
33
|
+
<Popover.Root>
|
|
34
|
+
<Popover.Trigger
|
|
35
|
+
class={cn(
|
|
36
|
+
buttonVariants({ variant: "outline" }),
|
|
37
|
+
!value && "text-muted-foreground",
|
|
38
|
+
"h-7 px-3 text-xs font-normal",
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
<CalendarIcon class="mr-2 size-4" />
|
|
42
|
+
{#if value && value.start}
|
|
43
|
+
{#if value.end}
|
|
44
|
+
{df.format(value.start.toDate(getLocalTimeZone()))} - {df.format(
|
|
45
|
+
value.end.toDate(getLocalTimeZone()),
|
|
46
|
+
)}
|
|
47
|
+
{:else}
|
|
48
|
+
{df.format(value.start.toDate(getLocalTimeZone()))}
|
|
49
|
+
{/if}
|
|
50
|
+
{:else if startValue}
|
|
51
|
+
{df.format(startValue.toDate(getLocalTimeZone()))}
|
|
52
|
+
{:else}
|
|
53
|
+
Pick a date
|
|
54
|
+
{/if}
|
|
55
|
+
</Popover.Trigger>
|
|
56
|
+
<Popover.Content class="flex w-auto p-0" align="start">
|
|
57
|
+
<div class="flex flex-col border-r">
|
|
58
|
+
<div
|
|
59
|
+
class="flex flex-col overflow-hidden text-muted-foreground"
|
|
60
|
+
>
|
|
61
|
+
<button
|
|
62
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
63
|
+
onclick={() => {
|
|
64
|
+
const currentDate = today(getLocalTimeZone());
|
|
65
|
+
value = {
|
|
66
|
+
start: currentDate,
|
|
67
|
+
end: currentDate,
|
|
68
|
+
};
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
Today
|
|
72
|
+
</button>
|
|
73
|
+
<button
|
|
74
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
75
|
+
onclick={() => {
|
|
76
|
+
const currentDate = today(getLocalTimeZone());
|
|
77
|
+
value = {
|
|
78
|
+
start: currentDate.subtract({ days: 1 }),
|
|
79
|
+
end: currentDate.subtract({ days: 1 }),
|
|
80
|
+
};
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
Yesterday
|
|
84
|
+
</button>
|
|
85
|
+
<button
|
|
86
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
87
|
+
onclick={() => {
|
|
88
|
+
const currentDate = today(getLocalTimeZone());
|
|
89
|
+
const weekStart = startOfWeek(currentDate, "en-US");
|
|
90
|
+
value = {
|
|
91
|
+
start: weekStart,
|
|
92
|
+
end: currentDate,
|
|
93
|
+
};
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
This week (Sun - Today)
|
|
97
|
+
</button>
|
|
98
|
+
<button
|
|
99
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
100
|
+
onclick={() => {
|
|
101
|
+
const currentDate = today(getLocalTimeZone());
|
|
102
|
+
const thisWeekStart = startOfWeek(
|
|
103
|
+
currentDate,
|
|
104
|
+
"en-US",
|
|
105
|
+
);
|
|
106
|
+
const lastWeekEnd = thisWeekStart.subtract({
|
|
107
|
+
days: 1,
|
|
108
|
+
});
|
|
109
|
+
const lastWeekStart = startOfWeek(
|
|
110
|
+
lastWeekEnd,
|
|
111
|
+
"en-US",
|
|
112
|
+
);
|
|
113
|
+
value = {
|
|
114
|
+
start: lastWeekStart,
|
|
115
|
+
end: lastWeekEnd,
|
|
116
|
+
};
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
Last week (Sun - Sat)
|
|
120
|
+
</button>
|
|
121
|
+
<button
|
|
122
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
123
|
+
onclick={() => {
|
|
124
|
+
const currentDate = today(getLocalTimeZone());
|
|
125
|
+
value = {
|
|
126
|
+
start: currentDate.subtract({ days: 6 }),
|
|
127
|
+
end: currentDate,
|
|
128
|
+
};
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
Last 7 days
|
|
132
|
+
</button>
|
|
133
|
+
<button
|
|
134
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
135
|
+
onclick={() => {
|
|
136
|
+
const currentDate = today(getLocalTimeZone());
|
|
137
|
+
value = {
|
|
138
|
+
start: currentDate.subtract({ days: 29 }),
|
|
139
|
+
end: currentDate,
|
|
140
|
+
};
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
Last 30 days
|
|
144
|
+
</button>
|
|
145
|
+
<button
|
|
146
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
147
|
+
onclick={() => {
|
|
148
|
+
const currentDate = today(getLocalTimeZone());
|
|
149
|
+
value = {
|
|
150
|
+
start: currentDate.subtract({ days: 89 }),
|
|
151
|
+
end: currentDate,
|
|
152
|
+
};
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
Last 90 days
|
|
156
|
+
</button>
|
|
157
|
+
<button
|
|
158
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
159
|
+
onclick={() => {
|
|
160
|
+
const currentDate = today(getLocalTimeZone());
|
|
161
|
+
value = {
|
|
162
|
+
start: currentDate.subtract({ months: 12 }),
|
|
163
|
+
end: currentDate,
|
|
164
|
+
};
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
Last 12 months
|
|
168
|
+
</button>
|
|
169
|
+
<button
|
|
170
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
171
|
+
onclick={() => {
|
|
172
|
+
const currentDate = today(getLocalTimeZone());
|
|
173
|
+
const lastYearStart = currentDate
|
|
174
|
+
.subtract({ years: 1 })
|
|
175
|
+
.set({ month: 1, day: 1 });
|
|
176
|
+
const lastYearEnd = currentDate
|
|
177
|
+
.subtract({ years: 1 })
|
|
178
|
+
.set({ month: 12, day: 31 });
|
|
179
|
+
value = {
|
|
180
|
+
start: lastYearStart,
|
|
181
|
+
end: lastYearEnd,
|
|
182
|
+
};
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
Last Calendar year
|
|
186
|
+
</button>
|
|
187
|
+
<button
|
|
188
|
+
class="text-start text-sm py-2 px-2 hover:bg-muted/30 hover:text-primary"
|
|
189
|
+
onclick={() => {
|
|
190
|
+
const currentDate = today(getLocalTimeZone());
|
|
191
|
+
const yearStart = currentDate.set({
|
|
192
|
+
month: 1,
|
|
193
|
+
day: 1,
|
|
194
|
+
});
|
|
195
|
+
value = {
|
|
196
|
+
start: yearStart,
|
|
197
|
+
end: currentDate,
|
|
198
|
+
};
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
This Year (Jan - Today)
|
|
202
|
+
</button>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
<div>
|
|
206
|
+
<div class="flex gap-2 p-2 justify-end border-b">
|
|
207
|
+
<!-- Start Date -->
|
|
208
|
+
<Input
|
|
209
|
+
type="date"
|
|
210
|
+
class="w-fit"
|
|
211
|
+
bind:value={
|
|
212
|
+
() => {
|
|
213
|
+
if (!value?.start) return "";
|
|
214
|
+
// Convert CalendarDate → YYYY-MM-DD string
|
|
215
|
+
const jsDate = value.start.toDate("UTC");
|
|
216
|
+
return jsDate.toISOString().split("T")[0];
|
|
217
|
+
},
|
|
218
|
+
(v) => {
|
|
219
|
+
// Convert YYYY-MM-DD string → CalendarDate
|
|
220
|
+
value = {
|
|
221
|
+
...value,
|
|
222
|
+
start: parseDate(v),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/>
|
|
227
|
+
|
|
228
|
+
<!-- End Date -->
|
|
229
|
+
<Input
|
|
230
|
+
type="date"
|
|
231
|
+
class="w-fit"
|
|
232
|
+
bind:value={
|
|
233
|
+
() => {
|
|
234
|
+
if (!value?.end) return "";
|
|
235
|
+
const jsDate = value.end.toDate("UTC");
|
|
236
|
+
return jsDate.toISOString().split("T")[0];
|
|
237
|
+
},
|
|
238
|
+
(v) => {
|
|
239
|
+
value = {
|
|
240
|
+
...value,
|
|
241
|
+
end: parseDate(v),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
<RangeCalendar
|
|
248
|
+
bind:value
|
|
249
|
+
onStartValueChange={(v) => {
|
|
250
|
+
startValue = v;
|
|
251
|
+
}}
|
|
252
|
+
numberOfMonths={2}
|
|
253
|
+
/>
|
|
254
|
+
</div>
|
|
255
|
+
</Popover.Content>
|
|
256
|
+
</Popover.Root>
|
|
257
|
+
</div>
|