@k3-universe/react-kit 0.0.11 → 0.0.13

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 (43) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5393 -1357
  4. package/dist/kit/builder/form/components/FormBuilder.d.ts +21 -0
  5. package/dist/kit/builder/form/components/FormBuilder.d.ts.map +1 -1
  6. package/dist/kit/builder/form/components/fields/FileField.d.ts +1 -1
  7. package/dist/kit/builder/form/components/fields/FileField.d.ts.map +1 -1
  8. package/dist/kit/builder/stack-dialog/hooks.d.ts +2 -5
  9. package/dist/kit/builder/stack-dialog/hooks.d.ts.map +1 -1
  10. package/dist/kit/builder/stack-dialog/index.d.ts +3 -3
  11. package/dist/kit/builder/stack-dialog/index.d.ts.map +1 -1
  12. package/dist/kit/builder/stack-dialog/renderer.d.ts.map +1 -1
  13. package/dist/kit/builder/stack-dialog/types.d.ts +1 -0
  14. package/dist/kit/builder/stack-dialog/types.d.ts.map +1 -1
  15. package/dist/kit/components/fileuploader/FileUploader.d.ts +4 -0
  16. package/dist/kit/components/fileuploader/FileUploader.d.ts.map +1 -0
  17. package/dist/kit/components/fileuploader/index.d.ts +4 -0
  18. package/dist/kit/components/fileuploader/index.d.ts.map +1 -0
  19. package/dist/kit/components/fileuploader/types.d.ts +63 -0
  20. package/dist/kit/components/fileuploader/types.d.ts.map +1 -0
  21. package/dist/kit/themes/clean-slate.css +60 -0
  22. package/dist/kit/themes/default.css +60 -0
  23. package/dist/kit/themes/minimal-modern.css +60 -0
  24. package/dist/kit/themes/spotify.css +60 -0
  25. package/package.json +2 -1
  26. package/src/index.ts +2 -0
  27. package/src/kit/builder/form/components/FormBuilder.tsx +56 -0
  28. package/src/kit/builder/form/components/fields/FileField.tsx +17 -5
  29. package/src/kit/builder/stack-dialog/hooks.ts +2 -1
  30. package/src/kit/builder/stack-dialog/index.ts +8 -3
  31. package/src/kit/builder/stack-dialog/renderer.tsx +2 -2
  32. package/src/kit/builder/stack-dialog/types.ts +2 -0
  33. package/src/kit/components/fileuploader/FileUploader.tsx +488 -0
  34. package/src/kit/components/fileuploader/index.ts +3 -0
  35. package/src/kit/components/fileuploader/types.ts +73 -0
  36. package/src/kit/components/monthpicker/MonthPicker.tsx +2 -2
  37. package/src/kit/components/monthpicker/MonthRangePicker.tsx +2 -2
  38. package/src/stories/FileUploader.stories.tsx +166 -0
  39. package/src/stories/kit/builder/Form.ArrayLayouts.stories.tsx +1 -1
  40. package/src/stories/kit/builder/Form.Autocomplete.stories.tsx +5 -5
  41. package/src/stories/kit/builder/Form.DateTime.stories.tsx +1 -1
  42. package/src/stories/kit/builder/Form.Files.stories.tsx +125 -0
  43. package/src/stories/kit/builder/Form.Time.stories.tsx +1 -1
@@ -87,6 +87,8 @@
87
87
  --font-sans: var(--font-sans);
88
88
  --font-serif: var(--font-serif);
89
89
  --font-mono: var(--font-mono);
90
+ --color-red-500: oklch(63.7% .237 25.331);
91
+ --color-green-500: oklch(72.3% .219 149.579);
90
92
  --color-teal-50: oklch(98.4% .014 180.72);
91
93
  --color-teal-700: oklch(51.1% .096 186.391);
92
94
  --color-blue-50: oklch(97% .014 254.604);
@@ -470,6 +472,10 @@
470
472
  top: calc(var(--spacing) * 0);
471
473
  }
472
474
 
475
+ .top-1 {
476
+ top: calc(var(--spacing) * 1);
477
+ }
478
+
473
479
  .top-1\.5 {
474
480
  top: calc(var(--spacing) * 1.5);
475
481
  }
@@ -942,6 +948,10 @@
942
948
  height: calc(var(--spacing) * 5);
943
949
  }
944
950
 
951
+ .h-6 {
952
+ height: calc(var(--spacing) * 6);
953
+ }
954
+
945
955
  .h-7 {
946
956
  height: calc(var(--spacing) * 7);
947
957
  }
@@ -970,6 +980,10 @@
970
980
  height: calc(var(--spacing) * 24);
971
981
  }
972
982
 
983
+ .h-28 {
984
+ height: calc(var(--spacing) * 28);
985
+ }
986
+
973
987
  .h-40 {
974
988
  height: calc(var(--spacing) * 40);
975
989
  }
@@ -1118,6 +1132,10 @@
1118
1132
  width: calc(var(--spacing) * 5);
1119
1133
  }
1120
1134
 
1135
+ .w-6 {
1136
+ width: calc(var(--spacing) * 6);
1137
+ }
1138
+
1121
1139
  .w-7 {
1122
1140
  width: calc(var(--spacing) * 7);
1123
1141
  }
@@ -1146,6 +1164,10 @@
1146
1164
  width: calc(var(--spacing) * 24);
1147
1165
  }
1148
1166
 
1167
+ .w-28 {
1168
+ width: calc(var(--spacing) * 28);
1169
+ }
1170
+
1149
1171
  .w-32 {
1150
1172
  width: calc(var(--spacing) * 32);
1151
1173
  }
@@ -1734,6 +1756,10 @@
1734
1756
  row-gap: calc(var(--spacing) * .5);
1735
1757
  }
1736
1758
 
1759
+ .self-center {
1760
+ align-self: center;
1761
+ }
1762
+
1737
1763
  .self-start {
1738
1764
  align-self: flex-start;
1739
1765
  }
@@ -1960,6 +1986,16 @@
1960
1986
  }
1961
1987
  }
1962
1988
 
1989
+ .bg-black\/40 {
1990
+ background-color: #0006;
1991
+ }
1992
+
1993
+ @supports (color: color-mix(in lab, red, red)) {
1994
+ .bg-black\/40 {
1995
+ background-color: color-mix(in oklab, var(--color-black) 40%, transparent);
1996
+ }
1997
+ }
1998
+
1963
1999
  .bg-black\/50 {
1964
2000
  background-color: #00000080;
1965
2001
  }
@@ -2420,6 +2456,10 @@
2420
2456
  color: var(--color-gray-900);
2421
2457
  }
2422
2458
 
2459
+ .text-green-500 {
2460
+ color: var(--color-green-500);
2461
+ }
2462
+
2423
2463
  .text-muted-foreground {
2424
2464
  color: var(--muted-foreground);
2425
2465
  }
@@ -2436,6 +2476,10 @@
2436
2476
  color: var(--primary-foreground);
2437
2477
  }
2438
2478
 
2479
+ .text-red-500 {
2480
+ color: var(--color-red-500);
2481
+ }
2482
+
2439
2483
  .text-secondary-foreground {
2440
2484
  color: var(--secondary-foreground);
2441
2485
  }
@@ -2594,6 +2638,12 @@
2594
2638
  outline-width: 1px;
2595
2639
  }
2596
2640
 
2641
+ .drop-shadow {
2642
+ --tw-drop-shadow-size: drop-shadow(0 1px 2px var(--tw-drop-shadow-color, #0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color, #0000000f));
2643
+ --tw-drop-shadow: drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);
2644
+ filter: var(--tw-blur, ) var(--tw-brightness, ) var(--tw-contrast, ) var(--tw-grayscale, ) var(--tw-hue-rotate, ) var(--tw-invert, ) var(--tw-saturate, ) var(--tw-sepia, ) var(--tw-drop-shadow, );
2645
+ }
2646
+
2597
2647
  .filter {
2598
2648
  filter: var(--tw-blur, ) var(--tw-brightness, ) var(--tw-contrast, ) var(--tw-grayscale, ) var(--tw-hue-rotate, ) var(--tw-invert, ) var(--tw-saturate, ) var(--tw-sepia, ) var(--tw-drop-shadow, );
2599
2649
  }
@@ -3150,6 +3200,16 @@
3150
3200
  }
3151
3201
 
3152
3202
  @media (hover: hover) {
3203
+ .hover\:border-foreground\/50:hover {
3204
+ border-color: var(--foreground);
3205
+ }
3206
+
3207
+ @supports (color: color-mix(in lab, red, red)) {
3208
+ .hover\:border-foreground\/50:hover {
3209
+ border-color: color-mix(in oklab, var(--foreground) 50%, transparent);
3210
+ }
3211
+ }
3212
+
3153
3213
  .hover\:bg-accent:hover {
3154
3214
  background-color: var(--accent);
3155
3215
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k3-universe/react-kit",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -64,6 +64,7 @@
64
64
  "next-themes": "^0.4.6",
65
65
  "react-day-picker": "^9.9.0",
66
66
  "react-hook-form": "^7.62.0",
67
+ "react-dropzone": "^14.2.3",
67
68
  "react-resizable-panels": "^3.0.4",
68
69
  "recharts": "2.15.4",
69
70
  "sonner": "^2.0.7",
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ export * from './kit/builder/dialog';
6
6
  export * from './kit/builder/form';
7
7
  export * from './kit/builder/section';
8
8
  export * from './kit/builder/page';
9
+ export * from './kit/builder/stack-dialog';
9
10
  // Ensure default export for Page at root
10
11
  export { default as Page } from './kit/builder/page/Page';
11
12
 
@@ -16,6 +17,7 @@ export * from './kit/components/autocomplete';
16
17
  export * from './kit/components/login';
17
18
  // Ensure default export for Login at root
18
19
  export { default as Login } from './kit/components/login/Login';
20
+ export * from './kit/components/fileuploader';
19
21
 
20
22
  // -----------------------------
21
23
  // KIT: layouts (admin)
@@ -17,6 +17,11 @@ import type {
17
17
  AutocompleteFetcher,
18
18
  AutocompleteOption,
19
19
  } from '../../../components/autocomplete/types';
20
+ import type { Accept } from 'react-dropzone';
21
+ import type {
22
+ FileRecord,
23
+ FileUploaderLayout,
24
+ } from '../../../components/fileuploader/types';
20
25
 
21
26
  export interface FormBuilderFieldConfig {
22
27
  id?: string; // Optional ID for test fixtures
@@ -75,6 +80,9 @@ export interface FormBuilderFieldConfig {
75
80
  max?: { value: number; message: string };
76
81
  minLength?: { value: number; message: string };
77
82
  maxLength?: { value: number; message: string };
83
+ // For array-like fields (e.g., file uploader)
84
+ minItems?: { value: number; message: string };
85
+ maxItems?: { value: number; message: string };
78
86
  };
79
87
  defaultValue?: unknown;
80
88
  fields?: FormBuilderFieldConfig[]; // For nested object/array fields
@@ -136,6 +144,21 @@ export interface FormBuilderFieldConfig {
136
144
  hourCycle?: 12 | 24;
137
145
  minuteStep?: number;
138
146
  secondStep?: number;
147
+ // File uploader specific options
148
+ fileMultiple?: boolean;
149
+ fileMaxFiles?: number;
150
+ fileAccept?: Accept;
151
+ fileLayout?: FileUploaderLayout;
152
+ fileWithDownload?: boolean;
153
+ fileUploader?: (
154
+ file: File,
155
+ onProgress: (pct: number) => void,
156
+ ) => Promise<Partial<FileRecord>>;
157
+ fileOnUploadSuccess?: (file: FileRecord) => void;
158
+ fileOnUploadError?: (file: FileRecord, error: unknown) => void;
159
+ fileOnRemove?: (file: FileRecord) => void | Promise<void>;
160
+ fileOnRetry?: (file: FileRecord) => void;
161
+ fileOnRetryAll?: (files: FileRecord[]) => void;
139
162
  }
140
163
 
141
164
  export interface FormBuilderSectionConfig {
@@ -236,6 +259,9 @@ export function FormBuilder({
236
259
  case 'number':
237
260
  baseSchema = z.number();
238
261
  break;
262
+ case 'file':
263
+ baseSchema = z.array(z.unknown());
264
+ break;
239
265
  case 'date_picker':
240
266
  case 'month':
241
267
  case 'date':
@@ -304,6 +330,27 @@ export function FormBuilder({
304
330
  validationObj.maxLength.message
305
331
  );
306
332
  }
333
+ // Array item count constraints
334
+ if (baseSchema instanceof z.ZodArray) {
335
+ let arr = baseSchema as z.ZodArray<z.ZodTypeAny>;
336
+ if (validationObj.minItems) {
337
+ arr = arr.min(
338
+ validationObj.minItems.value,
339
+ validationObj.minItems.message,
340
+ );
341
+ }
342
+ if (validationObj.maxItems) {
343
+ arr = arr.max(
344
+ validationObj.maxItems.value,
345
+ validationObj.maxItems.message,
346
+ );
347
+ }
348
+ // If required and file field, enforce at least 1 item when no explicit minItems
349
+ if (field.type === 'file' && field.required && !validationObj.minItems) {
350
+ arr = arr.min(1, `${field.label} requires at least 1 file`);
351
+ }
352
+ baseSchema = arr;
353
+ }
307
354
 
308
355
  return field.required ? baseSchema : baseSchema.optional();
309
356
  }
@@ -317,6 +364,15 @@ export function FormBuilder({
317
364
  case 'number':
318
365
  fieldSchema = z.number();
319
366
  break;
367
+ case 'file': {
368
+ let arr = z.array(z.unknown());
369
+ // If required, ensure at least 1 file
370
+ if (field.required) {
371
+ arr = arr.min(1, `${field.label} requires at least 1 file`);
372
+ }
373
+ fieldSchema = arr;
374
+ break;
375
+ }
320
376
  case 'date_picker':
321
377
  case 'month':
322
378
  case 'date':
@@ -1,14 +1,26 @@
1
- import { Input } from '../../../../../shadcn/ui/input'
2
1
  import type { FieldRenderProps } from './types'
2
+ import { FileUploader } from '../../../../components/fileuploader/FileUploader'
3
3
 
4
- export function FileField({ field, onChange, className }: FieldRenderProps) {
4
+ export function FileField({ field, value, onChange, className }: FieldRenderProps) {
5
+ const files = Array.isArray(value) ? value : []
5
6
  return (
6
- <Input
7
+ <FileUploader
7
8
  className={className}
8
9
  disabled={field.disabled}
9
10
  placeholder={field.placeholder}
10
- type="file"
11
- onChange={(e) => onChange((e.target as HTMLInputElement).files?.[0] || null)}
11
+ value={files}
12
+ onChange={(files) => onChange(files)}
13
+ multiple={field.fileMultiple ?? true}
14
+ maxFiles={field.fileMaxFiles}
15
+ accept={field.fileAccept}
16
+ layout={field.fileLayout ?? 'grid'}
17
+ withDownload={field.fileWithDownload ?? true}
18
+ uploader={field.fileUploader}
19
+ onUploadSuccess={field.fileOnUploadSuccess}
20
+ onUploadError={field.fileOnUploadError}
21
+ onRemove={field.fileOnRemove}
22
+ onRetry={field.fileOnRetry}
23
+ onRetryAll={field.fileOnRetryAll}
12
24
  />
13
25
  )
14
26
  }
@@ -1,7 +1,8 @@
1
1
  import { useContext } from 'react';
2
2
  import { StackDialogContext } from './context';
3
+ import type { StackDialogHook } from './types';
3
4
 
4
- export function useStackDialog() {
5
+ export function useStackDialog(): StackDialogHook {
5
6
  const { createDialog, closeDialog, closeAllDialogs } = useContext(StackDialogContext);
6
7
  return {
7
8
  createDialog,
@@ -1,13 +1,18 @@
1
1
  export {
2
- StackDialogContext as Context
2
+ StackDialogContext,
3
3
  } from './context'
4
4
 
5
5
  export {
6
- StackDialogContextProvider as ContextProvider
6
+ StackDialogContextProvider as StackDialogProvider,
7
7
  } from './provider'
8
8
 
9
9
  export {
10
10
  useStackDialog
11
11
  } from './hooks'
12
12
 
13
- export type * from './types'
13
+ export type {
14
+ StackDialogContextInstance,
15
+ StackDialogCreateConfig,
16
+ StackDialogHook,
17
+ StackDialogInstance
18
+ } from './types'
@@ -1,6 +1,6 @@
1
- import { Dialog } from '@/shadcn/ui/dialog';
2
- import { StackDialogInstance } from './types';
3
1
  import { DialogContent } from '@radix-ui/react-dialog';
2
+ import { Dialog } from '../../../shadcn/ui/dialog';
3
+ import { StackDialogInstance } from './types';
4
4
 
5
5
  export function StackDialogRenderer(props: {
6
6
  dialogs: StackDialogInstance[],
@@ -20,3 +20,5 @@ export type StackDialogCreateConfig = {
20
20
  closeOnInteractOutside?: boolean,
21
21
  closeOnEscapePressed?: boolean,
22
22
  };
23
+
24
+ export type StackDialogHook = Omit<StackDialogContextInstance, "activeDialogs">