@js-empire/emperor-ui 1.2.6 → 1.2.7

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 (57) hide show
  1. package/package.json +2 -1
  2. package/src/components/atoms/uploader/{avatar-label.tsx → components/avatar-label.tsx} +17 -5
  3. package/src/components/atoms/uploader/components/index.ts +8 -0
  4. package/src/components/atoms/uploader/components/upload-file-error-box.tsx +40 -0
  5. package/src/components/atoms/uploader/{upload-file-label.tsx → components/upload-file-label.tsx} +18 -11
  6. package/src/components/atoms/uploader/index.ts +1 -8
  7. package/src/components/atoms/uploader/stories/uploader.stories.tsx +17 -10
  8. package/src/components/molecules/item-card/index.ts +2 -0
  9. package/src/components/molecules/item-card/item-actions-buttons.tsx +43 -0
  10. package/src/components/molecules/item-card/item-actions-overlay.tsx +41 -0
  11. package/src/components/molecules/item-card/item-card-body.tsx +8 -2
  12. package/src/components/molecules/item-card/item-card-footer.tsx +22 -1
  13. package/src/components/molecules/item-card/item-card-header.tsx +8 -2
  14. package/src/components/molecules/item-card/item-card.tsx +39 -1
  15. package/src/components/molecules/item-card/stories/item-card.stories.tsx +36 -0
  16. package/src/components/organisms/deletion-confirmor/deletion-confirmor.tsx +114 -0
  17. package/src/components/organisms/deletion-confirmor/index.ts +3 -0
  18. package/src/components/organisms/deletion-confirmor/stories/components.tsx +22 -0
  19. package/src/components/organisms/deletion-confirmor/stories/deletion-confirmor.stories.tsx +78 -0
  20. package/src/components/organisms/deletion-confirmor/styles/classes.ts +28 -0
  21. package/src/components/organisms/deletion-confirmor/styles/index.ts +2 -0
  22. package/src/components/organisms/deletion-confirmor/styles/styles.ts +4 -0
  23. package/src/components/organisms/index.ts +1 -0
  24. package/src/constants/card.tsx +5 -2
  25. package/src/hooks/use-uploader.tsx +21 -9
  26. package/src/i18n/locales/atoms/ar.ts +1 -1
  27. package/src/i18n/locales/atoms/en.ts +1 -1
  28. package/src/i18n/locales/organisms/ar.ts +4 -0
  29. package/src/i18n/locales/organisms/en.ts +4 -0
  30. package/src/mocks/deletion-confirmor.ts +16 -0
  31. package/src/mocks/index.ts +2 -0
  32. package/src/mocks/locales/index.ts +1 -0
  33. package/src/mocks/locales/uploader.ts +33 -0
  34. package/src/types/components/atoms/uploader.ts +4 -0
  35. package/src/types/components/molecules/item-card/item-card.ts +14 -4
  36. package/src/types/components/organisms/deletion-confirmor/deletion-confirmor.ts +22 -0
  37. package/src/types/components/organisms/deletion-confirmor/index.ts +1 -0
  38. package/src/types/components/organisms/index.ts +1 -0
  39. package/src/utils/uploader.ts +25 -7
  40. package/dist/emperor-ui.js +0 -134
  41. package/dist/emperor-ui.umd.cjs +0 -77
  42. package/dist/globals.css +0 -1
  43. package/dist/icons/emperor-ui-logo.ico +0 -0
  44. package/dist/images/avatar-female.jpg +0 -0
  45. package/dist/images/avatar-male.jpg +0 -0
  46. package/dist/images/emperor-ui-logo.png +0 -0
  47. package/dist/index-CLEmvl4g.js +0 -290
  48. package/dist/index-DceZlwg2.js +0 -5
  49. package/dist/index-KrC7oBFa.js +0 -59874
  50. package/dist/index.d.ts +0 -1220
  51. package/dist/src-UW24ZMRV-DfI_MdGD.js +0 -5
  52. package/src/components/atoms/uploader/upload-file-error-box.tsx +0 -29
  53. /package/src/components/atoms/uploader/{upload-file-input.tsx → components/upload-file-input.tsx} +0 -0
  54. /package/src/components/atoms/uploader/{upload-file-listing.tsx → components/upload-file-listing.tsx} +0 -0
  55. /package/src/components/atoms/uploader/{uploader-title.tsx → components/uploader-title.tsx} +0 -0
  56. /package/src/components/atoms/uploader/{uploader.tsx → components/uploader.tsx} +0 -0
  57. /package/src/components/atoms/uploader/{view-image-modal.tsx → components/view-image-modal.tsx} +0 -0
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.6",
4
+ "version": "1.2.7",
5
5
  "author": "JS Empire - Mustafa Alhasanat",
6
6
  "license": "ISC",
7
7
  "type": "module",
@@ -73,6 +73,7 @@
73
73
  "@heroui/system": "^2.4.27",
74
74
  "@heroui/theme": "^2.4.25",
75
75
  "@heroui/toast": "^2.0.21",
76
+ "@heroui/tooltip": "^2.2.28",
76
77
  "@hookform/resolvers": "^5.2.2",
77
78
  "@internationalized/date": "^3.11.0",
78
79
  "@storybook/react": "^10.1.8",
@@ -3,13 +3,15 @@
3
3
  import { Placeholders } from "@/enums";
4
4
  import { Avatar } from "@heroui/avatar";
5
5
  import { Spinner } from "@heroui/spinner";
6
- import { cn } from "@/utils";
6
+ import { cn, mergeUploaderLocale } from "@/utils";
7
7
  import { useEmperorUI, useUploaderContext } from "@/hooks";
8
8
  import { useState } from "react";
9
+ import { getLocales, Locale } from "@/i18n";
9
10
 
10
11
  export function AvatarLabel() {
11
12
  const { config } = useEmperorUI();
12
13
  const [draggableMessage, setDraggableMessage] = useState<string | null>(null);
14
+
13
15
  const {
14
16
  labelId,
15
17
  classNames,
@@ -21,12 +23,22 @@ export function AvatarLabel() {
21
23
  files,
22
24
  isLoading,
23
25
  isMulti,
26
+ locales,
24
27
  } = useUploaderContext();
25
28
 
26
- const locales = config?.interLocalization?.locales;
27
- const lang = config?.interLocalization?.lang;
29
+ const configLocales = config?.interLocalization?.locales;
30
+ const lang =
31
+ config?.interLocalization?.lang ||
32
+ config?.interLocalization?.defaultLanguage ||
33
+ "en";
28
34
 
29
- const locale = locales?.[lang || "en"];
35
+ const defaultLocale = getLocales(lang);
36
+ const uploaderLocale = mergeUploaderLocale({
37
+ defaultUploaderLocale: defaultLocale.atoms.uploader,
38
+ configUploaderLocale: (configLocales?.[lang] as Locale | undefined)?.atoms
39
+ ?.uploader,
40
+ contextUploaderLocale: locales,
41
+ });
30
42
 
31
43
  const handleDrop = (
32
44
  event: React.ChangeEvent<HTMLInputElement> &
@@ -41,7 +53,7 @@ export function AvatarLabel() {
41
53
  React.DragEvent<HTMLLabelElement>,
42
54
  ) => {
43
55
  event.preventDefault();
44
- setDraggableMessage(locale?.atoms?.uploader?.dropHere || "");
56
+ setDraggableMessage(uploaderLocale.dropHere);
45
57
  };
46
58
 
47
59
  const handleDragLeave = () => {
@@ -0,0 +1,8 @@
1
+ export * from "./uploader";
2
+ export * from "./avatar-label";
3
+ export * from "./upload-file-label";
4
+ export * from "./view-image-modal";
5
+ export * from "./upload-file-listing";
6
+ export * from "./upload-file-error-box";
7
+ export * from "./upload-file-input";
8
+ export * from "./uploader-title";
@@ -0,0 +1,40 @@
1
+ "use client";
2
+
3
+ import { cn, mergeUploaderLocale } from "@/utils";
4
+ import { useEmperorUI, useUploaderContext } from "@/hooks";
5
+ import { useMemo } from "react";
6
+ import { getLocales, Locale } from "@/i18n";
7
+
8
+ export function UploadFileErrorBox() {
9
+ const { config } = useEmperorUI();
10
+ const { files, isRequired, classNames, errorMessage, locales } =
11
+ useUploaderContext();
12
+
13
+ const configLocales = config?.interLocalization?.locales;
14
+ const lang =
15
+ config?.interLocalization?.lang ||
16
+ config?.interLocalization?.defaultLanguage ||
17
+ "en";
18
+
19
+ const defaultLocale = getLocales(lang);
20
+ const uploaderLocale = mergeUploaderLocale({
21
+ defaultUploaderLocale: defaultLocale.atoms.uploader,
22
+ configUploaderLocale: (configLocales?.[lang] as Locale | undefined)?.atoms
23
+ ?.uploader,
24
+ contextUploaderLocale: locales,
25
+ });
26
+
27
+ const isError = useMemo(
28
+ () => files?.length === 0 && isRequired,
29
+ [files, isRequired],
30
+ );
31
+
32
+ if (isError)
33
+ return (
34
+ <p className={cn("text-[14px] text-danger", classNames?.error)}>
35
+ {errorMessage ?? uploaderLocale.errorUploadingFile}
36
+ </p>
37
+ );
38
+
39
+ return null;
40
+ }
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import { Spinner } from "@heroui/spinner";
4
- import { cn } from "@/utils";
4
+ import { cn, mergeUploaderLocale } from "@/utils";
5
5
  import { useEmperorUI, useUploaderContext } from "@/hooks";
6
6
  import { UploadCloud } from "lucide-react";
7
7
  import { useState } from "react";
8
+ import { getLocales, Locale } from "@/i18n";
8
9
 
9
10
  export function UploadFileLabel() {
10
11
  const { config } = useEmperorUI();
@@ -18,12 +19,22 @@ export function UploadFileLabel() {
18
19
  isLoading,
19
20
  isMulti,
20
21
  files,
22
+ locales,
21
23
  } = useUploaderContext();
22
24
 
23
- const locales = config?.interLocalization?.locales;
24
- const lang = config?.interLocalization?.lang;
25
+ const configLocales = config?.interLocalization?.locales;
26
+ const lang =
27
+ config?.interLocalization?.lang ||
28
+ config?.interLocalization?.defaultLanguage ||
29
+ "en";
25
30
 
26
- const locale = locales?.[lang || "en"];
31
+ const defaultLocale = getLocales(lang);
32
+ const uploaderLocale = mergeUploaderLocale({
33
+ defaultUploaderLocale: defaultLocale.atoms.uploader,
34
+ configUploaderLocale: (configLocales?.[lang] as Locale | undefined)?.atoms
35
+ ?.uploader,
36
+ contextUploaderLocale: locales,
37
+ });
27
38
 
28
39
  const handleDrop = (
29
40
  event: React.ChangeEvent<HTMLInputElement> &
@@ -38,7 +49,7 @@ export function UploadFileLabel() {
38
49
  React.DragEvent<HTMLLabelElement>,
39
50
  ) => {
40
51
  event.preventDefault();
41
- setDraggableMessage(locale?.atoms?.uploader?.dropHere || "");
52
+ setDraggableMessage(uploaderLocale.dropHere);
42
53
  };
43
54
 
44
55
  const handleDragLeave = () => {
@@ -68,12 +79,8 @@ export function UploadFileLabel() {
68
79
  <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">
69
80
  <UploadCloud className="size-10 text-primary" />
70
81
 
71
- <p className="font-bold">
72
- {locale?.atoms?.uploader?.selectFile || ""}
73
- </p>
74
- <p className="opacity-70">
75
- {locale?.atoms?.uploader?.selectionTypes || ""}
76
- </p>
82
+ <p className="font-bold">{uploaderLocale.selectFile}</p>
83
+ <p className="opacity-70">{uploaderLocale.selectionTypes}</p>
77
84
 
78
85
  {draggableMessage && (
79
86
  <p className="text-sm font-bold">{draggableMessage}</p>
@@ -1,8 +1 @@
1
- export * from "./uploader";
2
- export * from "./avatar-label";
3
- export * from "./upload-file-label";
4
- export * from "./view-image-modal";
5
- export * from "./upload-file-listing";
6
- export * from "./upload-file-error-box";
7
- export * from "./upload-file-input";
8
- export * from "./uploader-title";
1
+ export * from "./components";
@@ -3,6 +3,7 @@ import { Uploader } from "@/components";
3
3
  import { getStorybookDecorators } from "@/utils";
4
4
  import { useUploader } from "@/hooks";
5
5
  import { useDisclosure } from "@heroui/modal";
6
+ import { uploaderLocalesMock } from "@/mocks";
6
7
  import { LangKey } from "@/i18n";
7
8
 
8
9
  const meta: Meta<typeof Uploader> = {
@@ -17,16 +18,6 @@ const meta: Meta<typeof Uploader> = {
17
18
  layout: {
18
19
  withScaffold: false,
19
20
  },
20
- interLocalization: {
21
- locales: {
22
- [LangKey.ENGLISH]: {
23
- atoms: {
24
- uploader: {},
25
- },
26
- },
27
- [LangKey.ARABIC]: {},
28
- },
29
- },
30
21
  },
31
22
  }),
32
23
  };
@@ -192,3 +183,19 @@ export const WithTitle: Story = {
192
183
  return <Uploader {...uploadProps} title="Upload your image" />;
193
184
  },
194
185
  };
186
+
187
+ export const WithCustomLocales: Story = {
188
+ args: {
189
+ locales: uploaderLocalesMock[LangKey.ENGLISH],
190
+ },
191
+ render: (args) => {
192
+ const uploadProps = useUploader({
193
+ fileTypes: ["image", "pdf"],
194
+ labelId: "image",
195
+ isRequired: true,
196
+ isMulti: true,
197
+ });
198
+
199
+ return <Uploader {...uploadProps} {...args} />;
200
+ },
201
+ };
@@ -2,6 +2,8 @@ export * from "./item-card";
2
2
  export * from "./loading-item";
3
3
  export * from "./item-banner";
4
4
  export * from "./item-actions-dropdown";
5
+ export * from "./item-actions-buttons";
6
+ export * from "./item-actions-overlay";
5
7
  export * from "./item-card-header";
6
8
  export * from "./item-card-body";
7
9
  export * from "./item-card-footer";
@@ -0,0 +1,43 @@
1
+ "use client";
2
+
3
+ import type { ItemCardProps } from "@/types";
4
+ import { cn } from "@/utils";
5
+ import { Button } from "@heroui/button";
6
+
7
+ type ItemActionsButtonsProps = Pick<
8
+ ItemCardProps,
9
+ "actions" | "classNames" | "onActionClick"
10
+ > & {
11
+ className?: string;
12
+ };
13
+
14
+ export function ItemActionsButtons({
15
+ actions,
16
+ classNames,
17
+ onActionClick,
18
+ className,
19
+ }: ItemActionsButtonsProps) {
20
+ if (!actions || actions.length === 0) return null;
21
+
22
+ return (
23
+ <div
24
+ data-slot="emperor-ui-item-card-actions-buttons"
25
+ className={cn("flex flex-wrap gap-2", classNames?.actions, className)}
26
+ >
27
+ {actions.map(
28
+ ({ key, label, className: actionClassName, ...props }, index) => (
29
+ <Button
30
+ key={key ?? index}
31
+ data-slot="emperor-ui-item-card-actions-button"
32
+ size="sm"
33
+ className={cn(classNames?.action, actionClassName)}
34
+ onPress={() => key && onActionClick?.(String(key))}
35
+ {...props}
36
+ >
37
+ {label}
38
+ </Button>
39
+ ),
40
+ )}
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import type { ItemCardProps } from "@/types";
4
+ import { cn } from "@/utils";
5
+ import { Button } from "@heroui/button";
6
+
7
+ type ItemActionsOverlayProps = Pick<
8
+ ItemCardProps,
9
+ "actions" | "classNames" | "onActionClick"
10
+ >;
11
+
12
+ export function ItemActionsOverlay({
13
+ actions,
14
+ classNames,
15
+ onActionClick,
16
+ }: ItemActionsOverlayProps) {
17
+ if (!actions || actions.length === 0) return null;
18
+
19
+ return (
20
+ <div
21
+ data-slot="emperor-ui-item-card-actions-overlay-buttons"
22
+ className={cn("flex flex-col items-center gap-3", classNames?.actions)}
23
+ >
24
+ {actions?.map(
25
+ ({ key, label, className: actionClassName, ...props }, index) => {
26
+ return (
27
+ <Button
28
+ key={key ?? index}
29
+ data-slot="emperor-ui-item-card-actions-overlay-button"
30
+ isIconOnly
31
+ radius="full"
32
+ className={cn(classNames?.action, actionClassName)}
33
+ onPress={() => key && onActionClick?.(String(key))}
34
+ {...props}
35
+ />
36
+ );
37
+ },
38
+ )}
39
+ </div>
40
+ );
41
+ }
@@ -17,9 +17,15 @@ export function ItemCardBody({
17
17
  classNames,
18
18
  actions,
19
19
  onActionClick,
20
+ actionsViewVariant = "dropdown",
20
21
  }: Pick<
21
22
  ItemCardProps,
22
- "item" | "orientation" | "classNames" | "actions" | "onActionClick"
23
+ | "item"
24
+ | "orientation"
25
+ | "classNames"
26
+ | "actions"
27
+ | "onActionClick"
28
+ | "actionsViewVariant"
23
29
  >) {
24
30
  return (
25
31
  <CardBody
@@ -56,7 +62,7 @@ export function ItemCardBody({
56
62
  </p>
57
63
  )}
58
64
 
59
- {orientation === "horizontal" && (
65
+ {orientation === "horizontal" && actionsViewVariant === "dropdown" && (
60
66
  <ItemActionsDropdown
61
67
  actions={actions}
62
68
  classNames={classNames}
@@ -5,12 +5,24 @@ import { cn } from "@/utils";
5
5
  import { itemFooterClasses, itemChipsClasses } from "./styles";
6
6
  import { CardFooter } from "@heroui/card";
7
7
  import { Chip } from "@heroui/chip";
8
+ import { ItemActionsButtons } from "./item-actions-buttons";
8
9
 
9
10
  export function ItemCardFooter({
10
11
  item,
11
12
  orientation,
12
13
  classNames,
13
- }: Pick<ItemCardProps, "item" | "orientation" | "classNames">) {
14
+ actions,
15
+ onActionClick,
16
+ actionsViewVariant = "dropdown",
17
+ }: Pick<
18
+ ItemCardProps,
19
+ | "item"
20
+ | "orientation"
21
+ | "classNames"
22
+ | "actions"
23
+ | "onActionClick"
24
+ | "actionsViewVariant"
25
+ >) {
14
26
  return (
15
27
  <CardFooter
16
28
  data-slot="emperor-ui-item-card-footer"
@@ -50,6 +62,15 @@ export function ItemCardFooter({
50
62
  )}
51
63
  </menu>
52
64
  )}
65
+
66
+ {actionsViewVariant === "buttons" && actions && actions.length > 0 && (
67
+ <ItemActionsButtons
68
+ actions={actions}
69
+ classNames={classNames}
70
+ onActionClick={onActionClick}
71
+ className="ml-auto"
72
+ />
73
+ )}
53
74
  </CardFooter>
54
75
  );
55
76
  }
@@ -15,9 +15,15 @@ export function ItemCardHeader({
15
15
  classNames,
16
16
  actions,
17
17
  onActionClick,
18
+ actionsViewVariant = "dropdown",
18
19
  }: Pick<
19
20
  ItemCardProps,
20
- "item" | "orientation" | "classNames" | "actions" | "onActionClick"
21
+ | "item"
22
+ | "orientation"
23
+ | "classNames"
24
+ | "actions"
25
+ | "onActionClick"
26
+ | "actionsViewVariant"
21
27
  >) {
22
28
  return (
23
29
  <CardHeader
@@ -49,7 +55,7 @@ export function ItemCardHeader({
49
55
  classNames={classNames}
50
56
  />
51
57
 
52
- {orientation === "vertical" && (
58
+ {orientation === "vertical" && actionsViewVariant === "dropdown" && (
53
59
  <ItemActionsDropdown
54
60
  actions={actions}
55
61
  classNames={classNames}
@@ -1,6 +1,10 @@
1
1
  "use client";
2
2
 
3
- import type { ItemCardOrientation, ItemCardProps } from "@/types";
3
+ import type {
4
+ ItemCardActionsViewVariant,
5
+ ItemCardOrientation,
6
+ ItemCardProps,
7
+ } from "@/types";
4
8
  import { cn } from "@/utils";
5
9
  import { Card } from "@heroui/card";
6
10
  import { motion } from "framer-motion";
@@ -13,6 +17,7 @@ import {
13
17
  ItemCardFooter,
14
18
  } from "@/components";
15
19
  import { useWindowSize } from "@/hooks";
20
+ import { ItemActionsOverlay } from "./item-actions-overlay";
16
21
 
17
22
  export function ItemCard({
18
23
  variants,
@@ -24,6 +29,7 @@ export function ItemCard({
24
29
  hoverEffect = "none",
25
30
  onActionClick,
26
31
  orientation: defaultOrientation = "vertical",
32
+ actionsViewVariant = "dropdown",
27
33
  }: ItemCardProps) {
28
34
  const { isExtraSmallDevice } = useWindowSize();
29
35
 
@@ -40,11 +46,18 @@ export function ItemCard({
40
46
  />
41
47
  );
42
48
 
49
+ const hasActions = actions && actions.length > 0;
50
+ const isDropdownVariant: boolean =
51
+ (actionsViewVariant as ItemCardActionsViewVariant) === "dropdown";
52
+ const isHoverOverlayVariant: boolean =
53
+ (actionsViewVariant as ItemCardActionsViewVariant) === "hover-overlay";
54
+
43
55
  return (
44
56
  <motion.div
45
57
  data-slot="emperor-ui-item-card"
46
58
  className={cn(
47
59
  itemCardMotionClasses({ orientation }),
60
+ "group",
48
61
  classNames?.base,
49
62
  className,
50
63
  )}
@@ -56,6 +69,7 @@ export function ItemCard({
56
69
  data-slot="emperor-ui-item-card-main-wrapper"
57
70
  className={cn(
58
71
  itemMainWrapperClasses({ orientation }),
72
+ "relative overflow-hidden",
59
73
  classNames?.mainWrapper,
60
74
  )}
61
75
  >
@@ -65,6 +79,9 @@ export function ItemCard({
65
79
  classNames={classNames}
66
80
  actions={actions}
67
81
  onActionClick={onActionClick}
82
+ actionsViewVariant={
83
+ isDropdownVariant ? "dropdown" : actionsViewVariant
84
+ }
68
85
  />
69
86
 
70
87
  <ItemCardBody
@@ -73,13 +90,34 @@ export function ItemCard({
73
90
  classNames={classNames}
74
91
  actions={actions}
75
92
  onActionClick={onActionClick}
93
+ actionsViewVariant={
94
+ isDropdownVariant ? "dropdown" : actionsViewVariant
95
+ }
76
96
  />
77
97
 
78
98
  <ItemCardFooter
79
99
  item={item}
80
100
  orientation={orientation}
81
101
  classNames={classNames}
102
+ actions={actions}
103
+ onActionClick={onActionClick}
104
+ actionsViewVariant={actionsViewVariant}
82
105
  />
106
+ {isHoverOverlayVariant && hasActions && (
107
+ <div
108
+ data-slot="emperor-ui-item-card-actions-hover-overlay"
109
+ className={cn(
110
+ "pointer-events-none absolute inset-0 z-20 flex items-center justify-center bg-black/60 opacity-0",
111
+ "transition-opacity duration-200 group-hover:opacity-100",
112
+ )}
113
+ >
114
+ <ItemActionsOverlay
115
+ actions={actions}
116
+ classNames={classNames}
117
+ onActionClick={onActionClick}
118
+ />
119
+ </div>
120
+ )}
83
121
  </Card>
84
122
  </motion.div>
85
123
  );
@@ -117,6 +117,42 @@ export const WithActions: Story = {
117
117
  },
118
118
  };
119
119
 
120
+ export const WithButtonsActions: Story = {
121
+ args: {
122
+ item: {
123
+ key: String(MOCK_LISTINGS[0]?.id),
124
+ title: MOCK_LISTINGS[0]?.title,
125
+ description: MOCK_LISTINGS[0]?.description,
126
+ image: {
127
+ src: MOCK_LISTINGS[0]?.image || "",
128
+ alt: MOCK_LISTINGS[0]?.title || "",
129
+ },
130
+ },
131
+ actions: ITEM_CARD_ACTIONS,
132
+ actionsViewVariant: "buttons",
133
+ },
134
+ };
135
+
136
+ export const WithHoverOverlayActions: Story = {
137
+ args: {
138
+ item: {
139
+ key: String(MOCK_LISTINGS[0]?.id),
140
+ title: MOCK_LISTINGS[0]?.title,
141
+ description: MOCK_LISTINGS[0]?.description,
142
+ image: {
143
+ src: MOCK_LISTINGS[0]?.image || "",
144
+ alt: MOCK_LISTINGS[0]?.title || "",
145
+ },
146
+ },
147
+ actions: ITEM_CARD_ACTIONS?.map((action) => ({
148
+ ...action,
149
+ variant: "solid",
150
+ size: "lg",
151
+ })),
152
+ actionsViewVariant: "hover-overlay",
153
+ },
154
+ };
155
+
120
156
  export const WithChips: Story = {
121
157
  args: {
122
158
  item: {
@@ -0,0 +1,114 @@
1
+ "use client";
2
+
3
+ import { Button } from "@heroui/button";
4
+ import {
5
+ Modal,
6
+ ModalBody,
7
+ ModalContent,
8
+ ModalFooter,
9
+ ModalHeader,
10
+ } from "@heroui/modal";
11
+ import { Trash2 } from "lucide-react";
12
+ import { Locale } from "@/i18n";
13
+ import { useEmperorUI } from "@/hooks";
14
+ import { cn } from "@/utils";
15
+ import type { DeletionConfirmorProps } from "@/types";
16
+ import {
17
+ deletionConfirmorBodyClasses,
18
+ deletionConfirmorContentClasses,
19
+ deletionConfirmorFooterClasses,
20
+ deletionConfirmorHeaderClasses,
21
+ } from "./styles";
22
+
23
+ export function DeletionConfirmor({
24
+ isOpen,
25
+ onClose,
26
+ title,
27
+ description,
28
+ className,
29
+ classNames,
30
+ confirmProps,
31
+ declineProps,
32
+ }: DeletionConfirmorProps) {
33
+ const { config } = useEmperorUI();
34
+
35
+ const lang = config?.interLocalization?.lang ?? "en";
36
+ const locale = config?.interLocalization?.locales?.[lang] as
37
+ | Locale
38
+ | undefined;
39
+ const deletionConfirmorLocale = locale?.organisms?.deletionConfirmor;
40
+
41
+ const handleDecline = (e?: unknown) => {
42
+ if (typeof declineProps?.onPress === "function") {
43
+ (declineProps.onPress as (e?: unknown) => void)(e);
44
+ }
45
+ onClose();
46
+ };
47
+
48
+ return (
49
+ <Modal
50
+ placement="center"
51
+ isOpen={isOpen}
52
+ onClose={onClose}
53
+ onOpenChange={(open) => {
54
+ if (!open) onClose();
55
+ }}
56
+ dir={lang === "ar" ? "rtl" : "ltr"}
57
+ classNames={
58
+ className || classNames?.base
59
+ ? { base: cn(className, classNames?.base) }
60
+ : undefined
61
+ }
62
+ >
63
+ <ModalContent
64
+ className={cn(deletionConfirmorContentClasses(), classNames?.content)}
65
+ >
66
+ <ModalHeader
67
+ className={cn(deletionConfirmorHeaderClasses(), classNames?.header)}
68
+ >
69
+ {title}
70
+ </ModalHeader>
71
+
72
+ <ModalBody
73
+ className={cn(deletionConfirmorBodyClasses(), classNames?.body)}
74
+ >
75
+ {description}
76
+ </ModalBody>
77
+
78
+ <ModalFooter
79
+ className={cn(deletionConfirmorFooterClasses(), classNames?.footer)}
80
+ >
81
+ <Button
82
+ variant="flat"
83
+ size="sm"
84
+ {...declineProps}
85
+ onPress={handleDecline}
86
+ className={cn(declineProps?.className, classNames?.declineButton)}
87
+ >
88
+ {declineProps?.children ??
89
+ deletionConfirmorLocale?.decline ??
90
+ "Decline"}
91
+ </Button>
92
+
93
+ <Button
94
+ color="danger"
95
+ size="sm"
96
+ {...confirmProps}
97
+ className={cn(confirmProps?.className, classNames?.confirmButton)}
98
+ startContent={
99
+ confirmProps?.isLoading
100
+ ? undefined
101
+ : (confirmProps?.startContent ?? (
102
+ <Trash2 className="size-4" aria-hidden />
103
+ ))
104
+ }
105
+ >
106
+ {confirmProps?.children ??
107
+ deletionConfirmorLocale?.confirm ??
108
+ "Confirm"}
109
+ </Button>
110
+ </ModalFooter>
111
+ </ModalContent>
112
+ </Modal>
113
+ );
114
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./deletion-confirmor";
2
+ export * from "./styles";
3
+ export * from "./stories/components";