@casinogate/ui 1.11.10 → 1.11.11

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.
@@ -2593,6 +2593,12 @@
2593
2593
  height: calc(var(--cgui-spacing) * 3.5);
2594
2594
  }
2595
2595
  }
2596
+ .cgui\:\[\&\>svg\]\:size-4 {
2597
+ &>svg {
2598
+ width: calc(var(--cgui-spacing) * 4);
2599
+ height: calc(var(--cgui-spacing) * 4);
2600
+ }
2601
+ }
2596
2602
  .cgui\:\[\&\>svg\]\:size-5 {
2597
2603
  &>svg {
2598
2604
  width: calc(var(--cgui-spacing) * 5);
@@ -1,30 +1,31 @@
1
1
  <script lang="ts">
2
- import { cn } from '../../../internal/utils/common.js';
2
+ import { cn, useId } from '../../../internal/utils/common.js';
3
+ import { boxWith, mergeProps } from 'svelte-toolbelt';
3
4
  import { fileUploadPrimitiveClearTriggerStyles } from '../styles.js';
4
5
  import type { FileUploadPrimitiveClearTriggerProps } from '../types.js';
5
- import { FileUploadRootContext } from './file-upload.svelte.js';
6
+ import { FileUploadClearTriggerState } from './file-upload.svelte.js';
7
+
8
+ const uid = $props.id();
6
9
 
7
10
  let {
8
11
  ref = $bindable(null),
12
+ id = useId(uid),
9
13
  children,
10
14
  class: className,
11
15
  ...restProps
12
16
  }: FileUploadPrimitiveClearTriggerProps = $props();
13
17
 
14
- const rootState = FileUploadRootContext.get();
18
+ const state$ = FileUploadClearTriggerState.create({
19
+ ref: boxWith(
20
+ () => ref,
21
+ (v) => (ref = v)
22
+ ),
23
+ id: boxWith(() => id),
24
+ });
15
25
 
16
- function handleClick() {
17
- rootState.clearFiles();
18
- }
26
+ const mergedProps = $derived(mergeProps(restProps, state$.props));
19
27
  </script>
20
28
 
21
- <button
22
- bind:this={ref}
23
- type="button"
24
- class={cn(fileUploadPrimitiveClearTriggerStyles(), className)}
25
- data-slot="file-upload-clear-trigger"
26
- onclick={handleClick}
27
- {...restProps}
28
- >
29
+ <button bind:this={ref} type="button" class={cn(fileUploadPrimitiveClearTriggerStyles(), className)} {...mergedProps}>
29
30
  {@render children?.()}
30
31
  </button>
@@ -4,7 +4,15 @@
4
4
 
5
5
  const { children }: FileUploadPrimitiveContextProps = $props();
6
6
 
7
- const context = FileUploadRootContext.get();
7
+ const context$ = FileUploadRootContext.get();
8
8
  </script>
9
9
 
10
- {@render children?.(context)}
10
+ {@render children?.({
11
+ acceptedFiles: () => context$.opts.acceptedFiles.current,
12
+ rejectedFiles: () => context$.opts.rejectedFiles.current,
13
+ dragging: () => context$.dragging,
14
+ addFiles: context$.addFiles,
15
+ removeFile: context$.removeFile,
16
+ clearFiles: context$.clearFiles,
17
+ openFilePicker: context$.openFilePicker,
18
+ })}
@@ -1,72 +1,44 @@
1
1
  <script lang="ts">
2
- import { cn } from '../../../internal/utils/common.js';
2
+ import { cn, useId } from '../../../internal/utils/common.js';
3
+ import { boxWith, mergeProps } from 'svelte-toolbelt';
3
4
  import { fileUploadPrimitiveDropzoneStyles } from '../styles.js';
4
5
  import type { FileUploadPrimitiveDropzoneProps } from '../types.js';
5
- import { FileUploadRootContext } from './file-upload.svelte.js';
6
+ import { FileUploadDropzoneState } from './file-upload.svelte.js';
7
+
8
+ const uid = $props.id();
6
9
 
7
10
  let {
8
11
  ref = $bindable(null),
12
+ id = useId(uid),
9
13
  disableClick = false,
10
14
  children,
11
15
  class: className,
12
16
  ...restProps
13
17
  }: FileUploadPrimitiveDropzoneProps = $props();
14
18
 
15
- const rootState = FileUploadRootContext.get();
16
-
17
- function handleDragEnter(e: DragEvent) {
18
- e.preventDefault();
19
- e.stopPropagation();
20
- rootState.dragging = true;
21
- }
22
-
23
- function handleDragLeave(e: DragEvent) {
24
- e.preventDefault();
25
- e.stopPropagation();
26
- rootState.dragging = false;
27
- }
28
-
29
- function handleDragOver(e: DragEvent) {
30
- e.preventDefault();
31
- e.stopPropagation();
32
- }
33
-
34
- function handleDrop(e: DragEvent) {
35
- e.preventDefault();
36
- e.stopPropagation();
37
- rootState.dragging = false;
38
-
39
- if (e.dataTransfer?.files) {
40
- rootState.addFiles(e.dataTransfer.files);
41
- }
42
- }
19
+ const state$ = FileUploadDropzoneState.create({
20
+ disabledClick: boxWith(() => disableClick),
21
+ id: boxWith(() => id),
22
+ ref: boxWith(
23
+ () => ref,
24
+ (v) => (ref = v)
25
+ ),
26
+ });
43
27
 
44
- function handleClick() {
45
- if (!disableClick) {
46
- rootState.openFilePicker();
47
- }
48
- }
28
+ const mergedProps = $derived(mergeProps(restProps, state$.props));
49
29
  </script>
50
30
 
51
31
  <div
52
32
  bind:this={ref}
53
- role="presentation"
54
33
  class={cn(
55
34
  fileUploadPrimitiveDropzoneStyles({
56
- dragging: rootState.dragging,
57
- invalid: rootState.opts.invalid.current,
58
- disabled: rootState.opts.disabled.current,
35
+ dragging: state$.root$.dragging,
36
+ invalid: state$.root$.opts.invalid.current,
37
+ disabled: state$.root$.opts.disabled.current,
59
38
  }),
60
- className,
39
+ className
61
40
  )}
62
- data-slot="file-upload-dropzone"
63
- data-dragging={rootState.dragging || undefined}
64
- ondragenter={handleDragEnter}
65
- ondragleave={handleDragLeave}
66
- ondragover={handleDragOver}
67
- ondrop={handleDrop}
68
- onclick={handleClick}
69
- {...restProps}
41
+ {...mergedProps}
70
42
  >
71
43
  {@render children?.()}
72
44
  </div>
@@ -1,35 +1,39 @@
1
1
  <script lang="ts">
2
+ import type { Attachment } from 'svelte/attachments';
2
3
  import type { FileUploadPrimitiveHiddenInputProps } from '../types.js';
3
4
  import { FileUploadRootContext } from './file-upload.svelte.js';
4
5
 
5
6
  let { ref = $bindable(null), ...restProps }: FileUploadPrimitiveHiddenInputProps = $props();
6
7
 
7
- const rootState = FileUploadRootContext.get();
8
+ const root$ = FileUploadRootContext.get();
8
9
 
9
- let inputEl = $state<HTMLInputElement | null>(null);
10
+ const attachEl: Attachment<HTMLInputElement> = (el) => {
11
+ root$.setHiddenInput(el);
10
12
 
11
- $effect(() => {
12
- rootState.setHiddenInput(inputEl);
13
- });
13
+ return () => {
14
+ root$.setHiddenInput(null);
15
+ };
16
+ };
14
17
 
15
18
  function handleChange(e: Event) {
16
19
  const input = e.target as HTMLInputElement;
17
20
  if (input.files) {
18
- rootState.addFiles(input.files);
21
+ root$.addFiles(input.files);
19
22
  input.value = '';
20
23
  }
21
24
  }
22
25
  </script>
23
26
 
24
27
  <input
25
- bind:this={inputEl}
28
+ bind:this={ref}
29
+ {@attach attachEl}
26
30
  type="file"
27
31
  tabindex={-1}
28
32
  style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;"
29
33
  data-slot="file-upload-hidden-input"
30
- accept={rootState.opts.accept.current}
31
- multiple={rootState.opts.maxFiles.current > 1}
32
- disabled={rootState.opts.disabled.current}
34
+ accept={root$.opts.accept.current}
35
+ multiple={root$.opts.maxFiles.current > 1}
36
+ disabled={root$.opts.disabled.current}
33
37
  onchange={handleChange}
34
38
  {...restProps}
35
39
  />
@@ -1,31 +1,41 @@
1
1
  <script lang="ts">
2
- import { cn } from '../../../internal/utils/common.js';
2
+ import { Icon } from '../../icons/index.js';
3
+ import { cn, useId } from '../../../internal/utils/common.js';
4
+ import { boxWith, mergeProps } from 'svelte-toolbelt';
3
5
  import { fileUploadPrimitiveItemDeleteTriggerStyles } from '../styles.js';
4
6
  import type { FileUploadPrimitiveItemDeleteTriggerProps } from '../types.js';
5
- import { FileUploadItemContext, FileUploadRootContext } from './file-upload.svelte.js';
7
+ import { FileUploadDeleteTriggerState } from './file-upload.svelte.js';
8
+
9
+ const uid = $props.id();
6
10
 
7
11
  let {
8
12
  ref = $bindable(null),
13
+ id = useId(uid),
9
14
  children,
10
15
  class: className,
11
16
  ...restProps
12
17
  }: FileUploadPrimitiveItemDeleteTriggerProps = $props();
13
18
 
14
- const rootState = FileUploadRootContext.get();
15
- const itemState = FileUploadItemContext.get();
19
+ const state$ = FileUploadDeleteTriggerState.create({
20
+ ref: boxWith(
21
+ () => ref,
22
+ (v) => (ref = v)
23
+ ),
24
+ id: boxWith(() => id),
25
+ });
16
26
 
17
- function handleClick() {
18
- rootState.removeFile(itemState.opts.file.current);
19
- }
27
+ const mergedProps = $derived(mergeProps(restProps, state$.props));
20
28
  </script>
21
29
 
22
30
  <button
23
31
  bind:this={ref}
24
32
  type="button"
25
33
  class={cn(fileUploadPrimitiveItemDeleteTriggerStyles(), className)}
26
- data-slot="file-upload-item-delete-trigger"
27
- onclick={handleClick}
28
- {...restProps}
34
+ {...mergedProps}
29
35
  >
30
- {@render children?.()}
36
+ {#if children}
37
+ {@render children()}
38
+ {:else}
39
+ <Icon.Cross />
40
+ {/if}
31
41
  </button>
@@ -4,14 +4,9 @@
4
4
  import type { FileUploadPrimitiveItemNameProps } from '../types.js';
5
5
  import { FileUploadItemContext } from './file-upload.svelte.js';
6
6
 
7
- let {
8
- ref = $bindable(null),
9
- children,
10
- class: className,
11
- ...restProps
12
- }: FileUploadPrimitiveItemNameProps = $props();
7
+ let { ref = $bindable(null), children, class: className, ...restProps }: FileUploadPrimitiveItemNameProps = $props();
13
8
 
14
- const itemState = FileUploadItemContext.get();
9
+ const item$ = FileUploadItemContext.get();
15
10
  </script>
16
11
 
17
12
  <div
@@ -23,6 +18,6 @@
23
18
  {#if children}
24
19
  {@render children()}
25
20
  {:else}
26
- {itemState.opts.file.current.name}
21
+ {item$.opts.file.current.name}
27
22
  {/if}
28
23
  </div>
@@ -2,30 +2,18 @@
2
2
  import { cn } from '../../../internal/utils/common.js';
3
3
  import { fileUploadPrimitiveItemPreviewImageStyles } from '../styles.js';
4
4
  import type { FileUploadPrimitiveItemPreviewImageProps } from '../types.js';
5
- import { FileUploadItemContext } from './file-upload.svelte.js';
5
+ import { FileUploadPreviewImageState } from './file-upload.svelte.js';
6
6
 
7
7
  let { ref = $bindable(null), class: className, ...restProps }: FileUploadPrimitiveItemPreviewImageProps = $props();
8
8
 
9
- const itemState = FileUploadItemContext.get();
10
-
11
- let url = $state<string | undefined>(undefined);
12
-
13
- $effect(() => {
14
- const file = itemState.opts.file.current;
15
- const objectUrl = URL.createObjectURL(file);
16
- url = objectUrl;
17
-
18
- return () => {
19
- URL.revokeObjectURL(objectUrl);
20
- };
21
- });
9
+ const state$ = FileUploadPreviewImageState.create();
22
10
  </script>
23
11
 
24
- {#if url}
12
+ {#if state$.imageUrl}
25
13
  <img
26
14
  bind:this={ref}
27
- src={url}
28
- alt={itemState.opts.file.current.name}
15
+ src={state$.imageUrl}
16
+ alt={state$.item$.opts.file.current.name}
29
17
  class={cn(fileUploadPrimitiveItemPreviewImageStyles(), className)}
30
18
  data-slot="file-upload-item-preview-image"
31
19
  {...restProps}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { Icon } from '../../icons/index.js';
2
3
  import { cn } from '../../../internal/utils/common.js';
3
4
  import { fileUploadPrimitiveItemPreviewStyles } from '../styles.js';
4
5
  import type { FileUploadPrimitiveItemPreviewProps } from '../types.js';
@@ -12,9 +13,9 @@
12
13
  ...restProps
13
14
  }: FileUploadPrimitiveItemPreviewProps = $props();
14
15
 
15
- const itemState = FileUploadItemContext.get();
16
+ const item$ = FileUploadItemContext.get();
16
17
 
17
- const isMatch = $derived(Boolean(itemState.opts.file.current.type.match(type)));
18
+ const isMatch = $derived(Boolean(item$.opts.file.current.type.match(type)));
18
19
  </script>
19
20
 
20
21
  {#if isMatch}
@@ -24,6 +25,10 @@
24
25
  data-slot="file-upload-item-preview"
25
26
  {...restProps}
26
27
  >
27
- {@render children?.()}
28
+ {#if children}
29
+ {@render children?.()}
30
+ {:else}
31
+ <Icon.File />
32
+ {/if}
28
33
  </div>
29
34
  {/if}
@@ -11,7 +11,7 @@
11
11
  ...restProps
12
12
  }: FileUploadPrimitiveItemSizeTextProps = $props();
13
13
 
14
- const itemState = FileUploadItemContext.get();
14
+ const item$ = FileUploadItemContext.get();
15
15
  </script>
16
16
 
17
17
  <div
@@ -23,6 +23,6 @@
23
23
  {#if children}
24
24
  {@render children()}
25
25
  {:else}
26
- {formatFileSize(itemState.opts.file.current.size)}
26
+ {formatFileSize(item$.opts.file.current.size)}
27
27
  {/if}
28
28
  </div>
@@ -1,37 +1,49 @@
1
1
  <script lang="ts">
2
- import { cn } from '../../../internal/utils/common.js';
3
- import { box } from 'svelte-toolbelt';
2
+ import { boolAttr } from '../../../internal/utils/attrs.js';
3
+ import { cn, noop } from '../../../internal/utils/common.js';
4
+ import { boxWith } from 'svelte-toolbelt';
4
5
  import { fileUploadPrimitiveRootStyles } from '../styles.js';
5
6
  import type { FileUploadPrimitiveRootProps } from '../types.js';
6
7
  import { FileUploadRootState } from './file-upload.svelte.js';
7
8
 
8
9
  let {
9
10
  ref = $bindable(null),
11
+ acceptedFiles = $bindable([]),
12
+ rejectedFiles = $bindable([]),
10
13
  accept,
11
14
  maxFiles = 1,
12
15
  maxFileSize = Infinity,
13
16
  disabled = false,
14
17
  invalid = false,
15
- onFileAccept,
16
- onFileReject,
17
- onFileChange,
18
+ onFileAccept = noop,
19
+ onFileReject = noop,
20
+ onFileChange = noop,
18
21
  children,
19
22
  class: className,
20
23
  ...restProps
21
24
  }: FileUploadPrimitiveRootProps = $props();
22
25
 
23
- const rootState = FileUploadRootState.create({
24
- accept: box.with(() => accept),
25
- maxFiles: box.with(() => maxFiles),
26
- maxFileSize: box.with(() => maxFileSize),
27
- disabled: box.with(() => disabled),
28
- invalid: box.with(() => invalid),
29
- });
30
-
31
- $effect(() => {
32
- rootState.onFileAccept = onFileAccept;
33
- rootState.onFileReject = onFileReject;
34
- rootState.onFileChange = onFileChange;
26
+ const state$ = FileUploadRootState.create({
27
+ acceptedFiles: boxWith(
28
+ () => acceptedFiles,
29
+ (v) => {
30
+ acceptedFiles = v;
31
+ }
32
+ ),
33
+ rejectedFiles: boxWith(
34
+ () => rejectedFiles,
35
+ (v) => {
36
+ rejectedFiles = v;
37
+ }
38
+ ),
39
+ accept: boxWith(() => accept),
40
+ maxFiles: boxWith(() => maxFiles),
41
+ maxFileSize: boxWith(() => maxFileSize),
42
+ disabled: boxWith(() => disabled),
43
+ invalid: boxWith(() => invalid),
44
+ onFileAccept: boxWith(() => onFileAccept),
45
+ onFileReject: boxWith(() => onFileReject),
46
+ onFileChange: boxWith(() => onFileChange),
35
47
  });
36
48
  </script>
37
49
 
@@ -39,8 +51,8 @@
39
51
  bind:this={ref}
40
52
  class={cn(fileUploadPrimitiveRootStyles({ disabled }), className)}
41
53
  data-slot="file-upload-root"
42
- data-disabled={disabled || undefined}
43
- data-dragging={rootState.dragging || undefined}
54
+ data-disabled={boolAttr(disabled)}
55
+ data-dragging={boolAttr(state$.dragging)}
44
56
  {...restProps}
45
57
  >
46
58
  {@render children?.()}
@@ -1,4 +1,4 @@
1
1
  import type { FileUploadPrimitiveRootProps } from '../types.js';
2
- declare const FileUpload: import("svelte").Component<FileUploadPrimitiveRootProps, {}, "ref">;
2
+ declare const FileUpload: import("svelte").Component<FileUploadPrimitiveRootProps, {}, "ref" | "acceptedFiles" | "rejectedFiles">;
3
3
  type FileUpload = ReturnType<typeof FileUpload>;
4
4
  export default FileUpload;
@@ -1,5 +1,6 @@
1
+ import type { RefAttachment } from '../../../internal/types/common.js';
1
2
  import { Context } from 'runed';
2
- import type { ReadableBoxedValues } from 'svelte-toolbelt';
3
+ import { type ReadableBoxedValues, type WithRefProps, type WritableBoxedValues } from 'svelte-toolbelt';
3
4
  import type { FileAcceptDetails, FileChangeDetails, FileRejectDetails, RejectedFile } from '../types.js';
4
5
  export declare const FileUploadRootContext: Context<FileUploadRootState>;
5
6
  type FileUploadRootStateOpts = ReadableBoxedValues<{
@@ -8,13 +9,17 @@ type FileUploadRootStateOpts = ReadableBoxedValues<{
8
9
  maxFileSize: number;
9
10
  disabled: boolean;
10
11
  invalid: boolean;
12
+ onFileAccept: (details: FileAcceptDetails) => void;
13
+ onFileReject: (details: FileRejectDetails) => void;
14
+ onFileChange: (details: FileChangeDetails) => void;
15
+ }> & WritableBoxedValues<{
16
+ acceptedFiles: File[];
17
+ rejectedFiles: RejectedFile[];
11
18
  }>;
12
19
  export declare class FileUploadRootState {
20
+ #private;
13
21
  static create(opts: FileUploadRootStateOpts): FileUploadRootState;
14
22
  readonly opts: FileUploadRootStateOpts;
15
- acceptedFiles: File[];
16
- rejectedFiles: RejectedFile[];
17
- dragging: boolean;
18
23
  onFileAccept?: (details: FileAcceptDetails) => void;
19
24
  onFileReject?: (details: FileRejectDetails) => void;
20
25
  onFileChange?: (details: FileChangeDetails) => void;
@@ -27,6 +32,34 @@ export declare class FileUploadRootState {
27
32
  clearFiles(): void;
28
33
  private validateFile;
29
34
  private matchesAccept;
35
+ get dragging(): boolean;
36
+ set dragging(value: boolean);
37
+ }
38
+ type FileUploadDropzoneStateOpts = WithRefProps<ReadableBoxedValues<{
39
+ disabledClick: boolean;
40
+ }>>;
41
+ export declare class FileUploadDropzoneState {
42
+ static create(opts: FileUploadDropzoneStateOpts): FileUploadDropzoneState;
43
+ readonly root$: FileUploadRootState;
44
+ readonly opts: FileUploadDropzoneStateOpts;
45
+ readonly attachment: RefAttachment;
46
+ constructor(opts: FileUploadDropzoneStateOpts, root$: FileUploadRootState);
47
+ onDragEnter(e: DragEvent): void;
48
+ onDragLeave(e: DragEvent): void;
49
+ onDragOver(e: DragEvent): void;
50
+ onDrop(e: DragEvent): void;
51
+ onClick(): void;
52
+ readonly props: {
53
+ readonly id: string;
54
+ readonly ondragenter: (e: DragEvent) => void;
55
+ readonly ondragleave: (e: DragEvent) => void;
56
+ readonly ondragover: (e: DragEvent) => void;
57
+ readonly ondrop: (e: DragEvent) => void;
58
+ readonly onclick: () => void;
59
+ readonly 'data-dragging': "" | undefined;
60
+ readonly 'data-slot': "file-upload-dropzone";
61
+ readonly role: "presentation";
62
+ };
30
63
  }
31
64
  export declare const FileUploadItemContext: Context<FileUploadItemState>;
32
65
  type FileUploadItemStateOpts = ReadableBoxedValues<{
@@ -37,5 +70,43 @@ export declare class FileUploadItemState {
37
70
  readonly opts: FileUploadItemStateOpts;
38
71
  constructor(opts: FileUploadItemStateOpts);
39
72
  }
73
+ export declare class FileUploadPreviewImageState {
74
+ #private;
75
+ static create(): FileUploadPreviewImageState;
76
+ readonly item$: FileUploadItemState;
77
+ constructor(item$: FileUploadItemState);
78
+ get imageUrl(): string | null;
79
+ set imageUrl(value: string | null);
80
+ }
81
+ type FileUploadDeleteTriggerStateOpts = WithRefProps<ReadableBoxedValues<{}>>;
82
+ export declare class FileUploadDeleteTriggerState {
83
+ static create(opts: FileUploadDeleteTriggerStateOpts): FileUploadDeleteTriggerState;
84
+ readonly root$: FileUploadRootState;
85
+ readonly item$: FileUploadItemState;
86
+ readonly opts: FileUploadDeleteTriggerStateOpts;
87
+ readonly attachment: RefAttachment;
88
+ constructor(opts: FileUploadDeleteTriggerStateOpts, root$: FileUploadRootState, item$: FileUploadItemState);
89
+ onClick(): void;
90
+ readonly props: {
91
+ readonly id: string;
92
+ readonly onclick: () => void;
93
+ readonly 'data-slot': "file-upload-delete-trigger";
94
+ readonly role: "presentation";
95
+ };
96
+ }
97
+ type FileUploadClearTriggerStateOpts = WithRefProps<ReadableBoxedValues<{}>>;
98
+ export declare class FileUploadClearTriggerState {
99
+ static create(opts: FileUploadClearTriggerStateOpts): FileUploadClearTriggerState;
100
+ readonly root$: FileUploadRootState;
101
+ readonly opts: FileUploadClearTriggerStateOpts;
102
+ readonly attachment: RefAttachment;
103
+ constructor(opts: FileUploadClearTriggerStateOpts, root$: FileUploadRootState);
104
+ onClick(): void;
105
+ readonly props: {
106
+ readonly id: string;
107
+ readonly onclick: () => void;
108
+ readonly 'data-slot': "file-upload-clear-trigger";
109
+ };
110
+ }
40
111
  export declare function formatFileSize(bytes: number): string;
41
112
  export {};
@@ -1,21 +1,27 @@
1
- import { keyWithPrefix } from '../../../internal/utils/common.js';
2
- import { Context } from 'runed';
1
+ import { boolAttr } from '../../../internal/utils/attrs.js';
2
+ import { Context, watch } from 'runed';
3
+ import { attachRef } from 'svelte-toolbelt';
3
4
  // --- Root State ---
4
- export const FileUploadRootContext = new Context(keyWithPrefix('file-upload-root'));
5
+ export const FileUploadRootContext = new Context('file-upload-root');
5
6
  export class FileUploadRootState {
6
7
  static create(opts) {
7
8
  return FileUploadRootContext.set(new FileUploadRootState(opts));
8
9
  }
9
10
  opts;
10
- acceptedFiles = $state([]);
11
- rejectedFiles = $state([]);
12
- dragging = $state(false);
11
+ #dragging = $state(false);
13
12
  onFileAccept;
14
13
  onFileReject;
15
14
  onFileChange;
16
15
  hiddenInputRef = null;
17
16
  constructor(opts) {
18
17
  this.opts = opts;
18
+ this.addFiles = this.addFiles.bind(this);
19
+ this.removeFile = this.removeFile.bind(this);
20
+ this.clearFiles = this.clearFiles.bind(this);
21
+ this.openFilePicker = this.openFilePicker.bind(this);
22
+ this.onFileAccept = opts.onFileAccept.current;
23
+ this.onFileReject = opts.onFileReject.current;
24
+ this.onFileChange = opts.onFileChange.current;
19
25
  }
20
26
  setHiddenInput(input) {
21
27
  this.hiddenInputRef = input;
@@ -43,31 +49,34 @@ export class FileUploadRootState {
43
49
  for (const file of overLimit) {
44
50
  rejected.push({ file, errors: ['TOO_MANY_FILES'] });
45
51
  }
46
- this.acceptedFiles = this.acceptedFiles.length + incoming.length > maxFiles ? incoming : [...this.acceptedFiles, ...incoming];
52
+ this.opts.acceptedFiles.current =
53
+ this.opts.acceptedFiles.current.length + incoming.length > maxFiles
54
+ ? incoming
55
+ : [...this.opts.acceptedFiles.current, ...incoming];
47
56
  this.onFileAccept?.({ files: incoming });
48
57
  }
49
58
  if (rejected.length > 0) {
50
- this.rejectedFiles = [...this.rejectedFiles, ...rejected];
59
+ this.opts.rejectedFiles.current = [...this.opts.rejectedFiles.current, ...rejected];
51
60
  this.onFileReject?.({ files: rejected });
52
61
  }
53
62
  this.onFileChange?.({
54
- acceptedFiles: this.acceptedFiles,
55
- rejectedFiles: this.rejectedFiles,
63
+ acceptedFiles: this.opts.acceptedFiles.current,
64
+ rejectedFiles: this.opts.rejectedFiles.current,
56
65
  });
57
66
  }
58
67
  removeFile(file) {
59
- this.acceptedFiles = this.acceptedFiles.filter((f) => f !== file);
68
+ this.opts.acceptedFiles.current = this.opts.acceptedFiles.current.filter((f) => f !== file);
60
69
  this.onFileChange?.({
61
- acceptedFiles: this.acceptedFiles,
62
- rejectedFiles: this.rejectedFiles,
70
+ acceptedFiles: this.opts.acceptedFiles.current,
71
+ rejectedFiles: this.opts.rejectedFiles.current,
63
72
  });
64
73
  }
65
74
  clearFiles() {
66
- this.acceptedFiles = [];
67
- this.rejectedFiles = [];
75
+ this.opts.acceptedFiles.current = [];
76
+ this.opts.rejectedFiles.current = [];
68
77
  this.onFileChange?.({
69
- acceptedFiles: this.acceptedFiles,
70
- rejectedFiles: this.rejectedFiles,
78
+ acceptedFiles: this.opts.acceptedFiles.current,
79
+ rejectedFiles: this.opts.rejectedFiles.current,
71
80
  });
72
81
  }
73
82
  validateFile(file) {
@@ -98,9 +107,73 @@ export class FileUploadRootState {
98
107
  return file.type === token;
99
108
  });
100
109
  }
110
+ get dragging() {
111
+ return this.#dragging;
112
+ }
113
+ set dragging(value) {
114
+ this.#dragging = value;
115
+ }
116
+ }
117
+ export class FileUploadDropzoneState {
118
+ static create(opts) {
119
+ return new FileUploadDropzoneState(opts, FileUploadRootContext.get());
120
+ }
121
+ root$;
122
+ opts;
123
+ attachment;
124
+ constructor(opts, root$) {
125
+ this.onDragEnter = this.onDragEnter.bind(this);
126
+ this.onDragLeave = this.onDragLeave.bind(this);
127
+ this.onDragOver = this.onDragOver.bind(this);
128
+ this.onDrop = this.onDrop.bind(this);
129
+ this.onClick = this.onClick.bind(this);
130
+ this.root$ = root$;
131
+ this.opts = opts;
132
+ this.attachment = attachRef(opts.ref);
133
+ }
134
+ onDragEnter(e) {
135
+ e.preventDefault();
136
+ e.stopPropagation();
137
+ this.root$.dragging = true;
138
+ }
139
+ onDragLeave(e) {
140
+ e.preventDefault();
141
+ e.stopPropagation();
142
+ this.root$.dragging = false;
143
+ }
144
+ onDragOver(e) {
145
+ e.preventDefault();
146
+ e.stopPropagation();
147
+ }
148
+ onDrop(e) {
149
+ e.preventDefault();
150
+ e.stopPropagation();
151
+ this.root$.dragging = false;
152
+ if (e.dataTransfer?.files) {
153
+ this.root$.addFiles(e.dataTransfer.files);
154
+ }
155
+ }
156
+ onClick() {
157
+ if (this.opts.disabledClick.current) {
158
+ return;
159
+ }
160
+ this.root$.openFilePicker();
161
+ }
162
+ props = $derived.by(() => ({
163
+ id: this.opts.id.current,
164
+ ondragenter: this.onDragEnter,
165
+ ondragleave: this.onDragLeave,
166
+ ondragover: this.onDragOver,
167
+ ondrop: this.onDrop,
168
+ onclick: this.onClick,
169
+ 'data-dragging': boolAttr(this.root$.dragging),
170
+ 'data-slot': 'file-upload-dropzone',
171
+ role: 'presentation',
172
+ ...this.attachment,
173
+ }));
101
174
  }
102
175
  // --- Item State ---
103
- export const FileUploadItemContext = new Context(keyWithPrefix('file-upload-item'));
176
+ export const FileUploadItemContext = new Context('file-upload-item');
104
177
  export class FileUploadItemState {
105
178
  static create(opts) {
106
179
  return FileUploadItemContext.set(new FileUploadItemState(opts));
@@ -110,6 +183,79 @@ export class FileUploadItemState {
110
183
  this.opts = opts;
111
184
  }
112
185
  }
186
+ // -- Preview Image State --
187
+ export class FileUploadPreviewImageState {
188
+ static create() {
189
+ return new FileUploadPreviewImageState(FileUploadItemContext.get());
190
+ }
191
+ item$;
192
+ #imageUrl = $state(null);
193
+ constructor(item$) {
194
+ this.item$ = item$;
195
+ watch(() => this.item$.opts.file.current, (file) => {
196
+ const objectUrl = URL.createObjectURL(file);
197
+ this.imageUrl = objectUrl;
198
+ return () => {
199
+ URL.revokeObjectURL(objectUrl);
200
+ };
201
+ });
202
+ }
203
+ get imageUrl() {
204
+ return this.#imageUrl;
205
+ }
206
+ set imageUrl(value) {
207
+ this.#imageUrl = value;
208
+ }
209
+ }
210
+ export class FileUploadDeleteTriggerState {
211
+ static create(opts) {
212
+ return new FileUploadDeleteTriggerState(opts, FileUploadRootContext.get(), FileUploadItemContext.get());
213
+ }
214
+ root$;
215
+ item$;
216
+ opts;
217
+ attachment;
218
+ constructor(opts, root$, item$) {
219
+ this.onClick = this.onClick.bind(this);
220
+ this.root$ = root$;
221
+ this.item$ = item$;
222
+ this.opts = opts;
223
+ this.attachment = attachRef(opts.ref);
224
+ }
225
+ onClick() {
226
+ this.root$.removeFile(this.item$.opts.file.current);
227
+ }
228
+ props = $derived.by(() => ({
229
+ id: this.opts.id.current,
230
+ onclick: this.onClick,
231
+ 'data-slot': 'file-upload-delete-trigger',
232
+ role: 'presentation',
233
+ ...this.attachment,
234
+ }));
235
+ }
236
+ export class FileUploadClearTriggerState {
237
+ static create(opts) {
238
+ return new FileUploadClearTriggerState(opts, FileUploadRootContext.get());
239
+ }
240
+ root$;
241
+ opts;
242
+ attachment;
243
+ constructor(opts, root$) {
244
+ this.onClick = this.onClick.bind(this);
245
+ this.root$ = root$;
246
+ this.opts = opts;
247
+ this.attachment = attachRef(opts.ref);
248
+ }
249
+ onClick() {
250
+ this.root$.clearFiles();
251
+ }
252
+ props = $derived.by(() => ({
253
+ id: this.opts.id.current,
254
+ onclick: this.onClick,
255
+ 'data-slot': 'file-upload-clear-trigger',
256
+ ...this.attachment,
257
+ }));
258
+ }
113
259
  // --- Utils ---
114
260
  export function formatFileSize(bytes) {
115
261
  if (bytes < 1024)
@@ -9,7 +9,7 @@ export const fileUploadPrimitiveRootStyles = tv({
9
9
  });
10
10
  export const fileUploadPrimitiveDropzoneStyles = tv({
11
11
  base: [
12
- 'cgui:flex cgui:flex-col cgui:items-center cgui:justify-center cgui:gap-2',
12
+ 'cgui:flex cgui:flex-col cgui:items-center cgui:justify-center cgui:gap-2 cgui:p-4',
13
13
  'cgui:rounded-md cgui:border cgui:border-dashed cgui:border-stroke-default',
14
14
  'cgui:cursor-pointer',
15
15
  'cgui:transition-colors cgui:duration-200',
@@ -43,7 +43,7 @@ export const fileUploadPrimitiveItemStyles = tv({
43
43
  });
44
44
  export const fileUploadPrimitiveItemPreviewStyles = tv({
45
45
  base: [
46
- 'cgui:size-10 cgui:shrink-0 cgui:flex cgui:items-center cgui:justify-center cgui:overflow-hidden cgui:rounded-xs',
46
+ 'cgui:size-10 cgui:shrink-0 cgui:flex cgui:items-center cgui:text-icon-regular cgui:[&>svg]:size-6 cgui:justify-center cgui:overflow-hidden cgui:rounded-xs',
47
47
  ],
48
48
  });
49
49
  export const fileUploadPrimitiveItemPreviewImageStyles = tv({
@@ -61,8 +61,9 @@ export const fileUploadPrimitiveItemDeleteTriggerStyles = tv({
61
61
  'cgui:rounded-xs cgui:cursor-pointer',
62
62
  'cgui:text-fg-regular cgui:hover:text-fg-darkest',
63
63
  'cgui:transition-colors cgui:duration-200',
64
+ 'cgui:[&>svg]:size-4 cgui:text-icon-regular',
64
65
  ],
65
66
  });
66
67
  export const fileUploadPrimitiveClearTriggerStyles = tv({
67
- base: ['cgui:cursor-pointer'],
68
+ base: ['cgui:cursor-pointer cgui:[&>svg]:size-4 cgui:text-icon-regular'],
68
69
  });
@@ -1,6 +1,7 @@
1
1
  import type { Without } from '../../internal/types/common.js';
2
2
  import type { WithVariants } from '../../internal/types/composition.js';
3
3
  import type { PrimitiveButtonAttributes, PrimitiveDivAttributes, PrimitiveImgAttributes, PrimitiveInputAttributes, PrimitiveLiAttributes, PrimitiveUListAttributes } from '../../internal/types/html-attributes.js';
4
+ import type { Snippet } from 'svelte';
4
5
  import type { WithChildren, WithElementRef } from 'svelte-toolbelt';
5
6
  import type { FileUploadPrimitiveClearTriggerVariants, FileUploadPrimitiveDropzoneVariants, FileUploadPrimitiveItemDeleteTriggerVariants, FileUploadPrimitiveItemGroupVariants, FileUploadPrimitiveItemNameVariants, FileUploadPrimitiveItemPreviewImageVariants, FileUploadPrimitiveItemPreviewVariants, FileUploadPrimitiveItemSizeTextVariants, FileUploadPrimitiveItemVariants, FileUploadPrimitiveTriggerVariants } from './styles.js';
6
7
  export type FileError = 'TOO_MANY_FILES' | 'FILE_INVALID_TYPE' | 'FILE_TOO_LARGE';
@@ -19,6 +20,8 @@ export type FileChangeDetails = {
19
20
  rejectedFiles: RejectedFile[];
20
21
  };
21
22
  type FileUploadPrimitiveRootPropsWithoutHTML = WithElementRef<WithChildren<{
23
+ acceptedFiles?: File[];
24
+ rejectedFiles?: RejectedFile[];
22
25
  accept?: string;
23
26
  maxFiles?: number;
24
27
  maxFileSize?: number;
@@ -58,16 +61,16 @@ export type FileUploadPrimitiveItemDeleteTriggerProps = FileUploadPrimitiveItemD
58
61
  type FileUploadPrimitiveClearTriggerPropsWithoutHTML = WithVariants<WithElementRef<WithChildren<{}>>, FileUploadPrimitiveClearTriggerVariants>;
59
62
  export type FileUploadPrimitiveClearTriggerProps = FileUploadPrimitiveClearTriggerPropsWithoutHTML & Without<PrimitiveButtonAttributes, FileUploadPrimitiveClearTriggerPropsWithoutHTML>;
60
63
  export type FileUploadContext = {
61
- readonly acceptedFiles: File[];
62
- readonly rejectedFiles: RejectedFile[];
63
- readonly dragging: boolean;
64
+ readonly acceptedFiles: () => File[];
65
+ readonly rejectedFiles: () => RejectedFile[];
66
+ readonly dragging: () => boolean;
64
67
  addFiles(files: FileList | File[]): void;
65
68
  removeFile(file: File): void;
66
69
  clearFiles(): void;
67
70
  openFilePicker(): void;
68
71
  };
69
72
  export type FileUploadPrimitiveContextProps = {
70
- children?: import('svelte').Snippet<[FileUploadContext]>;
73
+ children?: Snippet<[FileUploadContext]>;
71
74
  };
72
75
  type FileUploadRootPropsWithoutHTML = WithElementRef<WithChildren<{
73
76
  accept?: string;
@@ -16,6 +16,7 @@ export { default as Dots } from './dots.svelte';
16
16
  export { default as Error } from './error.svelte';
17
17
  export { default as EyeCrossed } from './eye-crossed.svelte';
18
18
  export { default as Eye } from './eye.svelte';
19
+ export { default as File } from './file.svelte';
19
20
  export { default as Info } from './info.svelte';
20
21
  export { default as Magnifier } from './magnifier.svelte';
21
22
  export { default as Minus } from './minus.svelte';
@@ -16,6 +16,7 @@ export { default as Dots } from './dots.svelte';
16
16
  export { default as Error } from './error.svelte';
17
17
  export { default as EyeCrossed } from './eye-crossed.svelte';
18
18
  export { default as Eye } from './eye.svelte';
19
+ export { default as File } from './file.svelte';
19
20
  export { default as Info } from './info.svelte';
20
21
  export { default as Magnifier } from './magnifier.svelte';
21
22
  export { default as Minus } from './minus.svelte';
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
2
+ ><path
3
+ fill="currentColor"
4
+ fill-rule="evenodd"
5
+ d="M12 2H6a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3v-8h-6a3 3 0 0 1-3-3zm9 7v-.172a3 3 0 0 0-.879-2.12l-3.828-3.83A3 3 0 0 0 14.172 2H14v6a1 1 0 0 0 1 1z"
6
+ clip-rule="evenodd"
7
+ /></svg
8
+ >
@@ -0,0 +1,26 @@
1
+ export default File;
2
+ type File = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const File: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ 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> {
15
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
+ $$bindings?: Bindings;
17
+ } & Exports;
18
+ (internal: unknown, props: {
19
+ $$events?: Events;
20
+ $$slots?: Slots;
21
+ }): Exports & {
22
+ $set?: any;
23
+ $on?: any;
24
+ };
25
+ z_$$bindings?: Bindings;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@casinogate/ui",
3
- "version": "1.11.10",
3
+ "version": "1.11.11",
4
4
  "svelte": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",