@tulip-systems/drive 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +662 -0
  2. package/package.json +113 -0
  3. package/src/components/content.tsx +13 -0
  4. package/src/components/context.client.tsx +12 -0
  5. package/src/components/dnd.client.tsx +47 -0
  6. package/src/components/grid-card.client.tsx +252 -0
  7. package/src/components/grid.client.tsx +96 -0
  8. package/src/components/navigation/breadcrumbs.client.tsx +125 -0
  9. package/src/components/navigation/header.client.tsx +45 -0
  10. package/src/components/navigation/toolbar.client.tsx +35 -0
  11. package/src/components/navigation/view-switcher.client.tsx +32 -0
  12. package/src/components/selection.client.tsx +48 -0
  13. package/src/components/view.client.tsx +67 -0
  14. package/src/config/filters.ts +14 -0
  15. package/src/config/types.tsx +90 -0
  16. package/src/entry.client.ts +7 -0
  17. package/src/entry.server.ts +4 -0
  18. package/src/entry.ts +10 -0
  19. package/src/lib/constants.ts +19 -0
  20. package/src/lib/contracts.ts +121 -0
  21. package/src/lib/dto.ts +83 -0
  22. package/src/lib/helpers.server.ts +14 -0
  23. package/src/lib/helpers.ts +32 -0
  24. package/src/lib/search-params.ts +5 -0
  25. package/src/lib/validators.ts +89 -0
  26. package/src/providers/google/components/command-file-update.tsx +100 -0
  27. package/src/providers/google/components/command-folder-create.tsx +104 -0
  28. package/src/providers/google/components/command-folder-update.tsx +100 -0
  29. package/src/providers/google/components/content.client.tsx +6 -0
  30. package/src/providers/google/components/navigation.client.tsx +21 -0
  31. package/src/providers/google/components/provider.client.tsx +60 -0
  32. package/src/providers/google/components/view.client.tsx +158 -0
  33. package/src/providers/google/config/columns-data.tsx +81 -0
  34. package/src/providers/google/config/filters.ts +3 -0
  35. package/src/providers/google/entry.client.ts +10 -0
  36. package/src/providers/google/entry.server.ts +5 -0
  37. package/src/providers/google/entry.ts +12 -0
  38. package/src/providers/google/lib/constants.ts +10 -0
  39. package/src/providers/google/lib/dto.ts +104 -0
  40. package/src/providers/google/lib/helpers.ts +37 -0
  41. package/src/providers/google/lib/router.server.ts +62 -0
  42. package/src/providers/google/lib/schema.ts +9 -0
  43. package/src/providers/google/lib/search-params.ts +7 -0
  44. package/src/providers/google/lib/service.server.ts +792 -0
  45. package/src/providers/google/lib/validators.ts +148 -0
  46. package/src/providers/local/components/command-file-update.tsx +93 -0
  47. package/src/providers/local/components/command-file-upload.tsx +29 -0
  48. package/src/providers/local/components/command-folder-create.tsx +100 -0
  49. package/src/providers/local/components/command-folder-update.tsx +93 -0
  50. package/src/providers/local/components/content.client.tsx +3 -0
  51. package/src/providers/local/components/navigation.client.tsx +23 -0
  52. package/src/providers/local/components/provider.client.tsx +90 -0
  53. package/src/providers/local/components/upload-zone-context.client.tsx +43 -0
  54. package/src/providers/local/components/upload-zone.client.tsx +182 -0
  55. package/src/providers/local/components/view.client.tsx +145 -0
  56. package/src/providers/local/config/columns-data.tsx +81 -0
  57. package/src/providers/local/config/filters.ts +14 -0
  58. package/src/providers/local/entry.client.ts +18 -0
  59. package/src/providers/local/entry.server.ts +7 -0
  60. package/src/providers/local/entry.ts +14 -0
  61. package/src/providers/local/lib/constants.ts +23 -0
  62. package/src/providers/local/lib/helpers.ts +105 -0
  63. package/src/providers/local/lib/route-handler.server.ts +153 -0
  64. package/src/providers/local/lib/router.server.ts +137 -0
  65. package/src/providers/local/lib/schema.ts +104 -0
  66. package/src/providers/local/lib/search-params.ts +4 -0
  67. package/src/providers/local/lib/service.server.ts +1116 -0
  68. package/src/providers/local/lib/upload.client.ts +177 -0
  69. package/src/providers/local/lib/validators.ts +154 -0
  70. package/src/styles.css +1 -0
@@ -0,0 +1,148 @@
1
+ import z from "zod";
2
+ import { type DriveNodeChild, driveNodeSchema } from "@/lib/dto";
3
+ import {
4
+ createDriveFolderInputSchema,
5
+ driveTableFiltersSchema,
6
+ getNodesByParentIdInputSchema,
7
+ listDriveTreeInputSchema,
8
+ updateDriveNodeInputSchema,
9
+ uploadDriveFileInputSchema,
10
+ } from "@/lib/validators";
11
+
12
+ /**
13
+ * GoogleDriveNode schema
14
+ * @description zod schema for validating the shape of a GoogleDriveNode
15
+ */
16
+ export const googleDriveNodeSchema = driveNodeSchema.extend({
17
+ /**
18
+ * Constant discriminator for Google-backed nodes.
19
+ * Maps to the provider implementation, not to a Google Drive API field.
20
+ */
21
+ provider: z.literal("google"),
22
+ /**
23
+ * Native Google shared drive id from `drive_v3.Schema$File.driveId`.
24
+ * We keep this provider-specific field only as source metadata. The shared
25
+ * `DriveNode.namespace` field is the canonical namespace used by callers.
26
+ */
27
+ googleDriveId: z.string().nullable(),
28
+ /**
29
+ * Original Google Drive MIME type from `drive_v3.Schema$File.mimeType`.
30
+ * The common `contentType` field uses the same source for files and `null`
31
+ * for folders so consumers can treat folders consistently across providers.
32
+ */
33
+ mimeType: z.string().nullable(),
34
+ /**
35
+ * Browser view URL from `drive_v3.Schema$File.webViewLink`.
36
+ * This is Google Drive's closest equivalent to a provider-neutral view URL.
37
+ */
38
+ webViewLink: z.string().nullable(),
39
+
40
+ /**
41
+ * Download URL from `drive_v3.Schema$File.webContentLink`.
42
+ * Google only returns this for binary files, not for all Google-native docs.
43
+ */
44
+ webContentLink: z.string().nullable(),
45
+ /**
46
+ * Thumbnail URL from `drive_v3.Schema$File.thumbnailLink`.
47
+ * Google provides this opportunistically, so the common `links.thumbnail`
48
+ * field is nullable.
49
+ */
50
+ thumbnailLink: z.string().nullable(),
51
+ /**
52
+ * Native Google trash state from `drive_v3.Schema$File.trashed`.
53
+ * The common `archivedAt` field derives from this because the local provider
54
+ * models soft removal as archive while Google Drive models it as trash.
55
+ */
56
+ trashed: z.boolean(),
57
+ /**
58
+ * Native Google trash timestamp from `drive_v3.Schema$File.trashedTime`.
59
+ * The common `archivedAt` field uses this when available, falling back to the
60
+ * modified time when Google only exposes `trashed: true`.
61
+ */
62
+ trashedAt: z.date().nullable(),
63
+ /**
64
+ * Raw Google parent ids from `drive_v3.Schema$File.parents`.
65
+ * The common `parentId` field uses the first parent, with the configured
66
+ * namespace converted to `null` so root nodes match the local provider shape.
67
+ */
68
+ googleParents: z.array(z.string()),
69
+
70
+ /**
71
+ * Google edit capability from `drive_v3.Schema$File.capabilities.canEdit`.
72
+ * The common `readonly` field is true when Google content restrictions mark
73
+ * the file readonly or when Google says the user cannot edit the file.
74
+ */
75
+ canEdit: z.boolean().nullable(),
76
+ });
77
+
78
+ export type GoogleDriveNode = z.infer<typeof googleDriveNodeSchema>;
79
+ export type GoogleDriveNodeChild = DriveNodeChild<GoogleDriveNode>;
80
+
81
+ /**
82
+ * GoogleDriveNodeWithChildren schema
83
+ * @description zod schema for validating the shape of a GoogleDriveNode with children
84
+ */
85
+ export const googleDriveNodeWithChildrenSchema = googleDriveNodeSchema.extend({
86
+ children: z.array(googleDriveNodeSchema),
87
+ });
88
+
89
+ export type GoogleDriveNodeWithChildren = z.infer<typeof googleDriveNodeWithChildrenSchema>;
90
+
91
+ /**
92
+ * Create drive folder input schema
93
+ * @description zod schema for validating the input of the create drive folder command
94
+ */
95
+ export const createGoogleDriveFolderInputSchema = createDriveFolderInputSchema;
96
+
97
+ export type CreateGoogleDriveFolderInput = z.input<typeof createGoogleDriveFolderInputSchema>;
98
+ export type CreateGoogleDriveFolderSchema = z.infer<typeof createGoogleDriveFolderInputSchema>;
99
+
100
+ /**
101
+ * Update drive node input schema
102
+ * @description zod schema for validating the input of the update drive node command
103
+ */
104
+ export const updateGoogleDriveNodeInputSchema = updateDriveNodeInputSchema.extend({
105
+ isArchived: z.boolean().optional(),
106
+ });
107
+
108
+ export type UpdateGoogleDriveNodeInput = z.input<typeof updateGoogleDriveNodeInputSchema>;
109
+ export type UpdateGoogleDriveNodeSchema = z.infer<typeof updateGoogleDriveNodeInputSchema>;
110
+
111
+ /**
112
+ * Upload drive file input schema
113
+ * @description zod schema for validating the input of the upload drive file command
114
+ */
115
+ export const uploadGoogleDriveFileInputSchema = uploadDriveFileInputSchema;
116
+
117
+ export type UploadGoogleDriveFileInput = z.input<typeof uploadGoogleDriveFileInputSchema>;
118
+ export type UploadGoogleDriveFileSchema = z.infer<typeof uploadGoogleDriveFileInputSchema>;
119
+
120
+ /**
121
+ * Filters
122
+ */
123
+ export const googleDriveTableFiltersSchema = driveTableFiltersSchema;
124
+
125
+ export type GoogleDriveTableFilters = z.infer<typeof googleDriveTableFiltersSchema>;
126
+ export type GoogleDriveTableFiltersInput = z.input<typeof googleDriveTableFiltersSchema>;
127
+
128
+ /**
129
+ * List drive tree input schema
130
+ * @description zod schema for validating the input of the list drive tree command
131
+ */
132
+ export const listGoogleDriveTreeInputSchema = listDriveTreeInputSchema;
133
+
134
+ export type ListGoogleDriveTreeInput = z.input<typeof listGoogleDriveTreeInputSchema>;
135
+ export type ListGoogleDriveTreeSchema = z.infer<typeof listGoogleDriveTreeInputSchema>;
136
+
137
+ /**
138
+ * Get drive nodes by parent id input schema
139
+ * @description zod schema for validating the input of the get drive nodes by parent id command
140
+ */
141
+ export const getGoogleDriveNodesByParentIdInputSchema = getNodesByParentIdInputSchema;
142
+
143
+ export type GetGoogleDriveNodesByParentIdInput = z.input<
144
+ typeof getGoogleDriveNodesByParentIdInputSchema
145
+ >;
146
+ export type GetGoogleDriveNodesByParentIdSchema = z.infer<
147
+ typeof getGoogleDriveNodesByParentIdInputSchema
148
+ >;
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ import { zodResolver } from "@hookform/resolvers/zod";
4
+ import type { DefaultError, MutationOptions } from "@tanstack/react-query";
5
+ import {
6
+ CommandFormDialog,
7
+ CommandFormDialogCancel,
8
+ CommandFormDialogContent,
9
+ CommandFormDialogFields,
10
+ CommandFormDialogFooter,
11
+ CommandFormDialogHeader,
12
+ CommandFormDialogSubmit,
13
+ CommandFormDialogTitle,
14
+ CommandFormDialogTrigger,
15
+ CommandLabel,
16
+ } from "@tulip-systems/core/commands/client";
17
+ import { Input } from "@tulip-systems/core/components";
18
+ import {
19
+ Form,
20
+ FormControl,
21
+ FormField,
22
+ FormItem,
23
+ FormLabel,
24
+ FormMessage,
25
+ } from "@tulip-systems/core/components/client";
26
+ import { FilePen } from "lucide-react";
27
+ import { useForm } from "react-hook-form";
28
+ import { type UpdateLocalDriveNodeInput, updateLocalDriveNodeInputSchema } from "../lib/validators";
29
+
30
+ export type LocalDriveFileUpdateCommandProps<
31
+ TData = unknown,
32
+ TError = DefaultError,
33
+ TVariables = unknown,
34
+ TOnMutateResult = unknown,
35
+ > = {
36
+ defaultValues: Partial<UpdateLocalDriveNodeInput>;
37
+ variables: (values: UpdateLocalDriveNodeInput) => TVariables;
38
+ mutation: MutationOptions<TData, TError, TVariables, TOnMutateResult>;
39
+ };
40
+
41
+ export function LocalDriveFileUpdateCommand<
42
+ TData = unknown,
43
+ TError = DefaultError,
44
+ TVariables = unknown,
45
+ TOnMutateResult = unknown,
46
+ >({
47
+ defaultValues,
48
+ variables,
49
+ mutation,
50
+ }: LocalDriveFileUpdateCommandProps<TData, TError, TVariables, TOnMutateResult>) {
51
+ const form = useForm<UpdateLocalDriveNodeInput>({
52
+ resolver: zodResolver(updateLocalDriveNodeInputSchema),
53
+ defaultValues,
54
+ });
55
+
56
+ return (
57
+ <Form {...form}>
58
+ <CommandFormDialog>
59
+ <CommandFormDialogTrigger label="Wijzigen">
60
+ <FilePen className="size-4" />
61
+ <CommandLabel />
62
+ </CommandFormDialogTrigger>
63
+
64
+ <CommandFormDialogContent variables={variables} mutation={mutation}>
65
+ <CommandFormDialogHeader>
66
+ <CommandFormDialogTitle>Bestand wijzigen</CommandFormDialogTitle>
67
+ </CommandFormDialogHeader>
68
+
69
+ <CommandFormDialogFields>
70
+ <FormField
71
+ control={form.control}
72
+ name="name"
73
+ render={({ field }) => (
74
+ <FormItem>
75
+ <FormLabel>Naam</FormLabel>
76
+ <FormControl>
77
+ <Input {...field} value={field.value ?? ""} />
78
+ </FormControl>
79
+ <FormMessage />
80
+ </FormItem>
81
+ )}
82
+ />
83
+ </CommandFormDialogFields>
84
+
85
+ <CommandFormDialogFooter>
86
+ <CommandFormDialogCancel>Annuleren</CommandFormDialogCancel>
87
+ <CommandFormDialogSubmit>Opslaan</CommandFormDialogSubmit>
88
+ </CommandFormDialogFooter>
89
+ </CommandFormDialogContent>
90
+ </CommandFormDialog>
91
+ </Form>
92
+ );
93
+ }
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import { CommandEmpty, CommandLabel } from "@tulip-systems/core/commands/client";
4
+ import { Input } from "@tulip-systems/core/components";
5
+ import { cn } from "@tulip-systems/core/lib";
6
+ import { UploadIcon } from "lucide-react";
7
+ import type { ComponentProps } from "react";
8
+ import { useLocalDriveUploadZone } from "./upload-zone-context.client";
9
+
10
+ export function LocalDriveFilesUploadCommand({ className, ...props }: ComponentProps<"div">) {
11
+ const { onUpload } = useLocalDriveUploadZone();
12
+
13
+ return (
14
+ <CommandEmpty {...props} className={cn("relative gap-2", className)} label="Upload">
15
+ <UploadIcon className="h-3 w-3" />
16
+ <CommandLabel />
17
+
18
+ <Input
19
+ type="file"
20
+ className="absolute inset-0 z-10 h-full w-full cursor-pointer opacity-0"
21
+ multiple
22
+ onChange={(event) => {
23
+ if (!event.target.files) return;
24
+ Array.from(event.target.files).forEach(onUpload);
25
+ }}
26
+ />
27
+ </CommandEmpty>
28
+ );
29
+ }
@@ -0,0 +1,100 @@
1
+ "use client";
2
+
3
+ import { zodResolver } from "@hookform/resolvers/zod";
4
+ import type { DefaultError, MutationOptions } from "@tanstack/react-query";
5
+ import {
6
+ CommandFormDialog,
7
+ CommandFormDialogCancel,
8
+ CommandFormDialogContent,
9
+ CommandFormDialogFields,
10
+ CommandFormDialogFooter,
11
+ CommandFormDialogHeader,
12
+ CommandFormDialogSubmit,
13
+ CommandFormDialogTitle,
14
+ CommandFormDialogTrigger,
15
+ CommandLabel,
16
+ } from "@tulip-systems/core/commands/client";
17
+ import { Input } from "@tulip-systems/core/components";
18
+ import {
19
+ Form,
20
+ FormControl,
21
+ FormField,
22
+ FormItem,
23
+ FormLabel,
24
+ FormMessage,
25
+ } from "@tulip-systems/core/components/client";
26
+ import { FolderPlus } from "lucide-react";
27
+ import { useQueryStates } from "nuqs";
28
+ import { useForm } from "react-hook-form";
29
+ import { driveTreeSearchParams } from "@/lib/search-params";
30
+ import {
31
+ type CreateLocalDriveFolderInput,
32
+ createLocalDriveFolderInputSchema,
33
+ } from "../lib/validators";
34
+
35
+ export type LocalDriveFoldersCreateCommandProps<
36
+ TData = unknown,
37
+ TError = DefaultError,
38
+ TVariables = unknown,
39
+ TOnMutateResult = unknown,
40
+ > = {
41
+ defaultValues?: Partial<CreateLocalDriveFolderInput>;
42
+ variables: (values: CreateLocalDriveFolderInput) => TVariables;
43
+ mutation: MutationOptions<TData, TError, TVariables, TOnMutateResult>;
44
+ };
45
+
46
+ export function LocalDriveFoldersCreateCommand<
47
+ TData = unknown,
48
+ TError = DefaultError,
49
+ TVariables = unknown,
50
+ TOnMutateResult = unknown,
51
+ >({
52
+ defaultValues,
53
+ variables,
54
+ mutation,
55
+ }: LocalDriveFoldersCreateCommandProps<TData, TError, TVariables, TOnMutateResult>) {
56
+ const [{ parentId }] = useQueryStates(driveTreeSearchParams);
57
+
58
+ const form = useForm<CreateLocalDriveFolderInput>({
59
+ resolver: zodResolver(createLocalDriveFolderInputSchema),
60
+ defaultValues: { ...defaultValues, parentId },
61
+ });
62
+
63
+ return (
64
+ <Form {...form}>
65
+ <CommandFormDialog>
66
+ <CommandFormDialogTrigger label="Folder toevoegen" hotkey="mod+i">
67
+ <FolderPlus className="size-4" />
68
+ <CommandLabel />
69
+ </CommandFormDialogTrigger>
70
+
71
+ <CommandFormDialogContent variables={variables} mutation={mutation}>
72
+ <CommandFormDialogHeader>
73
+ <CommandFormDialogTitle>Folder toevoegen</CommandFormDialogTitle>
74
+ </CommandFormDialogHeader>
75
+
76
+ <CommandFormDialogFields>
77
+ <FormField
78
+ control={form.control}
79
+ name="name"
80
+ render={({ field }) => (
81
+ <FormItem>
82
+ <FormLabel>Naam</FormLabel>
83
+ <FormControl>
84
+ <Input {...field} value={field.value ?? ""} />
85
+ </FormControl>
86
+ <FormMessage />
87
+ </FormItem>
88
+ )}
89
+ />
90
+ </CommandFormDialogFields>
91
+
92
+ <CommandFormDialogFooter>
93
+ <CommandFormDialogCancel>Annuleren</CommandFormDialogCancel>
94
+ <CommandFormDialogSubmit>Toevoegen</CommandFormDialogSubmit>
95
+ </CommandFormDialogFooter>
96
+ </CommandFormDialogContent>
97
+ </CommandFormDialog>
98
+ </Form>
99
+ );
100
+ }
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ import { zodResolver } from "@hookform/resolvers/zod";
4
+ import type { DefaultError, MutationOptions } from "@tanstack/react-query";
5
+ import {
6
+ CommandFormDialog,
7
+ CommandFormDialogCancel,
8
+ CommandFormDialogContent,
9
+ CommandFormDialogFields,
10
+ CommandFormDialogFooter,
11
+ CommandFormDialogHeader,
12
+ CommandFormDialogSubmit,
13
+ CommandFormDialogTitle,
14
+ CommandFormDialogTrigger,
15
+ CommandLabel,
16
+ } from "@tulip-systems/core/commands/client";
17
+ import { Input } from "@tulip-systems/core/components";
18
+ import {
19
+ Form,
20
+ FormControl,
21
+ FormField,
22
+ FormItem,
23
+ FormLabel,
24
+ FormMessage,
25
+ } from "@tulip-systems/core/components/client";
26
+ import { FolderPen } from "lucide-react";
27
+ import { useForm } from "react-hook-form";
28
+ import { type UpdateLocalDriveNodeInput, updateLocalDriveNodeInputSchema } from "../lib/validators";
29
+
30
+ export type LocalDriveFoldersUpdateCommandProps<
31
+ TData = unknown,
32
+ TError = DefaultError,
33
+ TVariables = unknown,
34
+ TOnMutateResult = unknown,
35
+ > = {
36
+ defaultValues: Partial<UpdateLocalDriveNodeInput>;
37
+ variables: (values: UpdateLocalDriveNodeInput) => TVariables;
38
+ mutation: MutationOptions<TData, TError, TVariables, TOnMutateResult>;
39
+ };
40
+
41
+ export function LocalDriveFoldersUpdateCommand<
42
+ TData = unknown,
43
+ TError = DefaultError,
44
+ TVariables = unknown,
45
+ TOnMutateResult = unknown,
46
+ >({
47
+ defaultValues,
48
+ variables,
49
+ mutation,
50
+ }: LocalDriveFoldersUpdateCommandProps<TData, TError, TVariables, TOnMutateResult>) {
51
+ const form = useForm<UpdateLocalDriveNodeInput>({
52
+ resolver: zodResolver(updateLocalDriveNodeInputSchema),
53
+ defaultValues,
54
+ });
55
+
56
+ return (
57
+ <Form {...form}>
58
+ <CommandFormDialog>
59
+ <CommandFormDialogTrigger label="Wijzigen">
60
+ <FolderPen className="size-4" />
61
+ <CommandLabel />
62
+ </CommandFormDialogTrigger>
63
+
64
+ <CommandFormDialogContent variables={variables} mutation={mutation}>
65
+ <CommandFormDialogHeader>
66
+ <CommandFormDialogTitle>Folder wijzigen</CommandFormDialogTitle>
67
+ </CommandFormDialogHeader>
68
+
69
+ <CommandFormDialogFields>
70
+ <FormField
71
+ control={form.control}
72
+ name="name"
73
+ render={({ field }) => (
74
+ <FormItem>
75
+ <FormLabel>Naam</FormLabel>
76
+ <FormControl>
77
+ <Input {...field} value={field.value ?? ""} />
78
+ </FormControl>
79
+ <FormMessage />
80
+ </FormItem>
81
+ )}
82
+ />
83
+ </CommandFormDialogFields>
84
+
85
+ <CommandFormDialogFooter>
86
+ <CommandFormDialogCancel>Annuleren</CommandFormDialogCancel>
87
+ <CommandFormDialogSubmit>Opslaan</CommandFormDialogSubmit>
88
+ </CommandFormDialogFooter>
89
+ </CommandFormDialogContent>
90
+ </CommandFormDialog>
91
+ </Form>
92
+ );
93
+ }
@@ -0,0 +1,3 @@
1
+ import { DriveContent } from "@/components/content";
2
+
3
+ export const LocalDriveContent = DriveContent;
@@ -0,0 +1,23 @@
1
+ "use client";
2
+
3
+ import {
4
+ DriveHeader,
5
+ DriveHeaderBreadcrumbs,
6
+ DriveHeaderTools,
7
+ } from "@/components/navigation/header.client";
8
+ import {
9
+ DriveToolbar,
10
+ DriveToolbarBreadcrumbs,
11
+ DriveToolbarTools,
12
+ } from "@/components/navigation/toolbar.client";
13
+ import { DriveViewSwitcher } from "@/components/navigation/view-switcher.client";
14
+
15
+ export type { DriveBreadcrumb } from "@/components/navigation/breadcrumbs.client";
16
+
17
+ export const LocalDriveHeader = DriveHeader;
18
+ export const LocalDriveHeaderBreadcrumbs = DriveHeaderBreadcrumbs;
19
+ export const LocalDriveHeaderTools = DriveHeaderTools;
20
+ export const LocalDriveToolbar = DriveToolbar;
21
+ export const LocalDriveToolbarBreadcrumbs = DriveToolbarBreadcrumbs;
22
+ export const LocalDriveToolbarTools = DriveToolbarTools;
23
+ export const LocalDriveViewSwitcher = DriveViewSwitcher;
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ import { Allowed } from "@tulip-systems/core/auth/client";
4
+ import { createContext, type PropsWithChildren, use } from "react";
5
+ import type { DriveContextValue } from "@/components/context.client";
6
+ import { DriveDragDropProvider, type DriveDragDropProviderProps } from "@/components/dnd.client";
7
+ import {
8
+ type DriveSelectionContextValue,
9
+ DriveSelectionProvider,
10
+ } from "@/components/selection.client";
11
+ import { DriveViewProvider, type DriveViewProviderProps } from "@/components/view.client";
12
+ import { LocalDriveUploadZone, type LocalDriveUploadZoneProps } from "./upload-zone.client";
13
+
14
+ export type LocalDriveContextValue = DriveContextValue & {
15
+ readonly?: boolean;
16
+ };
17
+
18
+ export const LocalDriveContext = createContext({} as LocalDriveContextValue);
19
+
20
+ export function useLocalDriveContext() {
21
+ const context = use(LocalDriveContext);
22
+ if (!context) throw new Error("LocalDriveContext not found!");
23
+ return context;
24
+ }
25
+
26
+ export type LocalDriveProviderProps = PropsWithChildren<
27
+ LocalDriveContextValue &
28
+ Partial<DriveSelectionContextValue> &
29
+ Pick<DriveViewProviderProps, "initialView"> &
30
+ Pick<DriveDragDropProviderProps, "onMove"> &
31
+ Pick<
32
+ LocalDriveUploadZoneProps,
33
+ | "driveUploadClient"
34
+ | "uploadHooks"
35
+ | "onUploadCompleted"
36
+ | "onUploadFailed"
37
+ | "optimistic"
38
+ | "disabled"
39
+ | "className"
40
+ > & {
41
+ parentId?: string | null;
42
+ }
43
+ >;
44
+
45
+ export function LocalDriveProvider({
46
+ initialView = "grid",
47
+ namespace,
48
+ parentId,
49
+ permission,
50
+ meta,
51
+ readonly = false,
52
+ selection,
53
+ selectionConditions,
54
+ onMove,
55
+ driveUploadClient,
56
+ uploadHooks,
57
+ onUploadCompleted,
58
+ onUploadFailed,
59
+ optimistic,
60
+ disabled,
61
+ className,
62
+ children,
63
+ }: LocalDriveProviderProps) {
64
+ const isDisabled = disabled ?? readonly;
65
+
66
+ return (
67
+ <Allowed permission={permission}>
68
+ <DriveSelectionProvider selection={selection} selectionConditions={selectionConditions}>
69
+ <DriveViewProvider initialView={initialView}>
70
+ <DriveDragDropProvider disabled={isDisabled} onMove={onMove}>
71
+ <LocalDriveContext value={{ namespace, permission, meta, readonly }}>
72
+ <LocalDriveUploadZone
73
+ disabled={isDisabled}
74
+ className={className}
75
+ variables={{ namespace, parentId }}
76
+ driveUploadClient={driveUploadClient}
77
+ uploadHooks={uploadHooks}
78
+ onUploadCompleted={onUploadCompleted}
79
+ onUploadFailed={onUploadFailed}
80
+ optimistic={optimistic}
81
+ >
82
+ {children}
83
+ </LocalDriveUploadZone>
84
+ </LocalDriveContext>
85
+ </DriveDragDropProvider>
86
+ </DriveViewProvider>
87
+ </DriveSelectionProvider>
88
+ </Allowed>
89
+ );
90
+ }
@@ -0,0 +1,43 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import type { LocalDriveUploadClient } from "@/providers/local/lib/upload.client";
5
+ import type { LocalDriveFileNode, LocalDriveNodeWithAsset } from "@/providers/local/lib/validators";
6
+
7
+ /**
8
+ * Upload Zone Context Value
9
+ */
10
+ export type LocalDriveUploadZoneContextValue = {
11
+ driveUploadClient: LocalDriveUploadClient;
12
+ onUpload: (file: File) => Promise<LocalDriveFileNode>;
13
+ onRemove: (ids: string[]) => Promise<void>;
14
+ optimistic?: {
15
+ add?: (newValue: LocalDriveNodeWithAsset) => Promise<void> | void;
16
+ remove?: (ids: string[]) => Promise<void> | void;
17
+ invalidate?: () => Promise<void> | void;
18
+ cancel?: () => Promise<void> | void;
19
+ };
20
+ disabled?: boolean;
21
+ };
22
+
23
+ /**
24
+ * Upload Zone Context
25
+ */
26
+ export const LocalDriveUploadZoneContext =
27
+ React.createContext<LocalDriveUploadZoneContextValue | null>(null);
28
+
29
+ export type DriveUploadZoneContextValue = LocalDriveUploadZoneContextValue;
30
+ export const DriveUploadZoneContext = LocalDriveUploadZoneContext;
31
+
32
+ /**
33
+ * Upload Zone Hook
34
+ */
35
+ export function useLocalDriveUploadZone() {
36
+ const context = React.useContext(LocalDriveUploadZoneContext);
37
+ if (!context) {
38
+ throw new Error("useUploadZone must be used within an UploadZoneContext provider");
39
+ }
40
+ return context;
41
+ }
42
+
43
+ export const useDriveUploadZone = useLocalDriveUploadZone;