compote-ui 0.6.0 → 0.8.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.
@@ -105,7 +105,7 @@
105
105
 
106
106
  <Combobox.Control
107
107
  class={cn(
108
- 'rounded-ark flex min-h-9 items-center gap-1 border px-3 shadow-sm',
108
+ 'flex min-h-9 items-center gap-1 rounded border px-3 shadow-sm',
109
109
  'focus-within:ring-1 focus-within:ring-primary',
110
110
  'data-invalid:border-danger data-invalid:focus-within:ring-danger',
111
111
  multiple && 'flex-wrap py-1'
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { FileUpload } from '@ark-ui/svelte/file-upload';
3
3
  import { getAcceptAttribute } from './utils';
4
- import type { Props } from './types';
4
+ import type { FileUploadDropzoneProps } from './types';
5
5
  import PhUploadSimple from '../../icons/PhUploadSimple.svelte';
6
6
 
7
7
  let {
@@ -10,7 +10,7 @@
10
10
  maxFiles = 1,
11
11
  onFileAccept,
12
12
  ...restProps
13
- }: Props = $props();
13
+ }: FileUploadDropzoneProps = $props();
14
14
 
15
15
  const accept = $derived(getAcceptAttribute(fileType));
16
16
  </script>
@@ -0,0 +1,4 @@
1
+ import type { FileUploadDropzoneProps } from './types';
2
+ declare const FileUploadDropzone: import("svelte").Component<FileUploadDropzoneProps, {}, "">;
3
+ type FileUploadDropzone = ReturnType<typeof FileUploadDropzone>;
4
+ export default FileUploadDropzone;
@@ -0,0 +1,90 @@
1
+ <script lang="ts">
2
+ import { FileUpload, type FileUploadFileError } from '@ark-ui/svelte/file-upload';
3
+ import PhX from '../../icons/PhX.svelte';
4
+ import type { FileUploadProps } from './types';
5
+ import { getAcceptAttribute } from './utils';
6
+ import { getFileIcon } from './icons';
7
+
8
+ const errorMessages: Record<FileUploadFileError, string> = {
9
+ TOO_MANY_FILES: 'Too many files',
10
+ FILE_INVALID_TYPE: 'Invalid file type',
11
+ FILE_TOO_LARGE: 'File too large',
12
+ FILE_TOO_SMALL: 'File too small',
13
+ FILE_INVALID: 'Invalid file',
14
+ FILE_EXISTS: 'File already exists'
15
+ };
16
+
17
+ let {
18
+ fileType,
19
+ label,
20
+ triggerLabel = 'Choose file',
21
+ maxFiles = 1,
22
+ ...restProps
23
+ }: FileUploadProps = $props();
24
+
25
+ const accept = $derived(getAcceptAttribute(fileType));
26
+ </script>
27
+
28
+ <FileUpload.Root {maxFiles} {accept} class="flex flex-col gap-1.5" {...restProps}>
29
+ {#if label}
30
+ <FileUpload.Label class="text-sm font-medium">{label}</FileUpload.Label>
31
+ {/if}
32
+
33
+ <FileUpload.Context>
34
+ {#snippet render(context)}
35
+ {@const ctx = context()}
36
+ {@const accepted = ctx.acceptedFiles}
37
+ {@const rejected = ctx.rejectedFiles}
38
+
39
+ {#if maxFiles === 1}
40
+ {#if accepted.length === 0}
41
+ <FileUpload.Trigger
42
+ class="inline-flex h-9 cursor-pointer items-center gap-2 rounded border px-3 text-sm text-ink transition-colors hover:bg-surface-2 disabled:pointer-events-none disabled:opacity-50"
43
+ >
44
+ {triggerLabel}
45
+ </FileUpload.Trigger>
46
+ {/if}
47
+ {:else}
48
+ <FileUpload.Trigger
49
+ disabled={accepted.length >= maxFiles}
50
+ class="inline-flex h-9 cursor-pointer items-center gap-2 rounded border px-3 text-sm text-ink transition-colors hover:bg-surface-2 disabled:pointer-events-none disabled:opacity-50"
51
+ >
52
+ {triggerLabel}
53
+ </FileUpload.Trigger>
54
+ {/if}
55
+
56
+ <FileUpload.ItemGroup class="flex flex-col gap-1">
57
+ {#each accepted as file (file.name)}
58
+ <FileUpload.Item {file} class="flex items-center gap-2 rounded border px-3 py-2 text-sm">
59
+ <FileUpload.ItemPreview type="image/*">
60
+ <FileUpload.ItemPreviewImage class="size-8 shrink-0 rounded object-cover" />
61
+ </FileUpload.ItemPreview>
62
+ {#if !file.type.startsWith('image/')}
63
+ {@const Icon = getFileIcon(file)}
64
+ <Icon class="size-4 shrink-0" />
65
+ {/if}
66
+ <FileUpload.ItemName class="flex-1 truncate" />
67
+ <FileUpload.ItemSizeText class="shrink-0 text-xs text-ink-dim" />
68
+ <FileUpload.ItemDeleteTrigger
69
+ class="ml-auto shrink-0 cursor-pointer text-ink-dim transition-colors hover:text-ink"
70
+ >
71
+ <PhX class="size-4" />
72
+ </FileUpload.ItemDeleteTrigger>
73
+ </FileUpload.Item>
74
+ {/each}
75
+ </FileUpload.ItemGroup>
76
+
77
+ {#if rejected.length > 0}
78
+ <div class="mt-1 flex flex-col gap-0.5">
79
+ {#each rejected as rejection (rejection.file.name)}
80
+ <p class="text-xs text-red-600">
81
+ {rejection.file.name}: {rejection.errors.map((e) => errorMessages[e]).join(', ')}
82
+ </p>
83
+ {/each}
84
+ </div>
85
+ {/if}
86
+ {/snippet}
87
+ </FileUpload.Context>
88
+
89
+ <FileUpload.HiddenInput />
90
+ </FileUpload.Root>
@@ -0,0 +1,5 @@
1
+ import { FileUpload } from '@ark-ui/svelte/file-upload';
2
+ import type { FileUploadProps } from './types';
3
+ declare const FileUpload: import("svelte").Component<FileUploadProps, {}, "">;
4
+ type FileUpload = ReturnType<typeof FileUpload>;
5
+ export default FileUpload;
@@ -1,6 +1,14 @@
1
1
  import type { FileUploadRootProps } from '@ark-ui/svelte/file-upload';
2
+ export type { FileUploadFileChangeDetails } from '@ark-ui/svelte/file-upload';
2
3
  import type { FileType } from './utils';
3
- export interface Props extends FileUploadRootProps {
4
+ export type { FileType } from './utils';
5
+ export interface FileUploadDropzoneProps extends FileUploadRootProps {
4
6
  fileType: FileType;
5
7
  label: string;
6
8
  }
9
+ export interface FileUploadProps extends Omit<FileUploadRootProps, 'accept'> {
10
+ fileType?: FileType;
11
+ label?: string;
12
+ triggerLabel?: string;
13
+ maxFiles?: number;
14
+ }
@@ -47,7 +47,14 @@
47
47
  }
48
48
  </script>
49
49
 
50
- <Dialog bind:open {title} {description} onOpenChange={(details) => { if (!details.open) onCancel(); }}>
50
+ <Dialog
51
+ bind:open
52
+ {title}
53
+ {description}
54
+ onOpenChange={(details) => {
55
+ if (!details.open) onCancel();
56
+ }}
57
+ >
51
58
  <ImageCropper bind:getCropData src={imageSrc} alt="Crop preview" {aspectRatio} />
52
59
  {#snippet footer()}
53
60
  <Button variant="outline" onclick={onCancel} disabled={processing}>Cancel</Button>
@@ -48,7 +48,8 @@
48
48
  // eslint-disable-next-line no-useless-assignment
49
49
  getCropData = () => imageCropper().getCropData();
50
50
  // eslint-disable-next-line no-useless-assignment
51
- getProcessedImage = (opts?: ProcessImageOptions) => cropImage(src, imageCropper().getCropData(), opts);
51
+ getProcessedImage = (opts?: ProcessImageOptions) =>
52
+ cropImage(src, imageCropper().getCropData(), opts);
52
53
 
53
54
  let cropData = $derived(imageCropper().getCropData());
54
55
  </script>
package/dist/index.d.ts CHANGED
@@ -8,9 +8,9 @@ export { default as Combobox } from './components/combobox/combobox.svelte';
8
8
  export { default as Dialog } from './components/dialog/dialog.svelte';
9
9
  export { default as AlertDialog } from './components/dialog/alert-dialog.svelte';
10
10
  export type { DialogProps, AlertDialogProps } from './components/dialog/dialog.types';
11
- export { default as FileUploadDropzone } from './components/file-upload/dropzone.svelte';
12
- export { default as FileUploadBasicDocument } from './components/file-upload/basic-document.svelte';
13
- export type { FileType } from './components/file-upload/utils';
11
+ export { default as FileUploadDropzone } from './components/file-upload/file-upload-dropzone.svelte';
12
+ export { default as FileUpload } from './components/file-upload/file-upload.svelte';
13
+ export type { FileUploadProps, FileUploadFileChangeDetails, FileType } from './components/file-upload/types';
14
14
  export { default as ImageCropper } from './components/image-cropper/image-cropper.svelte';
15
15
  export type { ImageCropperProps, ImageCropperCropData } from './components/image-cropper/types';
16
16
  export { default as ImageCropDialog } from './components/image-crop-dialog/image-crop-dialog.svelte';
package/dist/index.js CHANGED
@@ -7,8 +7,8 @@ export { default as CheckboxGroup } from './components/checkbox/checkbox-group.s
7
7
  export { default as Combobox } from './components/combobox/combobox.svelte';
8
8
  export { default as Dialog } from './components/dialog/dialog.svelte';
9
9
  export { default as AlertDialog } from './components/dialog/alert-dialog.svelte';
10
- export { default as FileUploadDropzone } from './components/file-upload/dropzone.svelte';
11
- export { default as FileUploadBasicDocument } from './components/file-upload/basic-document.svelte';
10
+ export { default as FileUploadDropzone } from './components/file-upload/file-upload-dropzone.svelte';
11
+ export { default as FileUpload } from './components/file-upload/file-upload.svelte';
12
12
  export { default as ImageCropper } from './components/image-cropper/image-cropper.svelte';
13
13
  export { default as ImageCropDialog } from './components/image-crop-dialog/image-crop-dialog.svelte';
14
14
  export { default as Listbox } from './components/listbox/listbox.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compote-ui",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -1,89 +0,0 @@
1
- <script lang="ts">
2
- import { FileUpload } from '@ark-ui/svelte/file-upload';
3
- import { Field } from '@ark-ui/svelte/field';
4
-
5
- import { getFileIcon } from './icons';
6
-
7
- import { getAcceptAttribute } from './utils';
8
- import type { Props } from './types';
9
- import PhFileText from '../../icons/PhFileText.svelte';
10
-
11
- let {
12
- fileType,
13
- acceptedFiles,
14
- onFileChange = $bindable(),
15
- label,
16
- ...restProps
17
- }: Props = $props();
18
- const accept = $derived(getAcceptAttribute(fileType));
19
- // const id = $props.id();
20
- </script>
21
-
22
- <!-- {id} -->
23
-
24
- <Field.Root class="w-full max-w-sm">
25
- <FileUpload.Root
26
- {...restProps}
27
- maxFiles={1}
28
- {accept}
29
- {acceptedFiles}
30
- {onFileChange}
31
- class="flex flex-col items-start gap-3"
32
- >
33
- {#if label}
34
- <Field.Label>
35
- {label}
36
- </Field.Label>
37
- {/if}
38
- <FileUpload.Context>
39
- {#snippet render(context)}
40
- <div class="flex items-center gap-3">
41
- <!-- Image Preview / Placeholder -->
42
- <div
43
- class="flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl border bg-surface-2"
44
- >
45
- {#if context().acceptedFiles.length > 0}
46
- <FileUpload.ItemGroup>
47
- {#each context().acceptedFiles as file (file.name)}
48
- <FileUpload.Item file={context().acceptedFiles[0]}>
49
- {#if file.type.startsWith('image/')}
50
- <FileUpload.ItemPreview type="image/*">
51
- <FileUpload.ItemPreviewImage class="h-full w-full object-cover" />
52
- </FileUpload.ItemPreview>
53
- {:else}
54
- {@const IconComponent = getFileIcon(file)}
55
- <IconComponent />
56
- {/if}
57
- </FileUpload.Item>
58
- {/each}
59
- </FileUpload.ItemGroup>
60
- {:else}
61
- <PhFileText class="h-5 w-5 text-ink-dim" />
62
- {/if}
63
- </div>
64
-
65
- <!-- Upload/Change Button -->
66
- <FileUpload.Trigger
67
- class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white hover:bg-primary/90 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:outline-hidden"
68
- >
69
- {context().acceptedFiles.length > 0 ? 'Change image' : 'Upload image'}
70
- </FileUpload.Trigger>
71
- </div>
72
-
73
- <!-- Filename and Remove -->
74
- {#if context().acceptedFiles.length > 0}
75
- <FileUpload.ItemGroup>
76
- <FileUpload.Item file={context().acceptedFiles[0]} class="flex items-center gap-2">
77
- <FileUpload.ItemName class="text-sm text-ink-dim" />
78
- <FileUpload.ItemDeleteTrigger class="text-sm text-red-500 hover:text-red-600">
79
- Remove
80
- </FileUpload.ItemDeleteTrigger>
81
- </FileUpload.Item>
82
- </FileUpload.ItemGroup>
83
- {/if}
84
- {/snippet}
85
- </FileUpload.Context>
86
-
87
- <FileUpload.HiddenInput />
88
- </FileUpload.Root>
89
- </Field.Root>
@@ -1,4 +0,0 @@
1
- import type { Props } from './types';
2
- declare const BasicDocument: import("svelte").Component<Props, {}, "onFileChange">;
3
- type BasicDocument = ReturnType<typeof BasicDocument>;
4
- export default BasicDocument;
@@ -1,50 +0,0 @@
1
- <script lang="ts">
2
- import PhUser from '../../icons/PhUser.svelte';
3
- import { FileUpload } from '@ark-ui/svelte/file-upload';
4
- </script>
5
-
6
- <FileUpload.Root maxFiles={1} accept="image/*" class="flex flex-col items-start gap-3">
7
- <FileUpload.Context>
8
- {#snippet render(context)}
9
- <div class="flex items-center gap-3">
10
- <!-- Image Preview / Placeholder -->
11
- <div
12
- class="flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl border bg-surface-2"
13
- >
14
- {#if context().acceptedFiles.length > 0}
15
- <FileUpload.ItemGroup>
16
- <FileUpload.Item file={context().acceptedFiles[0]}>
17
- <FileUpload.ItemPreview type="image/*">
18
- <FileUpload.ItemPreviewImage class="h-full w-full object-cover" />
19
- </FileUpload.ItemPreview>
20
- </FileUpload.Item>
21
- </FileUpload.ItemGroup>
22
- {:else}
23
- <PhUser class="h-5 w-5 text-ink-dim" />
24
- {/if}
25
- </div>
26
-
27
- <!-- Upload/Change Button -->
28
- <FileUpload.Trigger
29
- class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white hover:bg-primary/90 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:outline-hidden"
30
- >
31
- {context().acceptedFiles.length > 0 ? 'Change image' : 'Upload image'}
32
- </FileUpload.Trigger>
33
- </div>
34
-
35
- <!-- Filename and Remove -->
36
- {#if context().acceptedFiles.length > 0}
37
- <FileUpload.ItemGroup>
38
- <FileUpload.Item file={context().acceptedFiles[0]} class="flex items-center gap-2">
39
- <FileUpload.ItemName class="text-sm text-ink-dim" />
40
- <FileUpload.ItemDeleteTrigger class="text-sm text-red-500 hover:text-red-600">
41
- Remove
42
- </FileUpload.ItemDeleteTrigger>
43
- </FileUpload.Item>
44
- </FileUpload.ItemGroup>
45
- {/if}
46
- {/snippet}
47
- </FileUpload.Context>
48
-
49
- <FileUpload.HiddenInput />
50
- </FileUpload.Root>
@@ -1,18 +0,0 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const Basic: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type Basic = InstanceType<typeof Basic>;
18
- export default Basic;
@@ -1,4 +0,0 @@
1
- import type { Props } from './types';
2
- declare const Dropzone: import("svelte").Component<Props, {}, "">;
3
- type Dropzone = ReturnType<typeof Dropzone>;
4
- export default Dropzone;
@@ -1,97 +0,0 @@
1
- <script lang="ts">
2
- import { FileUpload } from '@ark-ui/svelte/file-upload';
3
- import { getFileIcon } from './icons';
4
- import PhFileText from '../../icons/PhFileText.svelte';
5
- import PhX from '../../icons/PhX.svelte';
6
-
7
- const defaultFiles = [
8
- new File(['Welcome to Ark UI Svelte`'], 'document.pdf', {
9
- type: 'text/plain'
10
- }),
11
- new File(['Welcome to Ark UI Svelte, this is a zip file`'], 'showcase.zip', {
12
- type: 'application/zip'
13
- }),
14
- new File(['Welcome to Ark UI Svelte, this is an excel file`'], 'data.xlsx', {
15
- type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
16
- })
17
- ];
18
- </script>
19
-
20
- <FileUpload.Root
21
- maxFiles={10}
22
- maxFileSize={100 * 1024 * 1024}
23
- class="w-full max-w-2xl space-y-4"
24
- defaultAcceptedFiles={defaultFiles}
25
- >
26
- <FileUpload.Context>
27
- {#snippet render(context)}
28
- <!-- Dropzone -->
29
- <FileUpload.Dropzone
30
- class="flex w-full cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed border-surface-3 bg-surface-2 px-6 py-12 transition-colors hover:bg-surface-3"
31
- >
32
- <!-- File Icon -->
33
- <div
34
- class="mb-4 flex h-12 w-12 items-center justify-center rounded-full border bg-surface-1"
35
- >
36
- <PhFileText class="h-5 w-5 text-ink-dim" />
37
- </div>
38
-
39
- <!-- Text -->
40
- <div class="space-y-2 text-center">
41
- <h3 class="text-sm font-medium text-ink">Upload files</h3>
42
- <p class="text-sm text-ink-dim">Drag & drop or click to browse</p>
43
- <p class="text-xs text-ink-dim">All files • Max 10 files • Up to 100MB</p>
44
- </div>
45
- </FileUpload.Dropzone>
46
-
47
- <!-- Files List -->
48
- {#if context().acceptedFiles.length > 0}
49
- <div class="space-y-3">
50
- <FileUpload.ItemGroup>
51
- {#each context().acceptedFiles as file (file.name)}
52
- <FileUpload.Item {file}>
53
- <div class="flex items-center gap-3 rounded-lg border bg-surface-1 p-3">
54
- <!-- File Icon/Preview -->
55
- <div
56
- class="flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg border bg-surface-2"
57
- >
58
- {#if file.type.startsWith('image/')}
59
- <FileUpload.ItemPreview type="image/*">
60
- <FileUpload.ItemPreviewImage class="h-full w-full object-cover" />
61
- </FileUpload.ItemPreview>
62
- {:else}
63
- {@const IconComponent = getFileIcon(file)}
64
- <IconComponent class="h-4 w-4 opacity-60" />
65
- {/if}
66
- </div>
67
-
68
- <!-- File Info -->
69
- <div class="min-w-0 flex-1">
70
- <FileUpload.ItemName class="truncate text-sm font-medium text-ink" />
71
- <FileUpload.ItemSizeText class="text-xs text-ink-dim" />
72
- </div>
73
-
74
- <!-- Delete Button -->
75
- <FileUpload.ItemDeleteTrigger
76
- class="flex h-6 w-6 shrink-0 items-center justify-center text-ink-dim hover:text-ink"
77
- >
78
- <PhX class="h-4 w-4" />
79
- </FileUpload.ItemDeleteTrigger>
80
- </div>
81
- </FileUpload.Item>
82
- {/each}
83
- </FileUpload.ItemGroup>
84
-
85
- <!-- Remove All Button -->
86
- <FileUpload.ClearTrigger
87
- class="inline-flex items-center rounded-md border bg-surface-1 px-3 py-1.5 text-xs font-medium text-ink hover:bg-surface-2 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:outline-hidden"
88
- >
89
- Remove all files
90
- </FileUpload.ClearTrigger>
91
- </div>
92
- {/if}
93
- {/snippet}
94
- </FileUpload.Context>
95
-
96
- <FileUpload.HiddenInput />
97
- </FileUpload.Root>
@@ -1,18 +0,0 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const FilesList: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type FilesList = InstanceType<typeof FilesList>;
18
- export default FilesList;