@lobb-js/studio 0.7.2 → 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.
Files changed (162) hide show
  1. package/package.json +2 -1
  2. package/src/App.svelte +5 -0
  3. package/src/app.css +124 -0
  4. package/src/lib/components/LlmButton.svelte +137 -0
  5. package/src/lib/components/Studio.svelte +129 -0
  6. package/src/lib/components/alertView.svelte +20 -0
  7. package/src/lib/components/breadCrumbs.svelte +60 -0
  8. package/src/lib/components/codeEditor.svelte +152 -0
  9. package/src/lib/components/combobox.svelte +92 -0
  10. package/src/lib/components/confirmationDialog/confirmationDialog.svelte +33 -0
  11. package/src/lib/components/confirmationDialog/store.svelte.ts +28 -0
  12. package/src/lib/components/createManyButton.svelte +109 -0
  13. package/src/lib/components/dataTable/childRecords.svelte +142 -0
  14. package/src/lib/components/dataTable/dataTable.svelte +225 -0
  15. package/src/lib/components/dataTable/fieldCell.svelte +77 -0
  16. package/src/lib/components/dataTable/filter.svelte +284 -0
  17. package/src/lib/components/dataTable/filterButton.svelte +39 -0
  18. package/src/lib/components/dataTable/footer.svelte +84 -0
  19. package/src/lib/components/dataTable/header.svelte +155 -0
  20. package/src/lib/components/dataTable/sort.svelte +173 -0
  21. package/src/lib/components/dataTable/sortButton.svelte +36 -0
  22. package/src/lib/components/dataTable/table.svelte +337 -0
  23. package/src/lib/components/dataTable/utils.ts +127 -0
  24. package/src/lib/components/detailView/create/children.svelte +70 -0
  25. package/src/lib/components/detailView/create/createDetailView.svelte +228 -0
  26. package/src/lib/components/detailView/create/createDetailViewButton.svelte +37 -0
  27. package/src/lib/components/detailView/create/createManyView.svelte +252 -0
  28. package/src/lib/components/detailView/create/subRecords.svelte +50 -0
  29. package/src/lib/components/detailView/detailViewForm.svelte +104 -0
  30. package/src/lib/components/detailView/fieldCustomInput.svelte +26 -0
  31. package/src/lib/components/detailView/fieldInput.svelte +258 -0
  32. package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
  33. package/src/lib/components/detailView/store.svelte.ts +59 -0
  34. package/src/lib/components/detailView/update/children.svelte +96 -0
  35. package/src/lib/components/detailView/update/updateDetailView.svelte +176 -0
  36. package/src/lib/components/detailView/update/updateDetailViewButton.svelte +56 -0
  37. package/src/lib/components/detailView/utils.ts +176 -0
  38. package/src/lib/components/diffViewer.svelte +105 -0
  39. package/src/lib/components/drawer.svelte +28 -0
  40. package/src/lib/components/extensionsComponents.svelte +33 -0
  41. package/src/lib/components/foreingKeyInput.svelte +80 -0
  42. package/src/lib/components/header.svelte +45 -0
  43. package/src/lib/components/loadingTypesForMonacoEditor.ts +36 -0
  44. package/src/lib/components/miniSidebar.svelte +226 -0
  45. package/src/lib/components/rangeCalendarButton.svelte +257 -0
  46. package/src/lib/components/richTextEditor.svelte +284 -0
  47. package/src/lib/components/routes/collections/collection.svelte +57 -0
  48. package/src/lib/components/routes/collections/collections.svelte +45 -0
  49. package/src/lib/components/routes/data_model/dataModel.svelte +40 -0
  50. package/src/lib/components/routes/data_model/flow.css +22 -0
  51. package/src/lib/components/routes/data_model/flow.svelte +84 -0
  52. package/src/lib/components/routes/data_model/syncManager.svelte +94 -0
  53. package/src/lib/components/routes/data_model/utils.ts +35 -0
  54. package/src/lib/components/routes/extensions/extension.svelte +19 -0
  55. package/src/lib/components/routes/home.svelte +40 -0
  56. package/src/lib/components/routes/workflows/workflows.svelte +136 -0
  57. package/src/lib/components/selectRecord.svelte +130 -0
  58. package/src/lib/components/setServerPage.svelte +50 -0
  59. package/src/lib/components/sidebar/index.ts +4 -0
  60. package/src/lib/components/sidebar/sidebar.svelte +149 -0
  61. package/src/lib/components/sidebar/sidebarElements.svelte +144 -0
  62. package/src/lib/components/sidebar/sidebarTrigger.svelte +33 -0
  63. package/src/lib/components/singletone.svelte +71 -0
  64. package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
  65. package/src/lib/components/ui/accordion/accordion-item.svelte +12 -0
  66. package/src/lib/components/ui/accordion/accordion-trigger.svelte +31 -0
  67. package/src/lib/components/ui/accordion/index.ts +17 -0
  68. package/src/lib/components/ui/alert/alert-description.svelte +16 -0
  69. package/src/lib/components/ui/alert/alert-title.svelte +24 -0
  70. package/src/lib/components/ui/alert/alert.svelte +39 -0
  71. package/src/lib/components/ui/alert/index.ts +14 -0
  72. package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +13 -0
  73. package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +17 -0
  74. package/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +26 -0
  75. package/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +16 -0
  76. package/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +20 -0
  77. package/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
  78. package/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +19 -0
  79. package/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +18 -0
  80. package/src/lib/components/ui/alert-dialog/index.ts +40 -0
  81. package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
  82. package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +16 -0
  83. package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
  84. package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
  85. package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
  86. package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
  87. package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +15 -0
  88. package/src/lib/components/ui/breadcrumb/index.ts +25 -0
  89. package/src/lib/components/ui/button/button.svelte +110 -0
  90. package/src/lib/components/ui/button/index.ts +17 -0
  91. package/src/lib/components/ui/checkbox/checkbox.svelte +35 -0
  92. package/src/lib/components/ui/checkbox/index.ts +6 -0
  93. package/src/lib/components/ui/command/command-dialog.svelte +35 -0
  94. package/src/lib/components/ui/command/command-empty.svelte +12 -0
  95. package/src/lib/components/ui/command/command-group.svelte +31 -0
  96. package/src/lib/components/ui/command/command-input.svelte +25 -0
  97. package/src/lib/components/ui/command/command-item.svelte +19 -0
  98. package/src/lib/components/ui/command/command-link-item.svelte +19 -0
  99. package/src/lib/components/ui/command/command-list.svelte +16 -0
  100. package/src/lib/components/ui/command/command-separator.svelte +12 -0
  101. package/src/lib/components/ui/command/command-shortcut.svelte +20 -0
  102. package/src/lib/components/ui/command/command.svelte +21 -0
  103. package/src/lib/components/ui/command/index.ts +40 -0
  104. package/src/lib/components/ui/dialog/dialog-content.svelte +38 -0
  105. package/src/lib/components/ui/dialog/dialog-description.svelte +16 -0
  106. package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
  107. package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
  108. package/src/lib/components/ui/dialog/dialog-overlay.svelte +19 -0
  109. package/src/lib/components/ui/dialog/dialog-title.svelte +16 -0
  110. package/src/lib/components/ui/dialog/index.ts +37 -0
  111. package/src/lib/components/ui/input/index.ts +7 -0
  112. package/src/lib/components/ui/input/input.svelte +46 -0
  113. package/src/lib/components/ui/label/index.ts +7 -0
  114. package/src/lib/components/ui/label/label.svelte +19 -0
  115. package/src/lib/components/ui/popover/index.ts +17 -0
  116. package/src/lib/components/ui/popover/popover-content.svelte +28 -0
  117. package/src/lib/components/ui/range-calendar/index.ts +30 -0
  118. package/src/lib/components/ui/range-calendar/range-calendar-cell.svelte +19 -0
  119. package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +35 -0
  120. package/src/lib/components/ui/range-calendar/range-calendar-grid-body.svelte +12 -0
  121. package/src/lib/components/ui/range-calendar/range-calendar-grid-head.svelte +12 -0
  122. package/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte +12 -0
  123. package/src/lib/components/ui/range-calendar/range-calendar-grid.svelte +16 -0
  124. package/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte +16 -0
  125. package/src/lib/components/ui/range-calendar/range-calendar-header.svelte +16 -0
  126. package/src/lib/components/ui/range-calendar/range-calendar-heading.svelte +16 -0
  127. package/src/lib/components/ui/range-calendar/range-calendar-months.svelte +20 -0
  128. package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +27 -0
  129. package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +27 -0
  130. package/src/lib/components/ui/range-calendar/range-calendar.svelte +57 -0
  131. package/src/lib/components/ui/select/index.ts +34 -0
  132. package/src/lib/components/ui/select/select-content.svelte +38 -0
  133. package/src/lib/components/ui/select/select-group-heading.svelte +16 -0
  134. package/src/lib/components/ui/select/select-item.svelte +37 -0
  135. package/src/lib/components/ui/select/select-scroll-down-button.svelte +19 -0
  136. package/src/lib/components/ui/select/select-scroll-up-button.svelte +19 -0
  137. package/src/lib/components/ui/select/select-separator.svelte +13 -0
  138. package/src/lib/components/ui/select/select-trigger.svelte +24 -0
  139. package/src/lib/components/ui/separator/index.ts +7 -0
  140. package/src/lib/components/ui/separator/separator.svelte +22 -0
  141. package/src/lib/components/ui/skeleton/index.ts +7 -0
  142. package/src/lib/components/ui/skeleton/skeleton.svelte +22 -0
  143. package/src/lib/components/ui/sonner/index.ts +1 -0
  144. package/src/lib/components/ui/sonner/sonner.svelte +20 -0
  145. package/src/lib/components/ui/switch/index.ts +7 -0
  146. package/src/lib/components/ui/switch/switch.svelte +27 -0
  147. package/src/lib/components/ui/textarea/index.ts +7 -0
  148. package/src/lib/components/ui/textarea/textarea.svelte +22 -0
  149. package/src/lib/components/ui/tooltip/index.ts +18 -0
  150. package/src/lib/components/ui/tooltip/tooltip-content.svelte +21 -0
  151. package/src/lib/components/workflowEditor.svelte +188 -0
  152. package/src/lib/context.ts +22 -0
  153. package/src/lib/eventSystem.ts +40 -0
  154. package/src/lib/extensions/extension.types.ts +92 -0
  155. package/src/lib/extensions/extensionUtils.ts +156 -0
  156. package/src/lib/index.ts +24 -0
  157. package/src/lib/store.svelte.ts +13 -0
  158. package/src/lib/store.types.ts +28 -0
  159. package/src/lib/utils.ts +68 -0
  160. package/src/main.ts +18 -0
  161. package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
  162. 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>