compote-ui 0.63.2 → 0.64.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.
- package/dist/components/image-upload/image-upload.svelte +111 -0
- package/dist/components/image-upload/image-upload.svelte.d.ts +4 -0
- package/dist/components/image-upload/types.d.ts +23 -0
- package/dist/components/image-upload/types.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from 'tailwind-variants';
|
|
3
|
+
import * as Menu from '../menu';
|
|
4
|
+
import ImageCropDialog from '../image-crop-dialog/image-crop-dialog.svelte';
|
|
5
|
+
import { fileToDataUrl } from '../../utils/image-processing';
|
|
6
|
+
import { PhUploadSimple, PhImage, PhX } from '../../icons';
|
|
7
|
+
import type { ImageUploadProps } from './types';
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
value = $bindable(),
|
|
11
|
+
alt = 'Image',
|
|
12
|
+
shape = 'circle',
|
|
13
|
+
class: className,
|
|
14
|
+
disabled = false,
|
|
15
|
+
aspectRatio,
|
|
16
|
+
processOptions,
|
|
17
|
+
uploadLabel = 'Upload image',
|
|
18
|
+
replaceLabel = 'Replace',
|
|
19
|
+
deleteLabel = 'Delete',
|
|
20
|
+
onChange,
|
|
21
|
+
onDelete
|
|
22
|
+
}: ImageUploadProps = $props();
|
|
23
|
+
|
|
24
|
+
let inputEl = $state<HTMLInputElement>();
|
|
25
|
+
let cropOpen = $state(false);
|
|
26
|
+
let cropSrc = $state('');
|
|
27
|
+
|
|
28
|
+
function openPicker() {
|
|
29
|
+
inputEl?.click();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function handleInputChange(e: Event) {
|
|
33
|
+
const input = e.currentTarget as HTMLInputElement;
|
|
34
|
+
const file = input.files?.[0];
|
|
35
|
+
input.value = '';
|
|
36
|
+
if (!file) return;
|
|
37
|
+
cropSrc = await fileToDataUrl(file);
|
|
38
|
+
cropOpen = true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function handleCropConfirm(blob: Blob) {
|
|
42
|
+
cropOpen = false;
|
|
43
|
+
if (value?.startsWith('blob:')) URL.revokeObjectURL(value);
|
|
44
|
+
value = URL.createObjectURL(blob);
|
|
45
|
+
onChange?.(blob);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function handleDelete() {
|
|
49
|
+
if (value?.startsWith('blob:')) URL.revokeObjectURL(value);
|
|
50
|
+
value = undefined;
|
|
51
|
+
onDelete?.();
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<div
|
|
56
|
+
class={cn(
|
|
57
|
+
'relative inline-flex size-24 shrink-0 items-center justify-center overflow-hidden bg-surface-2 text-ink-dim',
|
|
58
|
+
shape === 'circle' ? 'rounded-full' : 'rounded-lg',
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
{#if value}
|
|
63
|
+
<img src={value} {alt} class="h-full w-full object-cover" />
|
|
64
|
+
{:else}
|
|
65
|
+
<PhImage class="size-1/3" />
|
|
66
|
+
{/if}
|
|
67
|
+
|
|
68
|
+
<Menu.Root positioning={{ placement: 'bottom-end' }}>
|
|
69
|
+
<Menu.Trigger
|
|
70
|
+
variant="outline"
|
|
71
|
+
size="icon-sm"
|
|
72
|
+
class={cn(
|
|
73
|
+
'absolute right-1 bottom-1 rounded-full bg-surface-1 shadow-sm',
|
|
74
|
+
disabled && 'pointer-events-none opacity-50'
|
|
75
|
+
)}
|
|
76
|
+
>
|
|
77
|
+
<span class="sr-only">{value ? 'Edit image' : uploadLabel}</span>
|
|
78
|
+
<PhUploadSimple class="size-4" />
|
|
79
|
+
</Menu.Trigger>
|
|
80
|
+
<Menu.Content>
|
|
81
|
+
<Menu.Item value="upload" {disabled} onSelect={openPicker}>
|
|
82
|
+
<PhUploadSimple class="size-4" />
|
|
83
|
+
{value ? replaceLabel : uploadLabel}
|
|
84
|
+
</Menu.Item>
|
|
85
|
+
{#if value}
|
|
86
|
+
<Menu.Item value="delete" {disabled} onSelect={handleDelete}>
|
|
87
|
+
<PhX class="size-4" />
|
|
88
|
+
{deleteLabel}
|
|
89
|
+
</Menu.Item>
|
|
90
|
+
{/if}
|
|
91
|
+
</Menu.Content>
|
|
92
|
+
</Menu.Root>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<input
|
|
96
|
+
bind:this={inputEl}
|
|
97
|
+
type="file"
|
|
98
|
+
accept="image/*"
|
|
99
|
+
{disabled}
|
|
100
|
+
class="hidden"
|
|
101
|
+
onchange={handleInputChange}
|
|
102
|
+
/>
|
|
103
|
+
|
|
104
|
+
<ImageCropDialog
|
|
105
|
+
bind:open={cropOpen}
|
|
106
|
+
imageSrc={cropSrc}
|
|
107
|
+
{aspectRatio}
|
|
108
|
+
{processOptions}
|
|
109
|
+
onConfirm={handleCropConfirm}
|
|
110
|
+
onCancel={() => (cropOpen = false)}
|
|
111
|
+
/>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ClassValue } from 'tailwind-variants';
|
|
2
|
+
import type { ProcessImageOptions } from '../../utils/image-processing';
|
|
3
|
+
export type ImageUploadShape = 'circle' | 'square';
|
|
4
|
+
export interface ImageUploadProps {
|
|
5
|
+
/** Current image src (data URL, blob URL, or remote URL). Bindable. */
|
|
6
|
+
value?: string;
|
|
7
|
+
alt?: string;
|
|
8
|
+
shape?: ImageUploadShape;
|
|
9
|
+
/** Sizing classes for the preview box, e.g. 'size-24' or 'w-full aspect-video' */
|
|
10
|
+
class?: ClassValue;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** Aspect ratio locked in the crop dialog (undefined = free) */
|
|
13
|
+
aspectRatio?: number;
|
|
14
|
+
/** Resize/convert options applied when cropping or skipping crop */
|
|
15
|
+
processOptions?: ProcessImageOptions;
|
|
16
|
+
uploadLabel?: string;
|
|
17
|
+
replaceLabel?: string;
|
|
18
|
+
deleteLabel?: string;
|
|
19
|
+
/** Called with the processed Blob after the user crops/confirms a new image */
|
|
20
|
+
onChange?: (blob: Blob) => void;
|
|
21
|
+
/** Called when the user deletes the current image */
|
|
22
|
+
onDelete?: () => void;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export { default as ImageCropper } from './components/image-cropper/image-croppe
|
|
|
34
34
|
export type { ImageCropperProps, ImageCropperCropData } from './components/image-cropper/types';
|
|
35
35
|
export { default as ImageCropDialog } from './components/image-crop-dialog/image-crop-dialog.svelte';
|
|
36
36
|
export type { ImageCropDialogProps } from './components/image-crop-dialog/types';
|
|
37
|
+
export { default as ImageUpload } from './components/image-upload/image-upload.svelte';
|
|
38
|
+
export type { ImageUploadProps, ImageUploadShape } from './components/image-upload/types';
|
|
37
39
|
export { default as JsonTreeView } from './components/json-tree-view/json-tree-view.svelte';
|
|
38
40
|
export * as Listbox from './components/listbox';
|
|
39
41
|
export { default as NumberInput } from './components/number-input/number-input.svelte';
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ export { default as FileUploadDropzone } from './components/file-upload/file-upl
|
|
|
25
25
|
export { default as FileUpload } from './components/file-upload/file-upload.svelte';
|
|
26
26
|
export { default as ImageCropper } from './components/image-cropper/image-cropper.svelte';
|
|
27
27
|
export { default as ImageCropDialog } from './components/image-crop-dialog/image-crop-dialog.svelte';
|
|
28
|
+
export { default as ImageUpload } from './components/image-upload/image-upload.svelte';
|
|
28
29
|
export { default as JsonTreeView } from './components/json-tree-view/json-tree-view.svelte';
|
|
29
30
|
export * as Listbox from './components/listbox';
|
|
30
31
|
export { default as NumberInput } from './components/number-input/number-input.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compote-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev --open",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"@sveltejs/vite-plugin-svelte": "7.1.2",
|
|
66
66
|
"@tailwindcss/vite": "^4.3.2",
|
|
67
67
|
"@tanstack/intent": "^0.3.2",
|
|
68
|
-
"@tanstack/svelte-table": "^9.0.0-beta.
|
|
69
|
-
"@tanstack/svelte-virtual": "^3.13.
|
|
68
|
+
"@tanstack/svelte-table": "^9.0.0-beta.26",
|
|
69
|
+
"@tanstack/svelte-virtual": "^3.13.31",
|
|
70
70
|
"@types/node": "^22.20.0",
|
|
71
71
|
"eslint": "^10.5.0",
|
|
72
72
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"typescript": "^6.0.3",
|
|
84
84
|
"typescript-eslint": "^8.61.1",
|
|
85
85
|
"unplugin-icons": "^23.0.1",
|
|
86
|
-
"vite": "^8.
|
|
86
|
+
"vite": "^8.1.2"
|
|
87
87
|
},
|
|
88
88
|
"keywords": [
|
|
89
89
|
"svelte",
|