@rovula/ui 0.1.14 → 0.1.16

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 (34) hide show
  1. package/dist/cjs/bundle.js +1545 -1545
  2. package/dist/cjs/bundle.js.map +1 -1
  3. package/dist/cjs/types/components/Form/Form.d.ts +1 -1
  4. package/dist/cjs/types/index.d.ts +2 -0
  5. package/dist/cjs/types/patterns/confirm-dialog/ConfirmDialog.d.ts +25 -0
  6. package/dist/cjs/types/patterns/confirm-dialog/ConfirmDialog.stories.d.ts +54 -0
  7. package/dist/cjs/types/patterns/form-dialog/FormDialog.d.ts +40 -0
  8. package/dist/cjs/types/patterns/form-dialog/FormDialog.stories.d.ts +63 -0
  9. package/dist/components/AlertDialog/AlertDialog.js +1 -1
  10. package/dist/components/Dialog/Dialog.js +1 -1
  11. package/dist/components/Form/Form.js +15 -4
  12. package/dist/esm/bundle.js +1545 -1545
  13. package/dist/esm/bundle.js.map +1 -1
  14. package/dist/esm/types/components/Form/Form.d.ts +1 -1
  15. package/dist/esm/types/index.d.ts +2 -0
  16. package/dist/esm/types/patterns/confirm-dialog/ConfirmDialog.d.ts +25 -0
  17. package/dist/esm/types/patterns/confirm-dialog/ConfirmDialog.stories.d.ts +54 -0
  18. package/dist/esm/types/patterns/form-dialog/FormDialog.d.ts +40 -0
  19. package/dist/esm/types/patterns/form-dialog/FormDialog.stories.d.ts +63 -0
  20. package/dist/index.d.ts +67 -2
  21. package/dist/index.js +3 -0
  22. package/dist/patterns/confirm-dialog/ConfirmDialog.js +45 -0
  23. package/dist/patterns/confirm-dialog/ConfirmDialog.stories.js +103 -0
  24. package/dist/patterns/form-dialog/FormDialog.js +10 -0
  25. package/dist/patterns/form-dialog/FormDialog.stories.js +223 -0
  26. package/package.json +1 -1
  27. package/src/components/AlertDialog/AlertDialog.tsx +11 -13
  28. package/src/components/Dialog/Dialog.tsx +3 -9
  29. package/src/components/Form/Form.tsx +19 -4
  30. package/src/index.ts +4 -0
  31. package/src/patterns/confirm-dialog/ConfirmDialog.stories.tsx +193 -0
  32. package/src/patterns/confirm-dialog/ConfirmDialog.tsx +164 -0
  33. package/src/patterns/form-dialog/FormDialog.stories.tsx +437 -0
  34. package/src/patterns/form-dialog/FormDialog.tsx +144 -0
@@ -0,0 +1,223 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React, { useState } from "react";
3
+ import { useArgs } from "@storybook/preview-api";
4
+ import { FormDialog } from "./FormDialog";
5
+ import Button from "@/components/Button/Button";
6
+ import { TextInput } from "@/components/TextInput/TextInput";
7
+ import { useControlledForm, Field } from "@/components/Form";
8
+ import * as yup from "yup";
9
+ const meta = {
10
+ title: "Patterns/FormDialog",
11
+ component: FormDialog,
12
+ tags: ["autodocs"],
13
+ parameters: {
14
+ layout: "fullscreen",
15
+ },
16
+ decorators: [
17
+ (Story) => (_jsx("div", { className: "p-5 flex w-full", children: _jsx(Story, {}) })),
18
+ ],
19
+ argTypes: {
20
+ open: { control: "boolean" },
21
+ title: { control: "text" },
22
+ description: { control: "text" },
23
+ scrollable: { control: "boolean" },
24
+ },
25
+ };
26
+ export default meta;
27
+ const ContentArea = () => (_jsx("div", { className: "flex items-center justify-center bg-ramps-secondary-150 h-[200px] w-full rounded-sm", children: _jsx("p", { className: "typography-body3 text-text-contrast-max", children: "Content - Form Area" }) }));
28
+ export const Default = {
29
+ args: {
30
+ open: false,
31
+ title: "Title",
32
+ description: "Subtitle description",
33
+ },
34
+ render: (args) => {
35
+ const [{ open }, updateArgs] = useArgs();
36
+ return (_jsx(FormDialog, Object.assign({}, args, { open: open, onOpenChange: (next) => updateArgs({ open: next }), cancelAction: {
37
+ label: "Cancel",
38
+ onClick: () => updateArgs({ open: false }),
39
+ }, confirmAction: { label: "Confirm", disabled: true }, trigger: _jsx(Button, { fullwidth: false, onClick: () => updateArgs({ open: true }), children: "Open" }), children: _jsx(ContentArea, {}) })));
40
+ },
41
+ };
42
+ export const WithExtraAction = {
43
+ args: {
44
+ open: false,
45
+ title: "Title",
46
+ description: "Subtitle description",
47
+ },
48
+ render: (args) => {
49
+ const [{ open }, updateArgs] = useArgs();
50
+ return (_jsx(FormDialog, Object.assign({}, args, { open: open, onOpenChange: (next) => updateArgs({ open: next }), extraAction: {
51
+ label: "Medium",
52
+ onClick: () => console.log("extra action"),
53
+ }, cancelAction: {
54
+ label: "Cancel",
55
+ onClick: () => updateArgs({ open: false }),
56
+ }, confirmAction: { label: "Confirm", disabled: true }, trigger: _jsx(Button, { fullwidth: false, onClick: () => updateArgs({ open: true }), children: "Open (with extra action)" }), children: _jsx(ContentArea, {}) })));
57
+ },
58
+ };
59
+ export const WithForm = {
60
+ args: {
61
+ open: false,
62
+ title: "Edit profile",
63
+ description: "Make changes to your profile here.",
64
+ },
65
+ render: (args) => {
66
+ const [{ open }, updateArgs] = useArgs();
67
+ const [name, setName] = useState("");
68
+ const isValid = name.trim().length > 0;
69
+ return (_jsx(FormDialog, Object.assign({}, args, { open: open, onOpenChange: (next) => {
70
+ if (!next)
71
+ setName("");
72
+ updateArgs({ open: next });
73
+ }, cancelAction: {
74
+ label: "Cancel",
75
+ onClick: () => {
76
+ setName("");
77
+ updateArgs({ open: false });
78
+ },
79
+ }, confirmAction: {
80
+ label: "Save changes",
81
+ disabled: !isValid,
82
+ onClick: () => {
83
+ console.log("save:", name);
84
+ updateArgs({ open: false });
85
+ },
86
+ }, trigger: _jsx(Button, { variant: "outline", fullwidth: false, onClick: () => updateArgs({ open: true }), children: "Edit profile" }), children: _jsx("div", { className: "flex flex-col gap-4", children: _jsx(TextInput, { label: "Display name", required: true, value: name, onChange: (e) => setName(e.target.value), fullwidth: true }) }) })));
87
+ },
88
+ };
89
+ export const FigmaFormDefaultOpen = {
90
+ args: {
91
+ open: true,
92
+ title: "Title",
93
+ description: "Subtitle description",
94
+ },
95
+ render: (args) => {
96
+ const [{ open }, updateArgs] = useArgs();
97
+ return (_jsx(FormDialog, Object.assign({}, args, { open: open, onOpenChange: (next) => updateArgs({ open: next }), cancelAction: {
98
+ label: "Cancel",
99
+ onClick: () => updateArgs({ open: false }),
100
+ }, confirmAction: { label: "Confirm", disabled: true }, trigger: _jsx(Button, { fullwidth: false, onClick: () => updateArgs({ open: true }), children: "Open" }), children: _jsx(ContentArea, {}) })));
101
+ },
102
+ };
103
+ export const FigmaFormWithActionDefaultOpen = {
104
+ args: {
105
+ open: true,
106
+ title: "Title",
107
+ description: "Subtitle description",
108
+ },
109
+ render: (args) => {
110
+ const [{ open }, updateArgs] = useArgs();
111
+ return (_jsx(FormDialog, Object.assign({}, args, { open: open, onOpenChange: (next) => updateArgs({ open: next }), extraAction: { label: "Medium" }, cancelAction: {
112
+ label: "Cancel",
113
+ onClick: () => updateArgs({ open: false }),
114
+ }, confirmAction: { label: "Confirm", disabled: true }, trigger: _jsx(Button, { fullwidth: false, onClick: () => updateArgs({ open: true }), children: "Open" }), children: _jsx(ContentArea, {}) })));
115
+ },
116
+ };
117
+ const editProfileSchema = yup.object({
118
+ displayName: yup.string().required("Display name is required."),
119
+ email: yup
120
+ .string()
121
+ .required("Email is required.")
122
+ .email("Must be a valid email."),
123
+ });
124
+ const EditProfileDialog = ({ open, onOpenChange }) => {
125
+ const formId = React.useId();
126
+ const { methods, FormRoot } = useControlledForm({
127
+ defaultValues: { displayName: "", email: "" },
128
+ validationSchema: editProfileSchema,
129
+ mode: "onTouched",
130
+ reValidateMode: "onChange",
131
+ });
132
+ const isValid = methods.formState.isValid;
133
+ const handleClose = () => {
134
+ methods.reset();
135
+ onOpenChange(false);
136
+ };
137
+ const handleSubmit = (values) => {
138
+ console.log("submitted:", values);
139
+ handleClose();
140
+ };
141
+ return (_jsx(FormDialog, { open: open, onOpenChange: (next) => {
142
+ if (!next)
143
+ handleClose();
144
+ else
145
+ onOpenChange(next);
146
+ }, title: "Edit profile", description: "Make changes to your profile here.", formId: formId, cancelAction: { label: "Cancel", onClick: handleClose }, confirmAction: { label: "Save changes", disabled: !isValid }, children: _jsxs(FormRoot, { id: formId, onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsx(Field, { name: "displayName", component: TextInput, componentProps: {
147
+ label: "Display name",
148
+ required: true,
149
+ fullwidth: true,
150
+ } }), _jsx(Field, { name: "email", component: TextInput, componentProps: { label: "Email", required: true, fullwidth: true } })] }) }));
151
+ };
152
+ const EditProfileDialogWithLoading = ({ open, onOpenChange, }) => {
153
+ const formId = React.useId();
154
+ const [isLoading, setIsLoading] = useState(false);
155
+ const { methods, FormRoot } = useControlledForm({
156
+ defaultValues: { displayName: "", email: "" },
157
+ validationSchema: editProfileSchema,
158
+ mode: "onTouched",
159
+ reValidateMode: "onChange",
160
+ });
161
+ const isValid = methods.formState.isValid;
162
+ const handleClose = () => {
163
+ methods.reset();
164
+ onOpenChange(false);
165
+ };
166
+ const handleSubmit = async (values) => {
167
+ setIsLoading(true);
168
+ try {
169
+ await new Promise((resolve) => setTimeout(resolve, 2000));
170
+ console.log("saved:", values);
171
+ handleClose();
172
+ }
173
+ finally {
174
+ setIsLoading(false);
175
+ }
176
+ };
177
+ return (_jsx(FormDialog, { open: open, onOpenChange: (next) => {
178
+ if (!next && !isLoading)
179
+ handleClose();
180
+ else if (next)
181
+ onOpenChange(next);
182
+ }, title: "Edit profile", description: "Make changes to your profile here.", formId: formId, cancelAction: {
183
+ label: "Cancel",
184
+ onClick: handleClose,
185
+ disabled: isLoading,
186
+ }, confirmAction: {
187
+ label: "Save changes",
188
+ disabled: !isValid || isLoading,
189
+ isLoading,
190
+ }, children: _jsxs(FormRoot, { id: formId, onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsx(Field, { name: "displayName", component: TextInput, componentProps: {
191
+ label: "Display name",
192
+ required: true,
193
+ fullwidth: true,
194
+ } }), _jsx(Field, { name: "email", component: TextInput, componentProps: { label: "Email", required: true, fullwidth: true } })] }) }));
195
+ };
196
+ /**
197
+ * Pattern A: useControlledForm
198
+ *
199
+ * Use when you need to:
200
+ * - access formState (isValid, isDirty) to control the confirm button
201
+ * - reset the form on close
202
+ * - trigger validation outside the form element
203
+ */
204
+ export const WithRovulaForm = {
205
+ args: { open: false },
206
+ render: () => {
207
+ const [{ open }, updateArgs] = useArgs();
208
+ return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", fullwidth: false, onClick: () => updateArgs({ open: true }), children: "Edit profile" }), _jsx(EditProfileDialog, { open: open, onOpenChange: (next) => updateArgs({ open: next }) })] }));
209
+ },
210
+ };
211
+ /**
212
+ * Pattern B: useControlledForm + async API call + loading state
213
+ *
214
+ * Use when the confirm action calls an API.
215
+ * isLoading disables + shows spinner on the confirm button while the request is in flight.
216
+ */
217
+ export const WithRovulaFormAndApiLoading = {
218
+ args: { open: false },
219
+ render: () => {
220
+ const [{ open }, updateArgs] = useArgs();
221
+ return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", fullwidth: false, onClick: () => updateArgs({ open: true }), children: "Edit profile (with loading)" }), _jsx(EditProfileDialogWithLoading, { open: open, onOpenChange: (next) => updateArgs({ open: next }) })] }));
222
+ },
223
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rovula/ui",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "main": "dist/cjs/bundle.js",
5
5
  "module": "dist/esm/bundle.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,7 +19,7 @@ const AlertDialogOverlay = React.forwardRef<
19
19
  <AlertDialogPrimitive.Overlay
20
20
  className={cn(
21
21
  "fixed inset-0 bg-modal-overlay z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
22
- className
22
+ className,
23
23
  )}
24
24
  {...props}
25
25
  ref={ref}
@@ -37,8 +37,8 @@ const AlertDialogContent = React.forwardRef<
37
37
  <AlertDialogPrimitive.Content
38
38
  ref={ref}
39
39
  className={cn(
40
- "fixed left-[50%] top-[50%] z-50 grid w-[calc(100%-32px)] max-w-[460px] translate-x-[-50%] translate-y-[-50%] gap-6 rounded-md bg-modal-surface px-6 py-8 text-text-contrast-max shadow-[0px_12px_24px_-4px_rgba(0,0,0,0.12)] duration-200 focus:outline-none focus-visible:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
41
- className
40
+ "fixed left-[50%] top-[50%] z-50 grid w-[calc(100%-32px)] max-w-[460px] translate-x-[-50%] translate-y-[-50%] gap-6 rounded-md bg-modal-surface px-6 py-8 text-text-contrast-max shadow-[0px_12px_24px_-4px_rgba(0,0,0,0.12)] duration-200 focus:outline-none focus-visible:outline-none outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
41
+ className,
42
42
  )}
43
43
  {...props}
44
44
  />
@@ -51,10 +51,7 @@ const AlertDialogHeader = ({
51
51
  ...props
52
52
  }: React.HTMLAttributes<HTMLDivElement>) => (
53
53
  <div
54
- className={cn(
55
- "flex flex-col items-center gap-2 text-center",
56
- className
57
- )}
54
+ className={cn("flex flex-col items-center gap-2 text-center", className)}
58
55
  {...props}
59
56
  />
60
57
  );
@@ -65,10 +62,7 @@ const AlertDialogFooter = ({
65
62
  ...props
66
63
  }: React.HTMLAttributes<HTMLDivElement>) => (
67
64
  <div
68
- className={cn(
69
- "flex flex-row items-center justify-center gap-4",
70
- className
71
- )}
65
+ className={cn("flex flex-row items-center justify-center gap-4", className)}
72
66
  {...props}
73
67
  />
74
68
  );
@@ -105,7 +99,11 @@ const AlertDialogAction = React.forwardRef<
105
99
  >(({ className, ...props }, ref) => (
106
100
  <AlertDialogPrimitive.Action
107
101
  ref={ref}
108
- className={cn(buttonVariants({ fullwidth: false }), "w-[100px] justify-center", className)}
102
+ className={cn(
103
+ buttonVariants({ fullwidth: false }),
104
+ "w-[100px] justify-center",
105
+ className,
106
+ )}
109
107
  {...props}
110
108
  />
111
109
  ));
@@ -120,7 +118,7 @@ const AlertDialogCancel = React.forwardRef<
120
118
  className={cn(
121
119
  buttonVariants({ fullwidth: false, variant: "outline" }),
122
120
  "w-[100px] justify-center",
123
- className
121
+ className,
124
122
  )}
125
123
  {...props}
126
124
  />
@@ -51,7 +51,7 @@ const DialogContent = React.forwardRef<
51
51
  <DialogPrimitive.Content
52
52
  ref={ref}
53
53
  className={cn(
54
- "fixed left-[50%] top-[50%] z-50 flex w-[calc(100%-32px)] max-w-[650px] translate-x-[-50%] translate-y-[-50%] flex-col gap-6 rounded-md bg-modal-surface p-8 text-text-g-contrast-medium shadow-[0px_12px_24px_-4px_rgba(0,0,0,0.12)] duration-200 focus:outline-none focus-visible:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
54
+ "fixed left-[50%] top-[50%] z-50 flex w-[calc(100%-32px)] max-w-[650px] translate-x-[-50%] translate-y-[-50%] flex-col gap-6 rounded-md bg-modal-surface p-8 text-text-g-contrast-medium shadow-[0px_12px_24px_-4px_rgba(0,0,0,0.12)] duration-200 focus:outline-none focus-visible:outline-none outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
55
55
  className,
56
56
  )}
57
57
  {...props}
@@ -103,10 +103,7 @@ const DialogFooter = ({
103
103
  ...props
104
104
  }: React.HTMLAttributes<HTMLDivElement>) => (
105
105
  <div
106
- className={cn(
107
- "flex flex-row items-center justify-end gap-4",
108
- className,
109
- )}
106
+ className={cn("flex flex-row items-center justify-end gap-4", className)}
110
107
  {...props}
111
108
  />
112
109
  );
@@ -133,10 +130,7 @@ const DialogDescription = React.forwardRef<
133
130
  >(({ className, ...props }, ref) => (
134
131
  <DialogPrimitive.Description
135
132
  ref={ref}
136
- className={cn(
137
- "typography-body3 text-text-contrast-max",
138
- className,
139
- )}
133
+ className={cn("typography-body3 text-text-contrast-max", className)}
140
134
  {...props}
141
135
  />
142
136
  ));
@@ -110,11 +110,26 @@ export const useControlledForm = <TFieldValues extends FieldValues>({
110
110
  reValidateMode,
111
111
  });
112
112
 
113
- return createControlledForm<TFieldValues>({
113
+ // Keep FormRoot component reference stable across re-renders.
114
+ // createControlledForm creates a new component type (via forwardRef) on every call —
115
+ // if FormRoot changes reference, React unmounts + remounts the entire form tree,
116
+ // causing inputs to lose focus whenever parent state changes (e.g. isValid).
117
+ const stableRef = React.useRef<ReturnType<
118
+ typeof createControlledForm<TFieldValues>
119
+ > | null>(null);
120
+
121
+ if (!stableRef.current) {
122
+ stableRef.current = createControlledForm<TFieldValues>({
123
+ methods,
124
+ defaultValues,
125
+ controllerRef,
126
+ });
127
+ }
128
+
129
+ return {
114
130
  methods,
115
- defaultValues,
116
- controllerRef,
117
- });
131
+ FormRoot: stableRef.current.FormRoot,
132
+ };
118
133
  };
119
134
 
120
135
  const FormInner = <TFieldValues extends FieldValues>(
package/src/index.ts CHANGED
@@ -52,6 +52,10 @@ export * from "./components/FocusedScrollView/FocusedScrollView";
52
52
  export * from "./components/RadioGroup/RadioGroup";
53
53
  export * from "./components/Form";
54
54
 
55
+ // Patterns
56
+ export * from "./patterns/confirm-dialog/ConfirmDialog";
57
+ export * from "./patterns/form-dialog/FormDialog";
58
+
55
59
  // Export component types
56
60
  export type { ButtonProps } from "./components/Button/Button";
57
61
  export type { InputProps } from "./components/TextInput/TextInput";
@@ -0,0 +1,193 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { useArgs } from "@storybook/preview-api";
4
+ import { ConfirmDialog } from "./ConfirmDialog";
5
+ import Button from "@/components/Button/Button";
6
+
7
+ const meta = {
8
+ title: "Patterns/ConfirmDialog",
9
+ component: ConfirmDialog,
10
+ tags: ["autodocs"],
11
+ parameters: {
12
+ layout: "fullscreen",
13
+ },
14
+ decorators: [
15
+ (Story) => (
16
+ <div className="p-5 flex w-full">
17
+ <Story />
18
+ </div>
19
+ ),
20
+ ],
21
+ argTypes: {
22
+ open: { control: "boolean" },
23
+ typeToConfirm: { control: "text" },
24
+ title: { control: "text" },
25
+ description: { control: "text" },
26
+ confirmLabel: { control: "text" },
27
+ cancelLabel: { control: "text" },
28
+ },
29
+ } satisfies Meta<typeof ConfirmDialog>;
30
+
31
+ export default meta;
32
+ type Story = StoryObj<typeof ConfirmDialog>;
33
+
34
+ export const Default: Story = {
35
+ args: {
36
+ open: false,
37
+ title: "Are you sure?",
38
+ description: "This action cannot be undone.",
39
+ confirmLabel: "Confirm",
40
+ cancelLabel: "Cancel",
41
+ },
42
+ render: (args) => {
43
+ const [{ open }, updateArgs] = useArgs();
44
+ return (
45
+ <ConfirmDialog
46
+ {...args}
47
+ open={open}
48
+ onOpenChange={(next) => updateArgs({ open: next })}
49
+ onConfirm={() => updateArgs({ open: false })}
50
+ onCancel={() => updateArgs({ open: false })}
51
+ trigger={
52
+ <Button fullwidth={false} onClick={() => updateArgs({ open: true })}>
53
+ Open
54
+ </Button>
55
+ }
56
+ />
57
+ );
58
+ },
59
+ };
60
+
61
+ export const WithoutDescription: Story = {
62
+ args: {
63
+ open: false,
64
+ title: "Delete item?",
65
+ confirmLabel: "Delete",
66
+ cancelLabel: "Cancel",
67
+ },
68
+ render: (args) => {
69
+ const [{ open }, updateArgs] = useArgs();
70
+ return (
71
+ <ConfirmDialog
72
+ {...args}
73
+ open={open}
74
+ onOpenChange={(next) => updateArgs({ open: next })}
75
+ onConfirm={() => updateArgs({ open: false })}
76
+ onCancel={() => updateArgs({ open: false })}
77
+ trigger={
78
+ <Button
79
+ fullwidth={false}
80
+ color="error"
81
+ onClick={() => updateArgs({ open: true })}
82
+ >
83
+ Delete
84
+ </Button>
85
+ }
86
+ />
87
+ );
88
+ },
89
+ };
90
+
91
+ export const WithoutCancelButton: Story = {
92
+ args: {
93
+ open: false,
94
+ title: "Are you sure?",
95
+ description: "This action cannot be undone.",
96
+ confirmLabel: "Confirm",
97
+ hideCancelButton: true,
98
+ },
99
+ render: (args) => {
100
+ const [{ open }, updateArgs] = useArgs();
101
+ return (
102
+ <ConfirmDialog
103
+ {...args}
104
+ open={open}
105
+ onOpenChange={(next) => updateArgs({ open: next })}
106
+ onConfirm={() => updateArgs({ open: false })}
107
+ onCancel={() => updateArgs({ open: false })}
108
+ trigger={
109
+ <Button
110
+ fullwidth={false}
111
+ color="error"
112
+ onClick={() => updateArgs({ open: true })}
113
+ >
114
+ Confirm
115
+ </Button>
116
+ }
117
+ />
118
+ );
119
+ },
120
+ };
121
+
122
+ export const RequireConfirmText: Story = {
123
+ args: {
124
+ open: false,
125
+ title: "Title",
126
+ description: "Subtitle description",
127
+ confirmLabel: "Confirm",
128
+ cancelLabel: "Cancel",
129
+ typeToConfirm: "confirm",
130
+ },
131
+ render: (args) => {
132
+ const [{ open }, updateArgs] = useArgs();
133
+ return (
134
+ <ConfirmDialog
135
+ {...args}
136
+ open={open}
137
+ onOpenChange={(next) => updateArgs({ open: next })}
138
+ onConfirm={() => updateArgs({ open: false })}
139
+ onCancel={() => updateArgs({ open: false })}
140
+ trigger={
141
+ <Button fullwidth={false} onClick={() => updateArgs({ open: true })}>
142
+ Open (require confirm)
143
+ </Button>
144
+ }
145
+ />
146
+ );
147
+ },
148
+ };
149
+
150
+ export const FigmaDefaultOpen: Story = {
151
+ args: {
152
+ open: false,
153
+ title: "Title",
154
+ description: "Subtitle description",
155
+ confirmLabel: "Confirm",
156
+ cancelLabel: "Cancel",
157
+ },
158
+ render: (args) => {
159
+ const [{ open }, updateArgs] = useArgs();
160
+ return (
161
+ <ConfirmDialog
162
+ {...args}
163
+ open={open}
164
+ onOpenChange={(next) => updateArgs({ open: next })}
165
+ onConfirm={() => updateArgs({ open: false })}
166
+ onCancel={() => updateArgs({ open: false })}
167
+ />
168
+ );
169
+ },
170
+ };
171
+
172
+ export const FigmaRequirePasswordDefaultOpen: Story = {
173
+ args: {
174
+ open: false,
175
+ title: "Title",
176
+ description: "Subtitle description",
177
+ confirmLabel: "Confirm",
178
+ cancelLabel: "Cancel",
179
+ typeToConfirm: "confirm",
180
+ },
181
+ render: (args) => {
182
+ const [{ open }, updateArgs] = useArgs();
183
+ return (
184
+ <ConfirmDialog
185
+ {...args}
186
+ open={open}
187
+ onOpenChange={(next) => updateArgs({ open: next })}
188
+ onConfirm={() => updateArgs({ open: false })}
189
+ onCancel={() => updateArgs({ open: false })}
190
+ />
191
+ );
192
+ },
193
+ };