@krak-stack/registry 0.1.0 → 0.1.2

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.
@@ -18,7 +18,7 @@ export declare const TableSearchSchema: Schema.Struct<{
18
18
  }>>, Schema.String, never, never>, never, never>>;
19
19
  readonly grouping: Schema.optional<Schema.$Array<Schema.String>>;
20
20
  }>;
21
- export declare const TableSearchSchemaStandard: ReturnType<typeof Schema.toStandardSchemaV1>;
21
+ export declare const TableSearchSchemaStandard: ReturnType<typeof Schema.toStandardSchemaV1<typeof TableSearchSchema>>;
22
22
  export type TableParams = Schema.Schema.Type<typeof TableSearchSchema>;
23
23
  export type DataTableMessages = PaginationMessages & {
24
24
  actions: string;
@@ -51,9 +51,10 @@ export type SubmitForm<A, E> = {
51
51
  lastSubmittedValues: Atom.Atom<Option.Option<unknown>>;
52
52
  submit: Atom.AtomResultFn<void, A, E>;
53
53
  };
54
- export declare const SubmitButton: <A, E>({ children, form, }: {
54
+ export declare const SubmitButton: <A, E>({ children, form, onSubmit, }: {
55
55
  children?: ReactNode;
56
56
  form: SubmitForm<A, E>;
57
+ onSubmit?: () => void;
57
58
  }) => JSX.Element;
58
59
  type TextFieldOptions = FieldOptions & Omit<ComponentProps<"input">, "children" | "defaultValue" | "id" | "name" | "onBlur" | "onChange" | "value"> & {
59
60
  errorMessage?: string;
@@ -287,6 +287,13 @@ function AttachmentAction({
287
287
  ...props
288
288
  }, undefined, false, undefined, this);
289
289
  }
290
+ function AttachmentGroup({ className, ...props }) {
291
+ return /* @__PURE__ */ jsxDEV3("div", {
292
+ "data-slot": "attachment-group",
293
+ className: cn("flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start", className),
294
+ ...props
295
+ }, undefined, false, undefined, this);
296
+ }
290
297
 
291
298
  // ../../src/components/ui/input.tsx
292
299
  import { Input as InputPrimitive } from "@base-ui/react/input";
@@ -735,15 +742,19 @@ var messages = {
735
742
  en: {
736
743
  accepts: (accepts) => `Accepts: ${accepts}`,
737
744
  chooseFile: "Choose file",
745
+ chooseFiles: "Choose files",
738
746
  deleteFile: "Delete",
739
747
  dropFile: "Drag and drop a file here",
748
+ dropFiles: "Drag and drop files here",
740
749
  replaceFile: "Replace file"
741
750
  },
742
751
  fr: {
743
752
  accepts: (accepts) => `Accepte : ${accepts}`,
744
753
  chooseFile: "Choisir un fichier",
754
+ chooseFiles: "Choisir des fichiers",
745
755
  deleteFile: "Supprimer",
746
756
  dropFile: "Glissez-déposez un fichier ici",
757
+ dropFiles: "Glissez-déposez des fichiers ici",
747
758
  replaceFile: "Remplacer le fichier"
748
759
  }
749
760
  };
@@ -768,14 +779,17 @@ var FilePicker = ({
768
779
  accept = "",
769
780
  canClear = false,
770
781
  file,
782
+ files = [],
771
783
  id,
772
784
  image,
773
785
  invalid = false,
774
786
  messages: messages2,
787
+ multiple = false,
775
788
  name,
776
789
  onBlur,
777
790
  onChange,
778
791
  onClear,
792
+ onFilesChange,
779
793
  required,
780
794
  title
781
795
  }) => {
@@ -791,17 +805,23 @@ var FilePicker = ({
791
805
  }, [objectUrl]);
792
806
  const imageUrl = objectUrl ?? image?.src;
793
807
  const selected = Boolean(file || imageUrl);
794
- const selectFile = (nextFile) => {
795
- if (nextFile)
796
- onChange(nextFile);
808
+ const selectFiles = (nextFiles) => {
809
+ const selectedFiles = Array.from(nextFiles);
810
+ if (multiple) {
811
+ const uniqueFiles = new Map([...files, ...selectedFiles].map((selectedFile) => [
812
+ `${selectedFile.name}:${selectedFile.size}:${selectedFile.lastModified}`,
813
+ selectedFile
814
+ ]));
815
+ onFilesChange?.(Array.from(uniqueFiles.values()));
816
+ } else if (selectedFiles[0]) {
817
+ onChange?.(selectedFiles[0]);
818
+ }
797
819
  };
798
820
  const handleDrop = (event) => {
799
821
  event.preventDefault();
800
822
  dragDepthRef.current = 0;
801
823
  setDragging(false);
802
- if (inputRef.current)
803
- inputRef.current.files = event.dataTransfer.files;
804
- selectFile(event.dataTransfer.files[0]);
824
+ selectFiles(event.dataTransfer.files);
805
825
  onBlur?.();
806
826
  };
807
827
  const clear = () => {
@@ -816,11 +836,101 @@ var FilePicker = ({
816
836
  name,
817
837
  type: "file",
818
838
  accept,
819
- required,
839
+ multiple,
840
+ required: required && (!multiple || files.length === 0),
820
841
  onBlur,
821
- onChange: (event) => selectFile(event.target.files?.[0]),
842
+ onChange: (event) => {
843
+ if (event.target.files)
844
+ selectFiles(event.target.files);
845
+ if (multiple)
846
+ event.target.value = "";
847
+ },
822
848
  "aria-invalid": invalid
823
849
  }, undefined, false, undefined, this);
850
+ if (multiple) {
851
+ return /* @__PURE__ */ jsxDEV5("div", {
852
+ className: "grid min-w-0 gap-3",
853
+ children: [
854
+ /* @__PURE__ */ jsxDEV5("div", {
855
+ className: "bg-muted/30 data-[dragging=true]:bg-muted flex min-h-32 min-w-0 flex-col items-center justify-center gap-3 rounded-xl border border-dashed p-6 text-center transition-colors",
856
+ "data-dragging": dragging,
857
+ onDragEnter: (event) => {
858
+ event.preventDefault();
859
+ dragDepthRef.current += 1;
860
+ setDragging(true);
861
+ },
862
+ onDragLeave: (event) => {
863
+ event.preventDefault();
864
+ dragDepthRef.current -= 1;
865
+ if (dragDepthRef.current <= 0) {
866
+ dragDepthRef.current = 0;
867
+ setDragging(false);
868
+ }
869
+ },
870
+ onDragOver: (event) => event.preventDefault(),
871
+ onDrop: handleDrop,
872
+ children: [
873
+ /* @__PURE__ */ jsxDEV5(CloudUpload, {
874
+ className: "text-muted-foreground size-6"
875
+ }, undefined, false, undefined, this),
876
+ /* @__PURE__ */ jsxDEV5("div", {
877
+ className: "max-w-full min-w-0",
878
+ children: [
879
+ /* @__PURE__ */ jsxDEV5("p", {
880
+ className: "text-sm font-medium",
881
+ children: labels.dropFiles
882
+ }, undefined, false, undefined, this),
883
+ /* @__PURE__ */ jsxDEV5("p", {
884
+ className: "text-muted-foreground max-w-full text-xs [overflow-wrap:anywhere]",
885
+ children: labels.accepts(accept)
886
+ }, undefined, false, undefined, this)
887
+ ]
888
+ }, undefined, true, undefined, this),
889
+ /* @__PURE__ */ jsxDEV5(Button, {
890
+ type: "button",
891
+ variant: "outline",
892
+ onClick: () => inputRef.current?.click(),
893
+ children: [
894
+ /* @__PURE__ */ jsxDEV5(FileUp, {
895
+ "data-icon": "inline-start"
896
+ }, undefined, false, undefined, this),
897
+ labels.chooseFiles
898
+ ]
899
+ }, undefined, true, undefined, this),
900
+ input
901
+ ]
902
+ }, undefined, true, undefined, this),
903
+ files.length > 0 ? /* @__PURE__ */ jsxDEV5(AttachmentGroup, {
904
+ children: files.map((selectedFile) => /* @__PURE__ */ jsxDEV5(Attachment, {
905
+ state: invalid ? "error" : "idle",
906
+ children: [
907
+ /* @__PURE__ */ jsxDEV5(AttachmentMedia, {
908
+ children: /* @__PURE__ */ jsxDEV5(FileIcon, {}, undefined, false, undefined, this)
909
+ }, undefined, false, undefined, this),
910
+ /* @__PURE__ */ jsxDEV5(AttachmentContent, {
911
+ children: [
912
+ /* @__PURE__ */ jsxDEV5(AttachmentTitle, {
913
+ children: selectedFile.name
914
+ }, undefined, false, undefined, this),
915
+ /* @__PURE__ */ jsxDEV5(AttachmentDescription, {
916
+ children: fileDescription(selectedFile)
917
+ }, undefined, false, undefined, this)
918
+ ]
919
+ }, undefined, true, undefined, this),
920
+ /* @__PURE__ */ jsxDEV5(AttachmentActions, {
921
+ children: /* @__PURE__ */ jsxDEV5(AttachmentAction, {
922
+ type: "button",
923
+ "aria-label": `${labels.deleteFile} ${selectedFile.name}`,
924
+ onClick: () => onFilesChange?.(files.filter((candidate) => candidate !== selectedFile)),
925
+ children: /* @__PURE__ */ jsxDEV5(Trash2, {}, undefined, false, undefined, this)
926
+ }, undefined, false, undefined, this)
927
+ }, undefined, false, undefined, this)
928
+ ]
929
+ }, `${selectedFile.name}:${selectedFile.size}:${selectedFile.lastModified}`, true, undefined, this))
930
+ }, undefined, false, undefined, this) : null
931
+ ]
932
+ }, undefined, true, undefined, this);
933
+ }
824
934
  if (!selected) {
825
935
  return /* @__PURE__ */ jsxDEV5("div", {
826
936
  className: "bg-muted/30 data-[dragging=true]:bg-muted flex min-h-32 flex-col items-center justify-center gap-3 rounded-xl border border-dashed p-6 text-center transition-colors",
@@ -1615,7 +1725,8 @@ var SubmitError = ({
1615
1725
  };
1616
1726
  var SubmitButton = ({
1617
1727
  children,
1618
- form
1728
+ form,
1729
+ onSubmit
1619
1730
  }) => {
1620
1731
  const submit = useAtomSet(form.submit);
1621
1732
  const submitResult = useAtomValue(form.submit);
@@ -1627,7 +1738,10 @@ var SubmitButton = ({
1627
1738
  type: "button",
1628
1739
  disabled: !hasPendingChanges || submitResult.waiting,
1629
1740
  className: "self-start",
1630
- onClick: () => submit(),
1741
+ onClick: () => {
1742
+ onSubmit?.();
1743
+ submit();
1744
+ },
1631
1745
  children: [
1632
1746
  submitResult.waiting && /* @__PURE__ */ jsxDEV14(Loader2, {
1633
1747
  "data-icon": "inline-start",
@@ -2,8 +2,10 @@ import { type ReactNode } from "react";
2
2
  export type FilePickerMessages = {
3
3
  accepts: (accepts: string) => string;
4
4
  chooseFile: string;
5
+ chooseFiles: string;
5
6
  deleteFile: string;
6
7
  dropFile: string;
8
+ dropFiles: string;
7
9
  replaceFile: string;
8
10
  };
9
11
  export type FilePickerMessageOverrides = Partial<FilePickerMessages>;
@@ -11,6 +13,7 @@ export type FilePickerProps = {
11
13
  accept?: string;
12
14
  canClear?: boolean;
13
15
  file?: File;
16
+ files?: readonly File[];
14
17
  id: string;
15
18
  image?: {
16
19
  alt: string;
@@ -20,11 +23,13 @@ export type FilePickerProps = {
20
23
  };
21
24
  invalid?: boolean;
22
25
  messages?: FilePickerMessageOverrides;
26
+ multiple?: boolean;
23
27
  name: string;
24
28
  onBlur?: () => void;
25
- onChange: (file: File) => void;
29
+ onChange?: (file: File) => void;
26
30
  onClear: () => void;
31
+ onFilesChange?: (files: File[]) => void;
27
32
  required?: boolean;
28
33
  title: ReactNode;
29
34
  };
30
- export declare const FilePicker: ({ accept, canClear, file, id, image, invalid, messages, name, onBlur, onChange, onClear, required, title, }: FilePickerProps) => import("react").JSX.Element;
35
+ export declare const FilePicker: ({ accept, canClear, file, files, id, image, invalid, messages, multiple, name, onBlur, onChange, onClear, onFilesChange, required, title, }: FilePickerProps) => import("react").JSX.Element;
@@ -170,6 +170,13 @@ function AttachmentAction({
170
170
  ...props
171
171
  }, undefined, false, undefined, this);
172
172
  }
173
+ function AttachmentGroup({ className, ...props }) {
174
+ return /* @__PURE__ */ jsxDEV2("div", {
175
+ "data-slot": "attachment-group",
176
+ className: cn("flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start", className),
177
+ ...props
178
+ }, undefined, false, undefined, this);
179
+ }
173
180
 
174
181
  // ../../src/components/ui/input.tsx
175
182
  import { Input as InputPrimitive } from "@base-ui/react/input";
@@ -618,15 +625,19 @@ var messages = {
618
625
  en: {
619
626
  accepts: (accepts) => `Accepts: ${accepts}`,
620
627
  chooseFile: "Choose file",
628
+ chooseFiles: "Choose files",
621
629
  deleteFile: "Delete",
622
630
  dropFile: "Drag and drop a file here",
631
+ dropFiles: "Drag and drop files here",
623
632
  replaceFile: "Replace file"
624
633
  },
625
634
  fr: {
626
635
  accepts: (accepts) => `Accepte : ${accepts}`,
627
636
  chooseFile: "Choisir un fichier",
637
+ chooseFiles: "Choisir des fichiers",
628
638
  deleteFile: "Supprimer",
629
639
  dropFile: "Glissez-déposez un fichier ici",
640
+ dropFiles: "Glissez-déposez des fichiers ici",
630
641
  replaceFile: "Remplacer le fichier"
631
642
  }
632
643
  };
@@ -651,14 +662,17 @@ var FilePicker = ({
651
662
  accept = "",
652
663
  canClear = false,
653
664
  file,
665
+ files = [],
654
666
  id,
655
667
  image,
656
668
  invalid = false,
657
669
  messages: messages2,
670
+ multiple = false,
658
671
  name,
659
672
  onBlur,
660
673
  onChange,
661
674
  onClear,
675
+ onFilesChange,
662
676
  required,
663
677
  title
664
678
  }) => {
@@ -674,17 +688,23 @@ var FilePicker = ({
674
688
  }, [objectUrl]);
675
689
  const imageUrl = objectUrl ?? image?.src;
676
690
  const selected = Boolean(file || imageUrl);
677
- const selectFile = (nextFile) => {
678
- if (nextFile)
679
- onChange(nextFile);
691
+ const selectFiles = (nextFiles) => {
692
+ const selectedFiles = Array.from(nextFiles);
693
+ if (multiple) {
694
+ const uniqueFiles = new Map([...files, ...selectedFiles].map((selectedFile) => [
695
+ `${selectedFile.name}:${selectedFile.size}:${selectedFile.lastModified}`,
696
+ selectedFile
697
+ ]));
698
+ onFilesChange?.(Array.from(uniqueFiles.values()));
699
+ } else if (selectedFiles[0]) {
700
+ onChange?.(selectedFiles[0]);
701
+ }
680
702
  };
681
703
  const handleDrop = (event) => {
682
704
  event.preventDefault();
683
705
  dragDepthRef.current = 0;
684
706
  setDragging(false);
685
- if (inputRef.current)
686
- inputRef.current.files = event.dataTransfer.files;
687
- selectFile(event.dataTransfer.files[0]);
707
+ selectFiles(event.dataTransfer.files);
688
708
  onBlur?.();
689
709
  };
690
710
  const clear = () => {
@@ -699,11 +719,101 @@ var FilePicker = ({
699
719
  name,
700
720
  type: "file",
701
721
  accept,
702
- required,
722
+ multiple,
723
+ required: required && (!multiple || files.length === 0),
703
724
  onBlur,
704
- onChange: (event) => selectFile(event.target.files?.[0]),
725
+ onChange: (event) => {
726
+ if (event.target.files)
727
+ selectFiles(event.target.files);
728
+ if (multiple)
729
+ event.target.value = "";
730
+ },
705
731
  "aria-invalid": invalid
706
732
  }, undefined, false, undefined, this);
733
+ if (multiple) {
734
+ return /* @__PURE__ */ jsxDEV4("div", {
735
+ className: "grid min-w-0 gap-3",
736
+ children: [
737
+ /* @__PURE__ */ jsxDEV4("div", {
738
+ className: "bg-muted/30 data-[dragging=true]:bg-muted flex min-h-32 min-w-0 flex-col items-center justify-center gap-3 rounded-xl border border-dashed p-6 text-center transition-colors",
739
+ "data-dragging": dragging,
740
+ onDragEnter: (event) => {
741
+ event.preventDefault();
742
+ dragDepthRef.current += 1;
743
+ setDragging(true);
744
+ },
745
+ onDragLeave: (event) => {
746
+ event.preventDefault();
747
+ dragDepthRef.current -= 1;
748
+ if (dragDepthRef.current <= 0) {
749
+ dragDepthRef.current = 0;
750
+ setDragging(false);
751
+ }
752
+ },
753
+ onDragOver: (event) => event.preventDefault(),
754
+ onDrop: handleDrop,
755
+ children: [
756
+ /* @__PURE__ */ jsxDEV4(CloudUpload, {
757
+ className: "text-muted-foreground size-6"
758
+ }, undefined, false, undefined, this),
759
+ /* @__PURE__ */ jsxDEV4("div", {
760
+ className: "max-w-full min-w-0",
761
+ children: [
762
+ /* @__PURE__ */ jsxDEV4("p", {
763
+ className: "text-sm font-medium",
764
+ children: labels.dropFiles
765
+ }, undefined, false, undefined, this),
766
+ /* @__PURE__ */ jsxDEV4("p", {
767
+ className: "text-muted-foreground max-w-full text-xs [overflow-wrap:anywhere]",
768
+ children: labels.accepts(accept)
769
+ }, undefined, false, undefined, this)
770
+ ]
771
+ }, undefined, true, undefined, this),
772
+ /* @__PURE__ */ jsxDEV4(Button, {
773
+ type: "button",
774
+ variant: "outline",
775
+ onClick: () => inputRef.current?.click(),
776
+ children: [
777
+ /* @__PURE__ */ jsxDEV4(FileUp, {
778
+ "data-icon": "inline-start"
779
+ }, undefined, false, undefined, this),
780
+ labels.chooseFiles
781
+ ]
782
+ }, undefined, true, undefined, this),
783
+ input
784
+ ]
785
+ }, undefined, true, undefined, this),
786
+ files.length > 0 ? /* @__PURE__ */ jsxDEV4(AttachmentGroup, {
787
+ children: files.map((selectedFile) => /* @__PURE__ */ jsxDEV4(Attachment, {
788
+ state: invalid ? "error" : "idle",
789
+ children: [
790
+ /* @__PURE__ */ jsxDEV4(AttachmentMedia, {
791
+ children: /* @__PURE__ */ jsxDEV4(FileIcon, {}, undefined, false, undefined, this)
792
+ }, undefined, false, undefined, this),
793
+ /* @__PURE__ */ jsxDEV4(AttachmentContent, {
794
+ children: [
795
+ /* @__PURE__ */ jsxDEV4(AttachmentTitle, {
796
+ children: selectedFile.name
797
+ }, undefined, false, undefined, this),
798
+ /* @__PURE__ */ jsxDEV4(AttachmentDescription, {
799
+ children: fileDescription(selectedFile)
800
+ }, undefined, false, undefined, this)
801
+ ]
802
+ }, undefined, true, undefined, this),
803
+ /* @__PURE__ */ jsxDEV4(AttachmentActions, {
804
+ children: /* @__PURE__ */ jsxDEV4(AttachmentAction, {
805
+ type: "button",
806
+ "aria-label": `${labels.deleteFile} ${selectedFile.name}`,
807
+ onClick: () => onFilesChange?.(files.filter((candidate) => candidate !== selectedFile)),
808
+ children: /* @__PURE__ */ jsxDEV4(Trash2, {}, undefined, false, undefined, this)
809
+ }, undefined, false, undefined, this)
810
+ }, undefined, false, undefined, this)
811
+ ]
812
+ }, `${selectedFile.name}:${selectedFile.size}:${selectedFile.lastModified}`, true, undefined, this))
813
+ }, undefined, false, undefined, this) : null
814
+ ]
815
+ }, undefined, true, undefined, this);
816
+ }
707
817
  if (!selected) {
708
818
  return /* @__PURE__ */ jsxDEV4("div", {
709
819
  className: "bg-muted/30 data-[dragging=true]:bg-muted flex min-h-32 flex-col items-center justify-center gap-3 rounded-xl border border-dashed p-6 text-center transition-colors",
@@ -444,6 +444,13 @@ function AttachmentAction({
444
444
  ...props
445
445
  }, undefined, false, undefined, this);
446
446
  }
447
+ function AttachmentGroup({ className, ...props }) {
448
+ return /* @__PURE__ */ jsxDEV6("div", {
449
+ "data-slot": "attachment-group",
450
+ className: cn("flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start", className),
451
+ ...props
452
+ }, undefined, false, undefined, this);
453
+ }
447
454
 
448
455
  // ../../src/paraglide/runtime.js
449
456
  import"@inlang/paraglide-js/urlpattern-polyfill";
@@ -880,15 +887,19 @@ var messages = {
880
887
  en: {
881
888
  accepts: (accepts) => `Accepts: ${accepts}`,
882
889
  chooseFile: "Choose file",
890
+ chooseFiles: "Choose files",
883
891
  deleteFile: "Delete",
884
892
  dropFile: "Drag and drop a file here",
893
+ dropFiles: "Drag and drop files here",
885
894
  replaceFile: "Replace file"
886
895
  },
887
896
  fr: {
888
897
  accepts: (accepts) => `Accepte : ${accepts}`,
889
898
  chooseFile: "Choisir un fichier",
899
+ chooseFiles: "Choisir des fichiers",
890
900
  deleteFile: "Supprimer",
891
901
  dropFile: "Glissez-déposez un fichier ici",
902
+ dropFiles: "Glissez-déposez des fichiers ici",
892
903
  replaceFile: "Remplacer le fichier"
893
904
  }
894
905
  };
@@ -913,14 +924,17 @@ var FilePicker = ({
913
924
  accept = "",
914
925
  canClear = false,
915
926
  file,
927
+ files = [],
916
928
  id,
917
929
  image,
918
930
  invalid = false,
919
931
  messages: messages2,
932
+ multiple = false,
920
933
  name,
921
934
  onBlur,
922
935
  onChange,
923
936
  onClear,
937
+ onFilesChange,
924
938
  required,
925
939
  title
926
940
  }) => {
@@ -936,17 +950,23 @@ var FilePicker = ({
936
950
  }, [objectUrl]);
937
951
  const imageUrl = objectUrl ?? image?.src;
938
952
  const selected = Boolean(file || imageUrl);
939
- const selectFile = (nextFile) => {
940
- if (nextFile)
941
- onChange(nextFile);
953
+ const selectFiles = (nextFiles) => {
954
+ const selectedFiles = Array.from(nextFiles);
955
+ if (multiple) {
956
+ const uniqueFiles = new Map([...files, ...selectedFiles].map((selectedFile) => [
957
+ `${selectedFile.name}:${selectedFile.size}:${selectedFile.lastModified}`,
958
+ selectedFile
959
+ ]));
960
+ onFilesChange?.(Array.from(uniqueFiles.values()));
961
+ } else if (selectedFiles[0]) {
962
+ onChange?.(selectedFiles[0]);
963
+ }
942
964
  };
943
965
  const handleDrop = (event) => {
944
966
  event.preventDefault();
945
967
  dragDepthRef.current = 0;
946
968
  setDragging(false);
947
- if (inputRef.current)
948
- inputRef.current.files = event.dataTransfer.files;
949
- selectFile(event.dataTransfer.files[0]);
969
+ selectFiles(event.dataTransfer.files);
950
970
  onBlur?.();
951
971
  };
952
972
  const clear = () => {
@@ -961,11 +981,101 @@ var FilePicker = ({
961
981
  name,
962
982
  type: "file",
963
983
  accept,
964
- required,
984
+ multiple,
985
+ required: required && (!multiple || files.length === 0),
965
986
  onBlur,
966
- onChange: (event) => selectFile(event.target.files?.[0]),
987
+ onChange: (event) => {
988
+ if (event.target.files)
989
+ selectFiles(event.target.files);
990
+ if (multiple)
991
+ event.target.value = "";
992
+ },
967
993
  "aria-invalid": invalid
968
994
  }, undefined, false, undefined, this);
995
+ if (multiple) {
996
+ return /* @__PURE__ */ jsxDEV7("div", {
997
+ className: "grid min-w-0 gap-3",
998
+ children: [
999
+ /* @__PURE__ */ jsxDEV7("div", {
1000
+ className: "bg-muted/30 data-[dragging=true]:bg-muted flex min-h-32 min-w-0 flex-col items-center justify-center gap-3 rounded-xl border border-dashed p-6 text-center transition-colors",
1001
+ "data-dragging": dragging,
1002
+ onDragEnter: (event) => {
1003
+ event.preventDefault();
1004
+ dragDepthRef.current += 1;
1005
+ setDragging(true);
1006
+ },
1007
+ onDragLeave: (event) => {
1008
+ event.preventDefault();
1009
+ dragDepthRef.current -= 1;
1010
+ if (dragDepthRef.current <= 0) {
1011
+ dragDepthRef.current = 0;
1012
+ setDragging(false);
1013
+ }
1014
+ },
1015
+ onDragOver: (event) => event.preventDefault(),
1016
+ onDrop: handleDrop,
1017
+ children: [
1018
+ /* @__PURE__ */ jsxDEV7(CloudUpload, {
1019
+ className: "text-muted-foreground size-6"
1020
+ }, undefined, false, undefined, this),
1021
+ /* @__PURE__ */ jsxDEV7("div", {
1022
+ className: "max-w-full min-w-0",
1023
+ children: [
1024
+ /* @__PURE__ */ jsxDEV7("p", {
1025
+ className: "text-sm font-medium",
1026
+ children: labels.dropFiles
1027
+ }, undefined, false, undefined, this),
1028
+ /* @__PURE__ */ jsxDEV7("p", {
1029
+ className: "text-muted-foreground max-w-full text-xs [overflow-wrap:anywhere]",
1030
+ children: labels.accepts(accept)
1031
+ }, undefined, false, undefined, this)
1032
+ ]
1033
+ }, undefined, true, undefined, this),
1034
+ /* @__PURE__ */ jsxDEV7(Button, {
1035
+ type: "button",
1036
+ variant: "outline",
1037
+ onClick: () => inputRef.current?.click(),
1038
+ children: [
1039
+ /* @__PURE__ */ jsxDEV7(FileUp, {
1040
+ "data-icon": "inline-start"
1041
+ }, undefined, false, undefined, this),
1042
+ labels.chooseFiles
1043
+ ]
1044
+ }, undefined, true, undefined, this),
1045
+ input
1046
+ ]
1047
+ }, undefined, true, undefined, this),
1048
+ files.length > 0 ? /* @__PURE__ */ jsxDEV7(AttachmentGroup, {
1049
+ children: files.map((selectedFile) => /* @__PURE__ */ jsxDEV7(Attachment, {
1050
+ state: invalid ? "error" : "idle",
1051
+ children: [
1052
+ /* @__PURE__ */ jsxDEV7(AttachmentMedia, {
1053
+ children: /* @__PURE__ */ jsxDEV7(FileIcon, {}, undefined, false, undefined, this)
1054
+ }, undefined, false, undefined, this),
1055
+ /* @__PURE__ */ jsxDEV7(AttachmentContent, {
1056
+ children: [
1057
+ /* @__PURE__ */ jsxDEV7(AttachmentTitle, {
1058
+ children: selectedFile.name
1059
+ }, undefined, false, undefined, this),
1060
+ /* @__PURE__ */ jsxDEV7(AttachmentDescription, {
1061
+ children: fileDescription(selectedFile)
1062
+ }, undefined, false, undefined, this)
1063
+ ]
1064
+ }, undefined, true, undefined, this),
1065
+ /* @__PURE__ */ jsxDEV7(AttachmentActions, {
1066
+ children: /* @__PURE__ */ jsxDEV7(AttachmentAction, {
1067
+ type: "button",
1068
+ "aria-label": `${labels.deleteFile} ${selectedFile.name}`,
1069
+ onClick: () => onFilesChange?.(files.filter((candidate) => candidate !== selectedFile)),
1070
+ children: /* @__PURE__ */ jsxDEV7(Trash2, {}, undefined, false, undefined, this)
1071
+ }, undefined, false, undefined, this)
1072
+ }, undefined, false, undefined, this)
1073
+ ]
1074
+ }, `${selectedFile.name}:${selectedFile.size}:${selectedFile.lastModified}`, true, undefined, this))
1075
+ }, undefined, false, undefined, this) : null
1076
+ ]
1077
+ }, undefined, true, undefined, this);
1078
+ }
969
1079
  if (!selected) {
970
1080
  return /* @__PURE__ */ jsxDEV7("div", {
971
1081
  className: "bg-muted/30 data-[dragging=true]:bg-muted flex min-h-32 flex-col items-center justify-center gap-3 rounded-xl border border-dashed p-6 text-center transition-colors",
@@ -10,6 +10,9 @@ type NavGroup = {
10
10
  items: NavItem[];
11
11
  };
12
12
  export type { NavItem, NavGroup };
13
+ export declare const useSidebarLayout: () => {
14
+ isMobile: boolean;
15
+ };
13
16
  type SidebarPageHeaderProps = {
14
17
  title: string;
15
18
  description?: string;
@@ -20,10 +23,12 @@ type SidebarPageHeaderProps = {
20
23
  actions?: React.ReactNode;
21
24
  };
22
25
  export declare function SidebarPageHeader({ title, description, badge, actions, }: SidebarPageHeaderProps): import("react").JSX.Element;
23
- export declare function SidebarLayout({ groups, children, sidebarFooter, sidebarHeader, headerActions, }: {
26
+ export declare function SidebarLayout({ groups, children, sidebarFooter, sidebarHeader, headerActions, contentClassName, fullPage, }: {
24
27
  groups: NavGroup[];
25
28
  children?: React.ReactNode;
26
29
  sidebarFooter?: React.ReactNode;
27
30
  sidebarHeader?: React.ReactNode;
28
31
  headerActions?: React.ReactNode;
32
+ contentClassName?: string;
33
+ fullPage?: boolean;
29
34
  }): import("react").JSX.Element;
@@ -613,6 +613,10 @@ var sidebarOpenAtom = Atom.kvs({
613
613
  schema: Schema.Boolean,
614
614
  defaultValue: () => true
615
615
  });
616
+ var useSidebarLayout = () => {
617
+ const { isMobile } = useSidebar();
618
+ return { isMobile };
619
+ };
616
620
  function AppSidebar({ footer, groups, header }) {
617
621
  const { isMobile, setOpenMobile } = useSidebar();
618
622
  const pathname = useRouterState({
@@ -715,12 +719,15 @@ function SidebarLayout({
715
719
  children,
716
720
  sidebarFooter,
717
721
  sidebarHeader,
718
- headerActions
722
+ headerActions,
723
+ contentClassName,
724
+ fullPage = false
719
725
  }) {
720
726
  const [sidebarOpen, setSidebarOpen] = useAtom(sidebarOpenAtom);
721
727
  return /* @__PURE__ */ jsxDEV8(SidebarProvider, {
722
728
  open: sidebarOpen,
723
729
  onOpenChange: setSidebarOpen,
730
+ className: cn(fullPage && "xl:h-svh xl:min-h-0 xl:overflow-hidden"),
724
731
  children: [
725
732
  /* @__PURE__ */ jsxDEV8(AppSidebar, {
726
733
  footer: sidebarFooter,
@@ -728,7 +735,7 @@ function SidebarLayout({
728
735
  header: sidebarHeader
729
736
  }, undefined, false, undefined, this),
730
737
  /* @__PURE__ */ jsxDEV8(SidebarInset, {
731
- className: "min-w-0 overflow-x-hidden",
738
+ className: cn("min-w-0 overflow-x-hidden", fullPage && "xl:h-svh xl:min-h-0 xl:overflow-hidden"),
732
739
  children: [
733
740
  /* @__PURE__ */ jsxDEV8("header", {
734
741
  className: "bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-10 flex h-14 shrink-0 items-center gap-2 border-b px-4 backdrop-blur",
@@ -741,7 +748,7 @@ function SidebarLayout({
741
748
  ]
742
749
  }, undefined, true, undefined, this),
743
750
  /* @__PURE__ */ jsxDEV8("div", {
744
- className: "flex flex-col gap-6 px-5 py-6 md:px-8",
751
+ className: contentClassName ?? "flex flex-col gap-6 px-5 py-6 md:px-8",
745
752
  children: children ?? /* @__PURE__ */ jsxDEV8(Outlet, {}, undefined, false, undefined, this)
746
753
  }, undefined, false, undefined, this)
747
754
  ]
@@ -750,6 +757,7 @@ function SidebarLayout({
750
757
  }, undefined, true, undefined, this);
751
758
  }
752
759
  export {
760
+ useSidebarLayout,
753
761
  SidebarPageHeader,
754
762
  SidebarLayout
755
763
  };
@@ -11,5 +11,6 @@ export declare class NotificationService extends NotificationService_base {
11
11
  static readonly layer: Layer.Layer<NotificationService, never, NotificationChannelRegistry>;
12
12
  static readonly makeLayer: (channels: ReadonlyArray<NotificationChannelShape>) => Layer.Layer<NotificationService, never, never>;
13
13
  static readonly noopLayer: Layer.Layer<NotificationService, never, never>;
14
+ static readonly localLayer: Layer.Layer<NotificationService, never, never>;
14
15
  }
15
16
  export {};
@@ -61,6 +61,12 @@ class NotificationService extends Context2.Service()("NotificationService", {
61
61
  static noopLayer = Layer2.succeed(this, {
62
62
  send: () => Effect2.void
63
63
  });
64
+ static localLayer = this.makeLayer([
65
+ {
66
+ key: "email",
67
+ send: (payload) => Effect2.logInfo("[fake email]", payload)
68
+ }
69
+ ]);
64
70
  }
65
71
  export {
66
72
  NotificationService
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krak-stack/registry",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Tree-shakable KrakStack components and Effect services.",
5
5
  "license": "MIT",
6
6
  "repository": {