dr-widget 0.1.3__py3-none-any.whl
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.
- dr_widget/__init__.py +5 -0
- dr_widget/py.typed +0 -0
- dr_widget/widgets/__init__.py +5 -0
- dr_widget/widgets/config_file_manager/.gitignore +24 -0
- dr_widget/widgets/config_file_manager/.vscode/extensions.json +3 -0
- dr_widget/widgets/config_file_manager/README.md +89 -0
- dr_widget/widgets/config_file_manager/__init__.py +283 -0
- dr_widget/widgets/config_file_manager/components.json +16 -0
- dr_widget/widgets/config_file_manager/index.html +12 -0
- dr_widget/widgets/config_file_manager/jsrepo.json +18 -0
- dr_widget/widgets/config_file_manager/package.json +49 -0
- dr_widget/widgets/config_file_manager/postcss.config.js +6 -0
- dr_widget/widgets/config_file_manager/public/fonts/Inter-roman.var.woff2 +0 -0
- dr_widget/widgets/config_file_manager/public/vite.svg +1 -0
- dr_widget/widgets/config_file_manager/src/App.svelte +62 -0
- dr_widget/widgets/config_file_manager/src/ConfigFileManager.svelte +605 -0
- dr_widget/widgets/config_file_manager/src/app.css +134 -0
- dr_widget/widgets/config_file_manager/src/index.js +5 -0
- dr_widget/widgets/config_file_manager/src/lib/@test_state.json +20 -0
- dr_widget/widgets/config_file_manager/src/lib/Counter.svelte +10 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/BrowseConfigsPanel.svelte +137 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/ComplexJsonViewer.svelte +94 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/ConfigViewerPanel.svelte +282 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/LoadedConfigPreview.svelte +74 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SaveConfigPanel.svelte +449 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SelectedFileRow.svelte +38 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SelectedFilesList.svelte +30 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SimpleJsonViewer.svelte +405 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/badge/badge.svelte +50 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/badge/index.ts +2 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/button/button.svelte +128 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/button/index.ts +27 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-action.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-content.svelte +15 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-description.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-footer.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-header.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/index.ts +25 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-close.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-content.svelte +47 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-description.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-footer.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-header.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-overlay.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-title.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/index.ts +41 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-close.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-content.svelte +41 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-description.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-footer.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-header.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-nested.svelte +16 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-overlay.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-title.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-trigger.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer.svelte +16 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/index.ts +45 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-content.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-description.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-header.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-media.svelte +41 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/index.ts +22 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-content.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-description.svelte +25 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-error.svelte +58 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-group.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-label.svelte +26 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-legend.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-separator.svelte +38 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-set.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-title.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field.svelte +53 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/index.ts +33 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/file-drop-zone/file-drop-zone.svelte +178 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/file-drop-zone/index.ts +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/file-drop-zone/types.ts +51 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/index.ts +34 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-actions.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-content.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-description.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-footer.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-group.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-header.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-media.svelte +42 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-separator.svelte +19 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item.svelte +60 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/label/index.ts +7 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/label/label.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/index.ts +13 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-content.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-description.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-footer.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-header.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-trigger.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal.svelte.ts +32 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/separator/index.ts +7 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/separator/separator.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/index.ts +16 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs-list.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs-trigger.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs.svelte +19 -0
- dr_widget/widgets/config_file_manager/src/lib/hooks/use-file-bindings.ts +189 -0
- dr_widget/widgets/config_file_manager/src/lib/react/JsonTreeCanvas.tsx +207 -0
- dr_widget/widgets/config_file_manager/src/lib/utils/config-format.ts +113 -0
- dr_widget/widgets/config_file_manager/src/lib/utils/utils.ts +21 -0
- dr_widget/widgets/config_file_manager/src/lib/utils.ts +17 -0
- dr_widget/widgets/config_file_manager/src/main.js +7 -0
- dr_widget/widgets/config_file_manager/static/fonts/Inter-roman.var.woff2 +0 -0
- dr_widget/widgets/config_file_manager/static/index.js +9719 -0
- dr_widget/widgets/config_file_manager/static/style.css +1 -0
- dr_widget/widgets/config_file_manager/static/vite.svg +1 -0
- dr_widget/widgets/config_file_manager/svelte.config.js +8 -0
- dr_widget/widgets/config_file_manager/tailwind.config.js +12 -0
- dr_widget/widgets/config_file_manager/tsconfig.json +28 -0
- dr_widget/widgets/config_file_manager/vite.config.js +36 -0
- dr_widget-0.1.3.dist-info/METADATA +62 -0
- dr_widget-0.1.3.dist-info/RECORD +127 -0
- dr_widget-0.1.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
|
+
|
|
4
|
+
export const emptyMediaVariants = tv({
|
|
5
|
+
base: "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: "bg-transparent",
|
|
9
|
+
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
defaultVariants: {
|
|
13
|
+
variant: "default",
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type EmptyMediaVariant = VariantProps<typeof emptyMediaVariants>["variant"];
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
22
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
ref = $bindable(null),
|
|
26
|
+
class: className,
|
|
27
|
+
children,
|
|
28
|
+
variant = "default",
|
|
29
|
+
...restProps
|
|
30
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { variant?: EmptyMediaVariant } = $props();
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<div
|
|
34
|
+
bind:this={ref}
|
|
35
|
+
data-slot="empty-icon"
|
|
36
|
+
data-variant={variant}
|
|
37
|
+
class={cn(emptyMediaVariants({ variant }), className)}
|
|
38
|
+
{...restProps}
|
|
39
|
+
>
|
|
40
|
+
{@render children?.()}
|
|
41
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="empty-title"
|
|
16
|
+
class={cn("text-lg font-medium tracking-tight", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="empty"
|
|
16
|
+
class={cn(
|
|
17
|
+
"flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Root from "./empty.svelte";
|
|
2
|
+
import Header from "./empty-header.svelte";
|
|
3
|
+
import Media from "./empty-media.svelte";
|
|
4
|
+
import Title from "./empty-title.svelte";
|
|
5
|
+
import Description from "./empty-description.svelte";
|
|
6
|
+
import Content from "./empty-content.svelte";
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
Root,
|
|
10
|
+
Header,
|
|
11
|
+
Media,
|
|
12
|
+
Title,
|
|
13
|
+
Description,
|
|
14
|
+
Content,
|
|
15
|
+
//
|
|
16
|
+
Root as Empty,
|
|
17
|
+
Header as EmptyHeader,
|
|
18
|
+
Media as EmptyMedia,
|
|
19
|
+
Title as EmptyTitle,
|
|
20
|
+
Description as EmptyDescription,
|
|
21
|
+
Content as EmptyContent,
|
|
22
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="field-content"
|
|
16
|
+
class={cn("group/field-content flex flex-1 flex-col gap-1.5 leading-snug", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<p
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="field-description"
|
|
16
|
+
class={cn(
|
|
17
|
+
"text-muted-foreground text-sm font-normal leading-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
|
|
18
|
+
"nth-last-2:-mt-1 last:mt-0 [[data-variant=legend]+&]:-mt-1.5",
|
|
19
|
+
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
|
|
20
|
+
className
|
|
21
|
+
)}
|
|
22
|
+
{...restProps}
|
|
23
|
+
>
|
|
24
|
+
{@render children?.()}
|
|
25
|
+
</p>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
import type { Snippet } from "svelte";
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
children,
|
|
10
|
+
errors,
|
|
11
|
+
...restProps
|
|
12
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
errors?: { message?: string }[];
|
|
15
|
+
} = $props();
|
|
16
|
+
|
|
17
|
+
const hasContent = $derived.by(() => {
|
|
18
|
+
// has slotted error
|
|
19
|
+
if (children) return true;
|
|
20
|
+
|
|
21
|
+
// no errors
|
|
22
|
+
if (!errors) return false;
|
|
23
|
+
|
|
24
|
+
// has an error but no message
|
|
25
|
+
if (errors.length === 1 && !errors[0]?.message) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const isMultipleErrors = $derived(errors && errors.length > 1);
|
|
33
|
+
const singleErrorMessage = $derived(errors && errors.length === 1 && errors[0]?.message);
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
{#if hasContent}
|
|
37
|
+
<div
|
|
38
|
+
bind:this={ref}
|
|
39
|
+
role="alert"
|
|
40
|
+
data-slot="field-error"
|
|
41
|
+
class={cn("text-destructive text-sm font-normal", className)}
|
|
42
|
+
{...restProps}
|
|
43
|
+
>
|
|
44
|
+
{#if children}
|
|
45
|
+
{@render children()}
|
|
46
|
+
{:else if singleErrorMessage}
|
|
47
|
+
{singleErrorMessage}
|
|
48
|
+
{:else if isMultipleErrors}
|
|
49
|
+
<ul class="ml-4 flex list-disc flex-col gap-1">
|
|
50
|
+
{#each errors ?? [] as error, index (index)}
|
|
51
|
+
{#if error?.message}
|
|
52
|
+
<li>{error.message}</li>
|
|
53
|
+
{/if}
|
|
54
|
+
{/each}
|
|
55
|
+
</ul>
|
|
56
|
+
{/if}
|
|
57
|
+
</div>
|
|
58
|
+
{/if}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="field-group"
|
|
16
|
+
class={cn(
|
|
17
|
+
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Label } from "$lib/components/ui/label/index.js";
|
|
3
|
+
import { cn } from "$lib/utils.js";
|
|
4
|
+
import type { ComponentProps } from "svelte";
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(null),
|
|
8
|
+
class: className,
|
|
9
|
+
children,
|
|
10
|
+
...restProps
|
|
11
|
+
}: ComponentProps<typeof Label> = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Label
|
|
15
|
+
bind:ref
|
|
16
|
+
data-slot="field-label"
|
|
17
|
+
class={cn(
|
|
18
|
+
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
19
|
+
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
|
|
20
|
+
"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...restProps}
|
|
24
|
+
>
|
|
25
|
+
{@render children?.()}
|
|
26
|
+
</Label>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
variant = "legend",
|
|
9
|
+
children,
|
|
10
|
+
...restProps
|
|
11
|
+
}: WithElementRef<HTMLAttributes<HTMLLegendElement>> & {
|
|
12
|
+
variant?: "legend" | "label";
|
|
13
|
+
} = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<legend
|
|
17
|
+
bind:this={ref}
|
|
18
|
+
data-slot="field-legend"
|
|
19
|
+
data-variant={variant}
|
|
20
|
+
class={cn(
|
|
21
|
+
"mb-3 font-medium",
|
|
22
|
+
"data-[variant=legend]:text-base",
|
|
23
|
+
"data-[variant=label]:text-sm",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...restProps}
|
|
27
|
+
>
|
|
28
|
+
{@render children?.()}
|
|
29
|
+
</legend>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Separator } from "$lib/components/ui/separator/index.js";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
ref = $bindable(null),
|
|
9
|
+
class: className,
|
|
10
|
+
children,
|
|
11
|
+
...restProps
|
|
12
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
const hasContent = $derived(!!children);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div
|
|
20
|
+
bind:this={ref}
|
|
21
|
+
data-slot="field-separator"
|
|
22
|
+
data-content={hasContent}
|
|
23
|
+
class={cn(
|
|
24
|
+
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...restProps}
|
|
28
|
+
>
|
|
29
|
+
<Separator class="absolute inset-0 top-1/2" />
|
|
30
|
+
{#if children}
|
|
31
|
+
<span
|
|
32
|
+
class="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
|
|
33
|
+
data-slot="field-separator-content"
|
|
34
|
+
>
|
|
35
|
+
{@render children()}
|
|
36
|
+
</span>
|
|
37
|
+
{/if}
|
|
38
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLFieldsetAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLFieldsetAttributes> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<fieldset
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="field-set"
|
|
16
|
+
class={cn(
|
|
17
|
+
"flex flex-col gap-6",
|
|
18
|
+
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...restProps}
|
|
22
|
+
>
|
|
23
|
+
{@render children?.()}
|
|
24
|
+
</fieldset>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="field-title"
|
|
16
|
+
class={cn(
|
|
17
|
+
"flex w-fit items-center gap-2 text-sm font-medium leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
|
+
|
|
4
|
+
export const fieldVariants = tv({
|
|
5
|
+
base: "group/field data-[invalid=true]:text-destructive flex w-full gap-3",
|
|
6
|
+
variants: {
|
|
7
|
+
orientation: {
|
|
8
|
+
vertical: "flex-col [&>*]:w-full [&>.sr-only]:w-auto",
|
|
9
|
+
horizontal: [
|
|
10
|
+
"flex-row items-center",
|
|
11
|
+
"[&>[data-slot=field-label]]:flex-auto",
|
|
12
|
+
"has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px has-[>[data-slot=field-content]]:items-start",
|
|
13
|
+
],
|
|
14
|
+
responsive: [
|
|
15
|
+
"@md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto flex-col [&>*]:w-full [&>.sr-only]:w-auto",
|
|
16
|
+
"@md/field-group:[&>[data-slot=field-label]]:flex-auto",
|
|
17
|
+
"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
orientation: "vertical",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type FieldOrientation = VariantProps<typeof fieldVariants>["orientation"];
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<script lang="ts">
|
|
30
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
31
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
ref = $bindable(null),
|
|
35
|
+
class: className,
|
|
36
|
+
orientation = "vertical",
|
|
37
|
+
children,
|
|
38
|
+
...restProps
|
|
39
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
40
|
+
orientation?: FieldOrientation;
|
|
41
|
+
} = $props();
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<div
|
|
45
|
+
bind:this={ref}
|
|
46
|
+
role="group"
|
|
47
|
+
data-slot="field"
|
|
48
|
+
data-orientation={orientation}
|
|
49
|
+
class={cn(fieldVariants({ orientation }), className)}
|
|
50
|
+
{...restProps}
|
|
51
|
+
>
|
|
52
|
+
{@render children?.()}
|
|
53
|
+
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Field from "./field.svelte";
|
|
2
|
+
import Set from "./field-set.svelte";
|
|
3
|
+
import Legend from "./field-legend.svelte";
|
|
4
|
+
import Group from "./field-group.svelte";
|
|
5
|
+
import Content from "./field-content.svelte";
|
|
6
|
+
import Label from "./field-label.svelte";
|
|
7
|
+
import Title from "./field-title.svelte";
|
|
8
|
+
import Description from "./field-description.svelte";
|
|
9
|
+
import Separator from "./field-separator.svelte";
|
|
10
|
+
import Error from "./field-error.svelte";
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
Field,
|
|
14
|
+
Set,
|
|
15
|
+
Legend,
|
|
16
|
+
Group,
|
|
17
|
+
Content,
|
|
18
|
+
Label,
|
|
19
|
+
Title,
|
|
20
|
+
Description,
|
|
21
|
+
Separator,
|
|
22
|
+
Error,
|
|
23
|
+
//
|
|
24
|
+
Set as FieldSet,
|
|
25
|
+
Legend as FieldLegend,
|
|
26
|
+
Group as FieldGroup,
|
|
27
|
+
Content as FieldContent,
|
|
28
|
+
Label as FieldLabel,
|
|
29
|
+
Title as FieldTitle,
|
|
30
|
+
Description as FieldDescription,
|
|
31
|
+
Separator as FieldSeparator,
|
|
32
|
+
Error as FieldError,
|
|
33
|
+
};
|
dr_widget/widgets/config_file_manager/src/lib/components/ui/file-drop-zone/file-drop-zone.svelte
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Installed from @ieedan/shadcn-svelte-extras
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { cn } from '../../../utils/utils';
|
|
7
|
+
import UploadIcon from '@lucide/svelte/icons/upload';
|
|
8
|
+
import { displaySize } from '.';
|
|
9
|
+
import { useId } from 'bits-ui';
|
|
10
|
+
import type { FileDropZoneProps, FileRejectedReason } from './types';
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
id = useId(),
|
|
14
|
+
children,
|
|
15
|
+
maxFiles,
|
|
16
|
+
maxFileSize,
|
|
17
|
+
fileCount,
|
|
18
|
+
disabled = false,
|
|
19
|
+
onUpload,
|
|
20
|
+
onFileRejected,
|
|
21
|
+
accept,
|
|
22
|
+
class: className,
|
|
23
|
+
...rest
|
|
24
|
+
}: FileDropZoneProps = $props();
|
|
25
|
+
|
|
26
|
+
if (maxFiles !== undefined && fileCount === undefined) {
|
|
27
|
+
console.warn(
|
|
28
|
+
'Make sure to provide FileDropZone with `fileCount` when using the `maxFiles` prompt'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let uploading = $state(false);
|
|
33
|
+
|
|
34
|
+
const drop = async (
|
|
35
|
+
e: DragEvent & {
|
|
36
|
+
currentTarget: EventTarget & HTMLLabelElement;
|
|
37
|
+
}
|
|
38
|
+
) => {
|
|
39
|
+
if (disabled || !canUploadFiles) return;
|
|
40
|
+
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
|
|
43
|
+
const droppedFiles = Array.from(e.dataTransfer?.files ?? []);
|
|
44
|
+
|
|
45
|
+
await upload(droppedFiles);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const change = async (
|
|
49
|
+
e: Event & {
|
|
50
|
+
currentTarget: EventTarget & HTMLInputElement;
|
|
51
|
+
}
|
|
52
|
+
) => {
|
|
53
|
+
if (disabled) return;
|
|
54
|
+
|
|
55
|
+
const selectedFiles = e.currentTarget.files;
|
|
56
|
+
|
|
57
|
+
if (!selectedFiles) return;
|
|
58
|
+
|
|
59
|
+
await upload(Array.from(selectedFiles));
|
|
60
|
+
|
|
61
|
+
// this if a file fails and we upload the same file again we still get feedback
|
|
62
|
+
if (e.target as HTMLInputElement) {
|
|
63
|
+
(e.target as HTMLInputElement).value = '';
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const shouldAcceptFile = (file: File, fileNumber: number): FileRejectedReason | undefined => {
|
|
68
|
+
if (maxFileSize !== undefined && file.size > maxFileSize) return 'Maximum file size exceeded';
|
|
69
|
+
|
|
70
|
+
if (maxFiles !== undefined && fileNumber > maxFiles) return 'Maximum files uploaded';
|
|
71
|
+
|
|
72
|
+
if (!accept) return undefined;
|
|
73
|
+
|
|
74
|
+
const acceptedTypes = accept.split(',').map((a) => a.trim().toLowerCase());
|
|
75
|
+
const fileType = file.type.toLowerCase();
|
|
76
|
+
const fileName = file.name.toLowerCase();
|
|
77
|
+
|
|
78
|
+
const isAcceptable = acceptedTypes.some((pattern) => {
|
|
79
|
+
// check extension like .mp4
|
|
80
|
+
if (fileType.startsWith('.')) {
|
|
81
|
+
return fileName.endsWith(pattern);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// if pattern has wild card like video/*
|
|
85
|
+
if (pattern.endsWith('/*')) {
|
|
86
|
+
const baseType = pattern.slice(0, pattern.indexOf('/*'));
|
|
87
|
+
return fileType.startsWith(baseType + '/');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// otherwise it must be a specific type like video/mp4
|
|
91
|
+
return fileType === pattern;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (!isAcceptable) return 'File type not allowed';
|
|
95
|
+
|
|
96
|
+
return undefined;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const upload = async (uploadFiles: File[]) => {
|
|
100
|
+
uploading = true;
|
|
101
|
+
|
|
102
|
+
const validFiles: File[] = [];
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < uploadFiles.length; i++) {
|
|
105
|
+
const file = uploadFiles[i];
|
|
106
|
+
|
|
107
|
+
const rejectedReason = shouldAcceptFile(file, (fileCount ?? 0) + i + 1);
|
|
108
|
+
|
|
109
|
+
if (rejectedReason) {
|
|
110
|
+
onFileRejected?.({ file, reason: rejectedReason });
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
validFiles.push(file);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await onUpload(validFiles);
|
|
118
|
+
|
|
119
|
+
uploading = false;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const canUploadFiles = $derived(
|
|
123
|
+
!disabled &&
|
|
124
|
+
!uploading &&
|
|
125
|
+
!(maxFiles !== undefined && fileCount !== undefined && fileCount >= maxFiles)
|
|
126
|
+
);
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<label
|
|
130
|
+
ondragover={(e) => e.preventDefault()}
|
|
131
|
+
ondrop={drop}
|
|
132
|
+
for={id}
|
|
133
|
+
aria-disabled={!canUploadFiles}
|
|
134
|
+
class={cn(
|
|
135
|
+
'border-border hover:bg-accent/25 flex h-48 w-full place-items-center justify-center rounded-lg border-2 border-dashed p-6 transition-all hover:cursor-pointer aria-disabled:opacity-50 aria-disabled:hover:cursor-not-allowed',
|
|
136
|
+
className
|
|
137
|
+
)}
|
|
138
|
+
>
|
|
139
|
+
{#if children}
|
|
140
|
+
{@render children()}
|
|
141
|
+
{:else}
|
|
142
|
+
<div class="flex flex-col place-items-center justify-center gap-2">
|
|
143
|
+
<div
|
|
144
|
+
class="border-border text-muted-foreground flex size-14 place-items-center justify-center rounded-full border border-dashed"
|
|
145
|
+
>
|
|
146
|
+
<UploadIcon class="size-7" />
|
|
147
|
+
</div>
|
|
148
|
+
<div class="flex flex-col gap-0.5 text-center">
|
|
149
|
+
<span class="text-muted-foreground font-medium">
|
|
150
|
+
Drag 'n' drop files here, or click to select files
|
|
151
|
+
</span>
|
|
152
|
+
{#if maxFiles || maxFileSize}
|
|
153
|
+
<span class="text-muted-foreground/75 text-sm">
|
|
154
|
+
{#if maxFiles}
|
|
155
|
+
<span>You can upload {maxFiles} files</span>
|
|
156
|
+
{/if}
|
|
157
|
+
{#if maxFiles && maxFileSize}
|
|
158
|
+
<span>(up to {displaySize(maxFileSize)} each)</span>
|
|
159
|
+
{/if}
|
|
160
|
+
{#if maxFileSize && !maxFiles}
|
|
161
|
+
<span>Maximum size {displaySize(maxFileSize)}</span>
|
|
162
|
+
{/if}
|
|
163
|
+
</span>
|
|
164
|
+
{/if}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
{/if}
|
|
168
|
+
<input
|
|
169
|
+
{...rest}
|
|
170
|
+
disabled={!canUploadFiles}
|
|
171
|
+
{id}
|
|
172
|
+
{accept}
|
|
173
|
+
multiple={maxFiles === undefined || maxFiles - (fileCount ?? 0) > 1}
|
|
174
|
+
type="file"
|
|
175
|
+
onchange={change}
|
|
176
|
+
class="hidden"
|
|
177
|
+
/>
|
|
178
|
+
</label>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Installed from @ieedan/shadcn-svelte-extras
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import FileDropZone from "./file-drop-zone.svelte";
|
|
6
|
+
import { type FileRejectedReason, type FileDropZoneProps } from "./types";
|
|
7
|
+
|
|
8
|
+
export const displaySize = (bytes: number): string => {
|
|
9
|
+
if (bytes < KILOBYTE) return `${bytes.toFixed(0)} B`;
|
|
10
|
+
|
|
11
|
+
if (bytes < MEGABYTE) return `${(bytes / KILOBYTE).toFixed(0)} KB`;
|
|
12
|
+
|
|
13
|
+
if (bytes < GIGABYTE) return `${(bytes / MEGABYTE).toFixed(0)} MB`;
|
|
14
|
+
|
|
15
|
+
return `${(bytes / GIGABYTE).toFixed(0)} GB`;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Utilities for working with file sizes
|
|
19
|
+
export const BYTE = 1;
|
|
20
|
+
export const KILOBYTE = 1024;
|
|
21
|
+
export const MEGABYTE = 1024 * KILOBYTE;
|
|
22
|
+
export const GIGABYTE = 1024 * MEGABYTE;
|
|
23
|
+
|
|
24
|
+
// utilities for limiting accepted files
|
|
25
|
+
export const ACCEPT_IMAGE = "image/*";
|
|
26
|
+
export const ACCEPT_VIDEO = "video/*";
|
|
27
|
+
export const ACCEPT_AUDIO = "audio/*";
|
|
28
|
+
|
|
29
|
+
export { FileDropZone, type FileRejectedReason, type FileDropZoneProps };
|