@js-empire/emperor-ui 1.2.0 → 1.2.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.
Files changed (70) hide show
  1. package/dist/emperor-ui.js +52 -48
  2. package/dist/emperor-ui.umd.cjs +8 -8
  3. package/dist/{features-animation-D_Ss-HYx.js → features-animation-uyo1KMg-.js} +1 -1
  4. package/dist/{index-C3mfrNCk.js → index-B3d8-vnJ.js} +4 -4
  5. package/dist/{index-SRvFgjzo.js → index-DOwkJus4.js} +3871 -3600
  6. package/dist/index-DrkA25TM.js +5 -0
  7. package/dist/index.d.ts +89 -7
  8. package/dist/src-UW24ZMRV-D6kiVea5.js +5 -0
  9. package/package.json +1 -1
  10. package/src/components/atoms/uploader/avatar-label.tsx +1 -1
  11. package/src/components/atoms/uploader/index.ts +1 -0
  12. package/src/components/atoms/uploader/stories/uploader.stories.tsx +52 -18
  13. package/src/components/atoms/uploader/upload-file-error-box.tsx +2 -2
  14. package/src/components/atoms/uploader/upload-file-label.tsx +7 -3
  15. package/src/components/atoms/uploader/uploader-title.tsx +21 -0
  16. package/src/components/atoms/uploader/uploader.tsx +3 -0
  17. package/src/components/organisms/footer/footer.tsx +1 -1
  18. package/src/components/organisms/listings/listings.tsx +12 -3
  19. package/src/components/organisms/listings/stories/listings.stories.tsx +30 -0
  20. package/src/components/organisms/listings/styles/classes.ts +11 -0
  21. package/src/components/organisms/listings/styles/index.ts +2 -0
  22. package/src/components/organisms/listings/styles/styles.ts +6 -0
  23. package/src/constants/defaults.ts +3 -26
  24. package/src/hooks/use-uploader.tsx +5 -4
  25. package/src/i18n/configs/i18n.ts +7 -0
  26. package/src/i18n/configs/index.ts +1 -0
  27. package/src/i18n/constants/index.ts +1 -0
  28. package/src/i18n/constants/locales.ts +4 -0
  29. package/src/i18n/index.ts +5 -0
  30. package/src/i18n/locales/ar.ts +15 -0
  31. package/src/i18n/locales/atoms/ar.ts +15 -0
  32. package/src/i18n/locales/atoms/en.ts +15 -0
  33. package/src/i18n/locales/atoms/index.ts +2 -0
  34. package/src/i18n/locales/common/ar.ts +1 -0
  35. package/src/i18n/locales/common/en.ts +1 -0
  36. package/src/i18n/locales/common/index.ts +2 -0
  37. package/src/i18n/locales/en.ts +15 -0
  38. package/src/i18n/locales/index.ts +4 -0
  39. package/src/i18n/locales/molecules/ar.ts +1 -0
  40. package/src/i18n/locales/molecules/en.ts +1 -0
  41. package/src/i18n/locales/molecules/index.ts +2 -0
  42. package/src/i18n/locales/organisms/ar.ts +1 -0
  43. package/src/i18n/locales/organisms/en.ts +1 -0
  44. package/src/i18n/locales/organisms/index.ts +2 -0
  45. package/src/i18n/locales/templates/ar.ts +1 -0
  46. package/src/i18n/locales/templates/en.ts +1 -0
  47. package/src/i18n/locales/templates/index.ts +2 -0
  48. package/src/i18n/locales/toasts/ar.ts +1 -0
  49. package/src/i18n/locales/toasts/en.ts +1 -0
  50. package/src/i18n/locales/toasts/index.ts +2 -0
  51. package/src/i18n/types/index.ts +2 -0
  52. package/src/i18n/types/locale.ts +5 -0
  53. package/src/i18n/types/toasts.ts +3 -0
  54. package/src/i18n/utils/get-locales.ts +4 -0
  55. package/src/i18n/utils/index.ts +2 -0
  56. package/src/i18n/utils/localize.ts +15 -0
  57. package/src/mocks/index.ts +1 -0
  58. package/src/mocks/listings.ts +200 -0
  59. package/src/providers/config-provider.tsx +18 -0
  60. package/src/types/components/atoms/uploader.ts +5 -5
  61. package/src/types/components/molecules/listings/listings.ts +5 -1
  62. package/src/types/context/config.ts +1 -32
  63. package/src/types/context/index.ts +2 -0
  64. package/src/types/context/localization.ts +23 -0
  65. package/src/types/context/theme.ts +17 -0
  66. package/src/utils/index.ts +1 -0
  67. package/src/utils/locales.ts +54 -0
  68. package/src/utils/uploader.ts +6 -5
  69. package/dist/index-CZpTSGZs.js +0 -5
  70. package/dist/src-UW24ZMRV-Ducut0ty.js +0 -5
@@ -0,0 +1,5 @@
1
+ import { d as a } from "./features-animation-uyo1KMg-.js";
2
+ var o = a;
3
+ export {
4
+ o as default
5
+ };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { ButtonProps as ButtonProps_2 } from '@heroui/button';
3
3
  import { ClassAttributes } from 'react';
4
4
  import { ClassProp } from 'class-variance-authority/types';
5
5
  import { ClassValue } from 'clsx';
6
+ import { ComponentProps } from 'react';
6
7
  import { Context } from 'react';
7
8
  import { Dispatch } from 'react';
8
9
  import { ElementType } from 'react';
@@ -34,6 +35,27 @@ export declare const ALLOWED_PDF_TYPES: string[];
34
35
 
35
36
  export declare type AppDirection = "ltr" | "rtl";
36
37
 
38
+ declare const ar: {
39
+ common: {};
40
+ toasts: {};
41
+ atoms: {
42
+ uploader: {
43
+ dropHere: string;
44
+ selectFile: string;
45
+ selectionTypes: string;
46
+ selectBtn: string;
47
+ errorUploadingFile: string;
48
+ maxNumImages: string;
49
+ errorUploadedTypes: string;
50
+ maxSizeExceededError: string;
51
+ duplicatesDenied: string;
52
+ };
53
+ };
54
+ molecules: {};
55
+ organisms: {};
56
+ templates: {};
57
+ };
58
+
37
59
  export declare function AvatarLabel(): JSX.Element;
38
60
 
39
61
  export declare const Brand: ForwardRefExoticComponent<Omit<ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
@@ -151,7 +173,11 @@ export declare type EmperorUILayout = {
151
173
  withScaffold: boolean;
152
174
  };
153
175
 
154
- export declare type EmperorUILocales = Record<EmperorUILang, Record<string, string>>;
176
+ export declare type EmperorUILocales = Record<EmperorUILang, Partial<{
177
+ atoms?: {
178
+ uploader?: Partial<Locale["atoms"]["uploader"]>;
179
+ };
180
+ }>>;
155
181
 
156
182
  export declare function EmperorUIProvider({ children, ...props }: EmperorUIProviderProps): JSX.Element;
157
183
 
@@ -162,6 +188,27 @@ export declare type EmperorUITheme = {
162
188
  colors: Partial<ColorsPalette>;
163
189
  };
164
190
 
191
+ declare const en: {
192
+ common: {};
193
+ toasts: {};
194
+ atoms: {
195
+ uploader: {
196
+ dropHere: string;
197
+ selectFile: string;
198
+ selectionTypes: string;
199
+ selectBtn: string;
200
+ errorUploadingFile: string;
201
+ maxNumImages: string;
202
+ errorUploadedTypes: string;
203
+ maxSizeExceededError: string;
204
+ duplicatesDenied: string;
205
+ };
206
+ };
207
+ molecules: {};
208
+ organisms: {};
209
+ templates: {};
210
+ };
211
+
165
212
  export declare const FAKE_PARAGRAPH = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,";
166
213
 
167
214
  export declare const FAKE_SENTENCE = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.";
@@ -229,6 +276,11 @@ export declare type FooterProps = SharedComponentProps & {
229
276
 
230
277
  export declare const getAllowedTypes: (fileTypes: FileType[]) => string[];
231
278
 
279
+ export declare const getListings: ({ page, pageSize, }: {
280
+ page?: number;
281
+ pageSize?: number;
282
+ }) => MockItemType[];
283
+
232
284
  export declare const getStorybookDecorators: ({ config, }: {
233
285
  config?: EmperorUIConfig;
234
286
  }) => ((Story: any) => JSX.Element)[];
@@ -330,18 +382,34 @@ export declare type LandingPageProps = SharedComponentProps & {
330
382
  variant?: "default";
331
383
  };
332
384
 
333
- export declare function Listings({ className }: ListingsProps): JSX.Element;
385
+ export declare function Listings<ListingType>({ className, variant, }: ListingsProps<ListingType>): JSX.Element;
334
386
 
335
387
  export declare type ListingsClassnames = {
336
388
  base?: string;
337
389
  };
338
390
 
339
- export declare type ListingsProps = SharedComponentProps & {
391
+ export declare type ListingsProps<ListingType> = SharedComponentProps & {
340
392
  classNames?: ListingsClassnames;
393
+ variant?: ListingsVariant;
394
+ items: ListingType[];
341
395
  };
342
396
 
397
+ export declare type ListingsVariant = "default";
398
+
399
+ declare type Locale = typeof ar | typeof en;
400
+
343
401
  export declare const mapFileType: (fileType: string) => FileType | null;
344
402
 
403
+ /**
404
+ * Deep-merges config locales into default locales. Default values are used as base,
405
+ * and config values override only the keys they provide. Nested objects are merged
406
+ * recursively so unrelated keys at any level remain unchanged.
407
+ */
408
+ export declare const mergeLocales: ({ defaultLocales, configLocales, }: {
409
+ defaultLocales: Locale;
410
+ configLocales?: Partial<Locale>;
411
+ }) => Locale;
412
+
345
413
  export declare const MOCK_HEADER_ACTIONS: SideBarAction[];
346
414
 
347
415
  export declare const MOCK_HEADER_ITEMS: NavigationItem[];
@@ -350,6 +418,15 @@ export declare const MOCK_HEADER_ITEMS_WITH_SUB_ITEMS: NavigationItem[];
350
418
 
351
419
  export declare const MOCK_HEADER_SUB_ITEMS: NavigationItem[];
352
420
 
421
+ export declare const MOCK_LISTINGS: MockItemType[];
422
+
423
+ export declare type MockItemType = {
424
+ id: number;
425
+ title: string;
426
+ description: string;
427
+ image: string;
428
+ };
429
+
353
430
  export declare const NavBar: ForwardRefExoticComponent<Omit<ClassAttributes<HTMLElement> & HTMLAttributes<HTMLElement> & VariantProps<(props?: ({
354
431
  hoverEffect?: "default" | "underline" | "solid" | "ghost" | "bordered" | "none" | null | undefined;
355
432
  variant?: "default" | "solid" | "bordered" | null | undefined;
@@ -475,7 +552,7 @@ export declare function QuickLinksBox({ quickLinks, classNames }: FooterProps):
475
552
 
476
553
  export declare function refineUploadedFiles({ uploadedFiles, locale, allowedTypes, isMulti, setFiles, }: {
477
554
  uploadedFiles: (File | undefined)[];
478
- locale?: Record<string, string> | undefined;
555
+ locale?: Locale;
479
556
  allowedTypes: string[];
480
557
  isMulti: boolean;
481
558
  setFiles: Dispatch<SetStateAction<FileObject[]>>;
@@ -510,7 +587,7 @@ export declare type SharedFilesType = FileObject[];
510
587
 
511
588
  export declare type SharedLabelIdType = string;
512
589
 
513
- export declare type SharedOnInputChangeType = (event: React.ChangeEvent<HTMLInputElement> & React.DragEvent<HTMLLabelElement>) => Promise<void>;
590
+ export declare type SharedOnInputChangeType = (event: React.ChangeEvent<HTMLInputElement> & React.DragEvent<HTMLLabelElement>) => Promise<void | string | null>;
514
591
 
515
592
  export declare const SideBar: ForwardRefExoticComponent<Omit<ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
516
593
  variant?: "default" | "compact" | null | undefined;
@@ -608,6 +685,8 @@ export declare type UploaderContextState = {
608
685
  labelId: SharedLabelIdType;
609
686
  labelContent?: ReactNode;
610
687
  avatarLabelContent?: ReactNode;
688
+ title?: ReactNode;
689
+ errorMessage?: ReactNode;
611
690
  isFileViewable?: boolean;
612
691
  isRequired?: boolean;
613
692
  isDraggable?: boolean;
@@ -628,6 +707,7 @@ export declare type UploaderContextState = {
628
707
  listing?: string;
629
708
  error?: string;
630
709
  input?: string;
710
+ title?: string;
631
711
  };
632
712
  };
633
713
 
@@ -643,6 +723,8 @@ export declare type UploaderProviderProps = UploaderContextState & {
643
723
  children: ReactNode;
644
724
  };
645
725
 
726
+ export declare function UploaderTitle({ ...props }: ComponentProps<"h3">): JSX.Element | null;
727
+
646
728
  export declare function UploadFileErrorBox(): JSX.Element | null;
647
729
 
648
730
  export declare function UploadFileInput(): JSX.Element;
@@ -686,14 +768,14 @@ export declare type UseUploadFileReturn = {
686
768
  isLoading: boolean;
687
769
  setFiles: Dispatch<SetStateAction<FileObject[]>>;
688
770
  handleClearFile: (fileName?: string) => void;
689
- onInputChange: (event: React.ChangeEvent<HTMLInputElement> & React.DragEvent<HTMLLabelElement>) => Promise<void | string | null>;
771
+ onInputChange: SharedOnInputChangeType;
690
772
  };
691
773
 
692
774
  export declare function validateUploadedFiles({ uploadedFiles, maxFileSize, compressFiles, locale, preventDuplicates, files, }: {
693
775
  uploadedFiles: File[];
694
776
  maxFileSize?: number;
695
777
  compressFiles?: boolean;
696
- locale?: Record<string, string> | undefined;
778
+ locale?: Locale;
697
779
  preventDuplicates?: boolean;
698
780
  files: FileObject[];
699
781
  }): Promise<{
@@ -0,0 +1,5 @@
1
+ import { d as a } from "./features-animation-uyo1KMg-.js";
2
+ var r = a;
3
+ export {
4
+ r as default
5
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@js-empire/emperor-ui",
3
3
  "description": "They provide the atoms, we provide the empire.",
4
- "version": "1.2.0",
4
+ "version": "1.2.2",
5
5
  "author": "JS Empire - Mustafa Alhasanat",
6
6
  "license": "ISC",
7
7
  "type": "module",
@@ -38,7 +38,7 @@ export function AvatarLabel() {
38
38
  React.DragEvent<HTMLLabelElement>,
39
39
  ) => {
40
40
  event.preventDefault();
41
- setDraggableMessage(locale?.dropHere || "");
41
+ setDraggableMessage(locale?.atoms?.uploader?.dropHere || "");
42
42
  };
43
43
 
44
44
  const handleDragLeave = () => {
@@ -5,3 +5,4 @@ export * from "./view-image-modal";
5
5
  export * from "./upload-file-listing";
6
6
  export * from "./upload-file-error-box";
7
7
  export * from "./upload-file-input";
8
+ export * from "./uploader-title";
@@ -1,9 +1,9 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
2
  import { Uploader } from "@/components";
3
3
  import { getStorybookDecorators } from "@/utils";
4
- import { UploaderProps } from "@/types";
5
4
  import { useUploader } from "@/hooks";
6
5
  import { useDisclosure } from "@heroui/react";
6
+ import { LangKey } from "@/i18n";
7
7
 
8
8
  const meta: Meta<typeof Uploader> = {
9
9
  title: "Atoms/Uploader",
@@ -17,6 +17,16 @@ const meta: Meta<typeof Uploader> = {
17
17
  layout: {
18
18
  withScaffold: false,
19
19
  },
20
+ interLocalization: {
21
+ locales: {
22
+ [LangKey.ENGLISH]: {
23
+ atoms: {
24
+ uploader: {},
25
+ },
26
+ },
27
+ [LangKey.ARABIC]: {},
28
+ },
29
+ },
20
30
  },
21
31
  }),
22
32
  };
@@ -27,32 +37,32 @@ type Story = StoryObj<typeof meta>;
27
37
 
28
38
  export const Default: Story = {
29
39
  args: {},
30
- render: (args: UploaderProps) => {
40
+ render: () => {
31
41
  const uploadProps = useUploader({
32
42
  fileTypes: ["image"],
33
43
  labelId: "image",
34
44
  });
35
45
 
36
- return <Uploader {...uploadProps} {...args} />;
46
+ return <Uploader {...uploadProps} />;
37
47
  },
38
48
  };
39
49
 
40
50
  export const MultiFiles: Story = {
41
51
  args: {},
42
- render: (args: UploaderProps) => {
52
+ render: () => {
43
53
  const uploadProps = useUploader({
44
54
  fileTypes: ["image"],
45
55
  labelId: "image",
46
56
  isMulti: true,
47
57
  });
48
58
 
49
- return <Uploader {...uploadProps} {...args} />;
59
+ return <Uploader {...uploadProps} />;
50
60
  },
51
61
  };
52
62
 
53
63
  export const WithMaxCount: Story = {
54
64
  args: {},
55
- render: (args: UploaderProps) => {
65
+ render: () => {
56
66
  const uploadProps = useUploader({
57
67
  fileTypes: ["image"],
58
68
  labelId: "image",
@@ -60,38 +70,51 @@ export const WithMaxCount: Story = {
60
70
  maxCount: 2,
61
71
  });
62
72
 
63
- return <Uploader {...uploadProps} {...args} />;
73
+ return <Uploader {...uploadProps} />;
64
74
  },
65
75
  };
66
76
 
67
77
  export const Required: Story = {
68
78
  args: {},
69
- render: (args: UploaderProps) => {
79
+ render: () => {
80
+ const uploadProps = useUploader({
81
+ fileTypes: ["image"],
82
+ labelId: "image",
83
+ isRequired: true,
84
+ });
85
+
86
+ return <Uploader {...uploadProps} />;
87
+ },
88
+ };
89
+
90
+ export const CustomErrorMessage: Story = {
91
+ args: {},
92
+ render: () => {
70
93
  const uploadProps = useUploader({
71
94
  fileTypes: ["image"],
72
95
  labelId: "image",
73
96
  isRequired: true,
74
97
  });
75
98
 
76
- return <Uploader {...uploadProps} {...args} />;
99
+ return <Uploader errorMessage="Please upload an image" {...uploadProps} />;
77
100
  },
78
101
  };
79
102
 
80
103
  export const HideListings: Story = {
81
104
  args: {},
82
- render: (args: UploaderProps) => {
105
+ render: () => {
83
106
  const uploadProps = useUploader({
84
107
  fileTypes: ["image"],
85
108
  labelId: "image",
86
109
  });
87
110
 
88
- return <Uploader hideListing {...uploadProps} {...args} />;
111
+ return <Uploader hideListing {...uploadProps} />;
89
112
  },
90
113
  };
91
114
 
92
115
  export const ViewableImages: Story = {
93
116
  args: {},
94
- render: (args: UploaderProps) => {
117
+ render: () => {
95
118
  const uploadProps = useUploader({
96
119
  fileTypes: ["image"],
97
120
  labelId: "image",
@@ -106,7 +129,6 @@ export const ViewableImages: Story = {
106
129
  ...modalProps,
107
130
  }}
108
131
  {...uploadProps}
109
- {...args}
110
132
  />
111
133
  );
112
134
  },
@@ -114,20 +136,20 @@ export const ViewableImages: Story = {
114
136
 
115
137
  export const AllowDuplicates: Story = {
116
138
  args: {},
117
- render: (args: UploaderProps) => {
139
+ render: () => {
118
140
  const uploadProps = useUploader({
119
141
  fileTypes: ["image"],
120
142
  labelId: "image",
121
143
  preventDuplicates: false,
122
144
  });
123
145
 
124
- return <Uploader {...uploadProps} {...args} />;
146
+ return <Uploader {...uploadProps} />;
125
147
  },
126
148
  };
127
149
 
128
150
  export const CompressFiles: Story = {
129
151
  args: {},
130
- render: (args: UploaderProps) => {
152
+ render: () => {
131
153
  const uploadProps = useUploader({
132
154
  fileTypes: ["image"],
133
155
  labelId: "uncompressed-image",
@@ -145,8 +167,8 @@ export const CompressFiles: Story = {
145
167
  <h3 className="text-lg font-bold">Uncompressed</h3>
146
168
  <h3 className="text-lg font-bold">Compressed</h3>
147
169
 
148
- <Uploader {...uploadProps} {...args} />
149
- <Uploader {...compressedUploadProps} {...args} />
170
+ <Uploader {...uploadProps} />
171
+ <Uploader {...compressedUploadProps} />
150
172
 
151
173
  <p className="text-sm text-gray-500">
152
174
  File size: {uploadProps.files[0]?.file?.size || "---"}
@@ -158,3 +180,15 @@ export const CompressFiles: Story = {
158
180
  );
159
181
  },
160
182
  };
183
+
184
+ export const WithTitle: Story = {
185
+ args: {},
186
+ render: () => {
187
+ const uploadProps = useUploader({
188
+ fileTypes: ["image"],
189
+ labelId: "image",
190
+ });
191
+
192
+ return <Uploader {...uploadProps} title="Upload your image" />;
193
+ },
194
+ };
@@ -6,7 +6,7 @@ import { useMemo } from "react";
6
6
 
7
7
  export function UploadFileErrorBox() {
8
8
  const { config } = useEmperorUI();
9
- const { files, isRequired, classNames } = useUploaderContext();
9
+ const { files, isRequired, classNames, errorMessage } = useUploaderContext();
10
10
 
11
11
  const locales = config?.interLocalization?.locales;
12
12
  const lang = config?.interLocalization?.lang;
@@ -21,7 +21,7 @@ export function UploadFileErrorBox() {
21
21
  if (isError)
22
22
  return (
23
23
  <p className={cn("text-[14px] text-danger", classNames?.error)}>
24
- {locale?.errorUploadingFile}
24
+ {errorMessage || locale?.atoms?.uploader?.errorUploadingFile}
25
25
  </p>
26
26
  );
27
27
 
@@ -35,7 +35,7 @@ export function UploadFileLabel() {
35
35
  React.DragEvent<HTMLLabelElement>,
36
36
  ) => {
37
37
  event.preventDefault();
38
- setDraggableMessage(locale?.dropHere || "");
38
+ setDraggableMessage(locale?.atoms?.uploader?.dropHere || "");
39
39
  };
40
40
 
41
41
  const handleDragLeave = () => {
@@ -61,8 +61,12 @@ export function UploadFileLabel() {
61
61
  <div className="pointer-events-none flex size-full flex-col items-center justify-center gap-2 rounded-md border border-dashed bg-primary/10 px-2 py-8 text-xs">
62
62
  <UploadCloud className="size-10 text-primary" />
63
63
 
64
- <p className="font-bold">{locale?.selectFile || ""}</p>
65
- <p className="opacity-70">{locale?.selectionTypes || ""}</p>
64
+ <p className="font-bold">
65
+ {locale?.atoms?.uploader?.selectFile || ""}
66
+ </p>
67
+ <p className="opacity-70">
68
+ {locale?.atoms?.uploader?.selectionTypes || ""}
69
+ </p>
66
70
 
67
71
  {draggableMessage && (
68
72
  <p className="text-sm font-bold">{draggableMessage}</p>
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import { useUploaderContext } from "@/hooks";
4
+ import { ComponentProps } from "react";
5
+ import { cn } from "@/utils";
6
+
7
+ export function UploaderTitle({ ...props }: ComponentProps<"h3">) {
8
+ const { title, classNames } = useUploaderContext();
9
+
10
+ if (!title) return null;
11
+
12
+ return (
13
+ <h3
14
+ className={cn("text-lg font-bold", classNames?.title)}
15
+ data-slot="emperor-uploader-title"
16
+ {...props}
17
+ >
18
+ {title}
19
+ </h3>
20
+ );
21
+ }
@@ -10,6 +10,7 @@ import {
10
10
  UploadFileListing,
11
11
  UploadFileErrorBox,
12
12
  UploadFileInput,
13
+ UploaderTitle,
13
14
  } from "@/components";
14
15
 
15
16
  const uploaderStyles = cva(["w-full flex flex-col gap-2"], {
@@ -40,6 +41,8 @@ export const Uploader = forwardRef<
40
41
  return (
41
42
  <UploaderProvider {...props}>
42
43
  <div ref={ref} className={cn(uploaderStyles({ className }))} {...props}>
44
+ <UploaderTitle />
45
+
43
46
  {isAvatar ? <AvatarLabel /> : <UploadFileLabel />}
44
47
 
45
48
  {!hideListing && <UploadFileListing />}
@@ -33,7 +33,7 @@ export const Footer = forwardRef<
33
33
  return (
34
34
  <footer
35
35
  ref={ref}
36
- data-slot="emperor-footer"
36
+ data-slot="emperor-ui-footer"
37
37
  className={cn(
38
38
  footerClasses({
39
39
  variant,
@@ -1,6 +1,15 @@
1
1
  import type { ListingsProps } from "@/types";
2
- import { cn } from "@/utils";
2
+ import { listingsClasses, listingsStyles } from "./styles";
3
3
 
4
- export function Listings({ className }: ListingsProps) {
5
- return <div className={cn("", className)}>Listings Component</div>;
4
+ export function Listings<ListingType>({
5
+ className,
6
+ variant = "default",
7
+ }: ListingsProps<ListingType>) {
8
+ return (
9
+ <section
10
+ data-slot="emperor-ui-listings"
11
+ className={listingsClasses({ className, variant })}
12
+ style={listingsStyles({ variant })}
13
+ ></section>
14
+ );
6
15
  }
@@ -0,0 +1,30 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Listings } from "@/components";
3
+ import { getStorybookDecorators } from "@/utils";
4
+ import { getListings, MockItemType } from "@/mocks";
5
+
6
+ const meta: Meta<typeof Listings> = {
7
+ title: "Organisms/Listings",
8
+ component: Listings,
9
+ parameters: {
10
+ layout: "fullscreen",
11
+ },
12
+ tags: ["autodocs"],
13
+ decorators: getStorybookDecorators({}),
14
+ };
15
+
16
+ export default meta;
17
+
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Default: Story = {
21
+ args: {},
22
+ render: (args) => {
23
+ const items = getListings({
24
+ page: 1,
25
+ pageSize: 10,
26
+ });
27
+
28
+ return <Listings<MockItemType> {...args} items={items} />;
29
+ },
30
+ };
@@ -0,0 +1,11 @@
1
+ import { cva } from "class-variance-authority";
2
+
3
+ export const listingsClasses = cva([], {
4
+ variants: {
5
+ variant: {
6
+ default: [],
7
+ },
8
+ },
9
+ defaultVariants: {},
10
+ compoundVariants: [],
11
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./classes";
2
+ export * from "./styles";
@@ -0,0 +1,6 @@
1
+ import { CSSProperties } from "react";
2
+
3
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type, no-empty-pattern
4
+ export const listingsStyles = ({}: {}): CSSProperties => {
5
+ return {};
6
+ };
@@ -1,3 +1,4 @@
1
+ import { en, ar } from "@/i18n";
1
2
  import type { ColorsPalette, EmperorUIConfig } from "@/types";
2
3
 
3
4
  export const defaultColorsPalette: ColorsPalette = {
@@ -26,32 +27,8 @@ export const defaultEmperorUIConfig: EmperorUIConfig = {
26
27
  dir: "ltr",
27
28
  isMultiLingual: false,
28
29
  locales: {
29
- en: {
30
- dropHere: "Drop file here",
31
- selectFile: "Select a file or drag and drop here",
32
- selectionTypes: "JPG, PNG or PDF, file size no more than 10MB",
33
- selectBtn: "Select file",
34
- errorUploadingFile: "No file was uploaded",
35
- maxNumImages: "Maximum number of uploaded images exceeded: ",
36
- errorUploadedTypes: "You can only upload files within these types: ",
37
- maxSizeExceededError:
38
- "You can only upload files/images with less than MAX_FILE_SIZE Mega Bytes, you're file's size is UPLOADED_FILE_SIZE Mega Bytes",
39
- duplicatesDenied:
40
- "You can't upload images with duplicate names, you may rename them before the upload process",
41
- },
42
- ar: {
43
- dropHere: "أسقط الملف هنا",
44
- selectFile: "حدد ملفًا أو اسحبه وأفلته هنا",
45
- selectionTypes: "JPG, PNG أو PDF، حجم الملف لا يزيد عن 10MB",
46
- selectBtn: "حدد ملف",
47
- errorUploadingFile: "لم يتم تحميل أي ملف",
48
- maxNumImages: "تم تجاوز الحد الأقصى للصور المرفوعة: ",
49
- errorUploadedTypes: "يمكنك فقط تحميل الملفات من الأنواع التالية: ",
50
- maxSizeExceededError:
51
- "يمكنك فقط رفع ملفات/صور بحجم أقل من MAX_FILE_SIZE ميغابايت، بينما حجم الملف الذي رفعته هو UPLOADED_FILE_SIZE ميغابايت",
52
- duplicatesDenied:
53
- "لا يمكنك رفع صور تحمل نفس الاسم، يمكنك إعادة تسميتها قبل عملية الرفع",
54
- },
30
+ en,
31
+ ar,
55
32
  },
56
33
  },
57
34
  };
@@ -10,6 +10,7 @@ import {
10
10
  refineUploadedFiles,
11
11
  } from "@/utils";
12
12
  import { ONE_MEGABYTE } from "@/constants";
13
+ import { Locale } from "@/i18n";
13
14
 
14
15
  export const useUploader = ({
15
16
  labelContent,
@@ -31,7 +32,7 @@ export const useUploader = ({
31
32
  const locales = config?.interLocalization?.locales;
32
33
  const lang = config?.interLocalization?.lang;
33
34
 
34
- const locale = locales?.[lang || "en"];
35
+ const locale = locales?.[lang || "en"] as Locale;
35
36
 
36
37
  // remove a specific uploaded file
37
38
  const handleClearFile = (fileName?: string) => {
@@ -58,7 +59,7 @@ export const useUploader = ({
58
59
  (!event?.dataTransfer?.files || !event?.dataTransfer?.files[0])
59
60
  ) {
60
61
  return addToast({
61
- title: locale?.errorUploadingFile,
62
+ title: locale?.atoms?.uploader?.errorUploadingFile,
62
63
  });
63
64
  }
64
65
 
@@ -80,7 +81,7 @@ export const useUploader = ({
80
81
  files?.length + uploadedFiles?.length > maxCount)
81
82
  ) {
82
83
  addToast({
83
- title: `${locale?.maxNumImages} ${maxCount}`,
84
+ title: `${locale?.atoms?.uploader?.maxNumImages} ${maxCount}`,
84
85
  });
85
86
  return;
86
87
  }
@@ -92,7 +93,7 @@ export const useUploader = ({
92
93
  ?.every((type) => allowedTypes.includes(type))
93
94
  ) {
94
95
  addToast({
95
- title: `${locale?.errorUploadedTypes} ${allowedTypes.join(", ")}`,
96
+ title: `${locale?.atoms?.uploader?.errorUploadedTypes} ${allowedTypes.join(", ")}`,
96
97
  });
97
98
  return;
98
99
  }